Updating an XML Document using powershell

In a mixed mode exchange environment ( where there are a mix of exchange versions, e.g. 2007, 2010 and 2013 ) you can force ARS to behave in a particular way.  There is a attribute called ‘edsvaExchangeProperties’ on the Server Configuration Object.

‘CN=Server Configuration,CN=Configuration’

The configuration is stored in XML.  To change how ARS behaves you need to add attributes and set the attribute values.  It’s not exposed in the UI so Dell provide a script to do this.

Here’s a link to the script I was provided,

How-to: Force ActiveRoles Server to use Exchange cmdlets instead of default MAPI (85047)

https://support.software.dell.com/kb/85047

I wanted to convert this to powershell and looking at the code I knew that there was a major flaw in it.  The code they provided would set the XML to the new value but it was assuming it already knew the values of the existing attribute.

In my environment I asked for a specific fix to be provided that would prevent the mail attribute being changed to a duplicate.  ARS has a nice feature in that you change the mail attribute and it sets the primarySMTP address to match.  The problem is that Dell don’t bother to check if the primarySMTP attribute is a duplicate and happily breaks your mail system.  If you set the primary SMTP address on the exchange tab it does check and prevents you from doing this.  I raised this as an issue and Dell provided a custom hotfix.  Sadly instead of checking for duplicates they broke the link so that when you change the mail attribute it does no try and update the primarySMTP address, which is what ADU&C does.  It would have been nice if they fixed it properly.  This behaviour is controlled using the edsvaExhange attribute.  They provided a powershell script to update the setting.  In fact 2, one to enable and one to disable.  No error checking either, not exactly a model of how to write a production script.

Anyway I’m procrastinating again.

I just updated the script to include some of my library functions and instead of toggling the value it now updates the attribute if it exists or adds it if it doesn’t exist and it also now handles attributes that have values other than true or false.

Edit the user variables to make the script do what you need it to do:

$xmlElementAttributeName    – this is the attribute to add to the XML element
$attributeValue                           – this is the value to set the attribute to
$addElement                               –  True ADD/UPDATE  – False  DELETE the attribute
$restoreFromBackup                 – True will restore the XML element from backup
$xmlBackupFileName               – The fileName to use
$xmlBackupPath                        – The path to store the XML backup

I’m also planning on using this script to manage other XML files to add and remove elements and attributes in an XML file although I think I just discovered a post that details how to do this already – try checking out this post http://powershell.com/cs/blogs/tobias/archive/2009/02/02/xml-part-2-write-add-and-change-xml-data.aspx

#region User defined attributes
#$xmlElementAttributeName = 'NeverUsePowerShellCmdlets'                            # element attribute name 
#$xmlElementAttributeName = 'UseMapiIfExchangeCmdletsNotInstalled'                 # element attribute name 
#$xmlElementAttributeName = 'DoNotChangeProxyAddressesWhenChangingMail'            # element attribute name 
$xmlElementAttributeName = 'UseCmdletsInE12CoExistingModeForEstablishEmailTasks'  # element attribute name 
$attributeValue = "true"    
#$xmlElementAttributeName= 'PreferredVersionForEstablishEmailTasks'
#$attributeValue = "16"   
$ARSObjectAttributeName = 'edsvaExchangeProperties'
$addElement = $true  # ADD / UPDATE the attribute
$addElement = $false # DELETE the attribute
$xmlBackupFileName = "ARSExchangeAttributes.xml"
$xmlBackupPath = "c:\cache"
$restoreFromBackup = $false  # $xmlBackupFileName filename will have the date appended to the $xmlBackupFilename
$padright = 120 
# $restoreFromBackup = $true # set to true to restore from backup using the explicit $xmlBackupFileName filename
#endregion User defined attributes
#Region Helper Functions
Function Get-DatedFileName {
<#
.SYNOPSIS
 Get-DatedFileName - this function will take a file name if given and return a file name based on a given date 
.DESCRIPTION
 This function takes a file name if given and either prefixes or post fixes the given file name with a sortable date string -YYYY-MM-DDThh-mm-ss
 Any invalid characters are replaced by hyphens - any resultant leading Hyphen is also removed
 If the file name includes a file extension it places this on the end of the resultant filename otherwise is adds a default .txt extension
.PARAMETER fileName
  Optional [string] parameter file name 
.PARAMETER dateTime
  Optional [dateTime] parameter date to use when creating the file name 
.PARAMETER prefixFileName
 Optional [switch] when present the date will prefix the resultant file name
.EXAMPLE
  Get-DatedFileName "<Test>FileName=" Returns  'Test--FileName-2015-11-25T13-36-59.txt' if todays date were the 25th November 2015
.EXAMPLE
 Get-DatedFileName  Returns '2015-11-25T13-36-59.txt'  if todays date were the 25th November 2015
.OUTPUTS
  A valid filename string
.NOTES
 NAME     : Get-DatedFileName
 VERSION  : 1.0 Initial Version
 AUTHOR   : Lee Andrews
#>
 [CmdletBinding()]
 param (
  [parameter(Mandatory=$false,Position=0,HelpMessage = "Optionally enter a filename which can include a file extension")][string]$fileName,
  [parameter(Mandatory=$false,Position=1,HelpMessage = "Optionally enter a date to use when creating the resultant filename")][datetime]$dateTime,
  [parameter(Mandatory=$false,Position=2,HelpMessage = "If present the resultant filname will be pre fixed rather than post fixed with the date string")][switch]$prefixFileName
 )
 if ( ( $fileName ) -and ( $fileName.Substring($fileName.Length-4,1) -eq "." ) ) {
  # we found a fileName extention so lets store it 
  $fileExtension = $fileName.Substring($fileName.Length-4)
  $fileName = $fileName.Substring(0,$fileName.Length-4)
 }
 else { 
  $fileExtension = ".txt"  # the default file extension
 }
 $invalidChars = [io.path]::GetInvalidFileNamechars() 
 if ( $dateTime ) { $date = Get-Date($dateTime) -format s }
 else {  $date = Get-Date -format s }
 if ( $prefixFileName ) { $fileName = $($($date.ToString() + "-" + $fileName) -replace "[$invalidChars]","-") + $fileExtension  }
 else { $fileName = $($($fileName + "-" + $date.ToString()) -replace "[$invalidChars]","-") + $fileExtension }
 # remove any leading hyphens
 $fileName.trim("-")
 }
function Get-SnapinStatus { 
 [CmdletBinding(SupportsShouldProcess=$true)]
 param (
  [parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="Enter the snapin name, e.g. Microsoft.Exchange.Management.PowerShell.Admin")]
  [string]$name
 )
 if(!(Get-PSSnapin -name "$name")) {
  if(Get-PSSnapin -Registered | Where-Object {$_.name -eq "$name"}) {
   try { 
    Add-PSSnapin -Name "$name" 
    return $true
   }
   catch { return $false }
  } 
  else { return $false }
 }
 else { return $true }
} # end function Get-SnapinStatus
#endregion Helper Functions
#region Get ARS Server configuration object
cls
if ( ! ( Get-SnapinStatus "Quest.ActiveRoles.ADManagement" ) ) {
 $msg = "FATAL ERROR: Cannot load ARS Snapins - QUITTING SCRIPT"
 Write-Host $msg.padright($padright) -ForegroundColor Red -BackgroundColor Black
 Throw $msg
}
$ARSServerConfigurationObject = get-qadObject 'CN=Server Configuration,CN=Configuration' -IncludedProperties $ARSObjectAttributeName -Proxy
if ( $ARSServerConfigurationObject -eq $null ) {
 $msg = "FATAL ERROR:  unable to get ARS Object"
 Write-Host $msg.padright($padright) -ForegroundColor Red -BackgroundColor Black
 Throw $msg 
}
#endregion Get ARS Server configuration object
#region Optionally restore from backup
$updateRequired = $false
if ( $restoreFromBackup ) {
 $xmlBackupFile = "$xmlBackupPath\$xmlBackupFileName"
 if ( Test-Path $xmlBackupFile ) {
  $xmlDocument = Import-Clixml $xmlBackupFile
  Write-Host "The following Attributes will be restored".padright($padright) -ForegroundColor Green -BackgroundColor Black
  $xmlDocument.DocumentElement | fl 
  if ( $(Read-Host -Prompt "Restore Y/N").toLower()  -eq "y" ) {
   $updateRequired = $true
  }
  else { 
   Write-Host "User ABORTED Restore from backup".padright($padright) -ForegroundColor Yellow -BackgroundColor Black 
  }
 }
 else {
  $msg = "FATAL ERROR: backupfile not found"
  Write-Host $msg.padright($padright) -ForegroundColor Red -BackgroundColor Black
  Throw $msg 
 }
}
#endregion Optionally restore from backup
#region Setup XML attribute
else {
 $xmlDocument = new-object System.Xml.XmlDocument
 $xmlDocument.LoadXML($ARSServerConfigurationObject[$ARSObjectAttributeName])
 $xmlBackupFile = "$($xmlBackupPath)\$(Get-DatedFileName $xmlBackupFileName)"
 Export-Clixml -InputObject $xmlDocument -Path $xmlBackupFile
 $xmlAttribute = $xmlDocument.createAttribute($xmlElementAttributeName)
 if ( $xmlDocument.DocumentElement.HasAttribute($xmlElementAttributeName) ) {
  $updateRequired = $true
  if ( $addElement ) {
   if ( ( $attributeValue -eq "true" ) -or ( $attributeValue -eq "false" ) ) {
    if ( $xmlDocument.ExchangeProperties."$xmlElementAttributeName" -eq $true ) {
     if ( $attributeValue -eq "true" ) {     
       Write-Host "Attribute value is ALREADY set to TRUE nothing to do".padright($padright) -ForegroundColor Yellow -BackgroundColor Black
       exit
     }
     Write-Host "Attribute value is currently TRUE setting to FALSE".padright($padright) -ForegroundColor Yellow -BackgroundColor Black
    }
    else {
     if ( $attributeValue -eq "false" ) {
      Write-Host "Attribute value is ALREADY set to FALSE nothing to do".padright($padright) -ForegroundColor Yellow -BackgroundColor Black
      exit
     }
     Write-Host "Attribute value is currently FALSE setting to TRUE".padright($padright) -ForegroundColor Yellow -BackgroundColor Black
    }
   }
   else {
    if ( $xmlDocument.ExchangeProperties."$xmlElementAttributeName" -eq $attributeValue ) {
     Write-Host "Attribute value is ALREADY set to $($attributeValue) nothing to do".padright($padright) -ForegroundColor Yellow -BackgroundColor Black
     exit
    }
   }
   $xmlAttribute.Value = $attributeValue
   $xmlDocument.DocumentElement.SetAttributeNode($xmlAttribute) | out-null
  } # addElement = TRUE
  else {
   Write-Host "Removing the attribute".padright($padright) -ForegroundColor Yellow -BackgroundColor Black
   $xmlDocument.DocumentElement.RemoveAttribute($xmlElementAttributeName) # remove the attribute 
  } # addElement = FALSE 
 } # Atttibute already exists 
 else { 
  if ( $addElement ) {
   $updateRequired = $true
   $xmlAttribute.Value = $attributeValue
   $xmlDocument.DocumentElement.Attributes.Append($xmlAttribute)| out-null
   Write-Host "Adding attribute".padright($padright) -ForegroundColor Green -BackgroundColor Black
  } # addElement = TRUE
  else {
   Write-Host "Attribute does not exist NOTHING TO DO".padright($padright) -ForegroundColor Yellow -BackgroundColor Black
   exit
  } # addElement = FALSE
 }
}
#endregion Setup XML attribute
#region Update ARS Server config object
if ( $updateRequired ) { 
 try { 
  set-qadObject 'CN=Server Configuration,CN=Configuration' -ObjectAttributes @{$ARSObjectAttributeName=$xmlDocument.innerxml} -proxy  | out-null
  Write-Host "The following Attributes are now set".padright($padright) -ForegroundColor Green -BackgroundColor Black
  $xmlDocument.DocumentElement | fl 
  Write-Host "".padright($padright,"-") -ForegroundColor Gray -BackgroundColor Black
 }
 catch { 
  $msg = "FATAL Error ubdating the ARS object NO CHANGES Made"
  Write-Host $msg.padright($padright) -ForegroundColor Red -BackgroundColor Black
  Throw $msg 
 }
}
Write-Host "Attribute Updated OK".padright($padright) -ForegroundColor Green -BackgroundColor Black
Write-Host "".padright($padright,"-") -ForegroundColor Gray -BackgroundColor Black
#endregion Update ARS Server config object
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.