Continuing the work on backing up Exchange configurations, I decided to simplify the script to back up the configurations to this:
$su = get-credential
$Target_Directory = 'C:\testfiles\Ex_ConfigExport'
$ExchangeServer = '006Exch-Srv1'
$SessionParams =
@{
ConfigurationName = 'MicroSoft.Exchange'
ConnectionURI = "http://$ExchangeServer/powershell/"
Authentication = 'Kerberos'
ErrorAction = 'Stop'
Credential = $su
}
$ExSession = New-PSSession @SessionParams
$Ex_Object_Folders =
@{
Server = @(
'MailboxServer',
'TransportServer',
'ClientAccessServer',
'UMServer'
)
UM = @(
'UMDialPlan'
'UMAutoAttendant'
'UMIPGateway'
'UMHuntGroup'
)
Transport = @(
'TransportRule',
'JournalRule',
'SendConnector',
'ReceiveConnector',
'ForeignConnector',
'RoutingGroupConnector',
'AcceptedDomain',
'RemoteDomain'
)
Database = @(
'MailboxDatabase',
'PublicFolderDatabase',
'DatabaseAvailabilityGroup'
)
Policy = @(
'ActiveSyncMailboxPolicy',
'AddressBookPolicy',
'EmailAddressPolicy',
'ManagedFolderMailboxPolicy',
'OwaMailboxPolicy',
'RetentionPolicy',
'RetentionPolicyTag',
'RoleAssignmentPolicy',
'SharingPolicy',
'ThrottlingPolicy',
'UMMailboxPolicy'
)
}
#Export set folder creation
$DirParams = @{
ItemType = 'Directory'
Verbose = $true
ErrorAction = 'Stop'
}
Write-Verbose "Checking target root folder"
if ( -not ( Test-Path $Target_Directory ) )
{ New-Item @DirParams -Path $Target_Directory }
$Export_Set = (get-date).tostring('yyyy-MM-dd_HH.mm.ss')
$Export_Set_Path = "$Target_Directory\$Export_Set"
Write-Verbose "Creating folder for this export set ($Export_Set)"
New-Item @DirParams -Path $Export_Set_Path
Write-Verbose "Exporting Exchange configuration objects"
foreach ($Ex_Object_Folder in $Ex_Object_Folders.keys)
{
$Ex_Object_Folder_Path = "$Export_Set_Path\$Ex_Object_Folder"
New-Item @DirParams -Path $Ex_Object_Folder_Path
foreach ( $Ex_Object_Type in $Ex_Object_Folders.$Ex_Object_Folder )
{
$Ex_Object_Type_Path = "$Ex_Object_Folder_Path\$Ex_Object_Type"
New-Item @DirParams -Path $Ex_Object_Type_Path
$SB = [ScriptBlock]::Create("Get-$Ex_Object_Type")
Invoke-Command -ScriptBlock $SB -Session $ExSession |
foreach {
$_ | Export-Clixml "$Ex_Object_Type_Path\$($_.guid).clixml" -Verbose
}
}
}
Remove-PSSession $ExSession
Again, right now this is in a testing phase and you’ll need to modify the server name and target directory, and you can remove the credential prompt and session parameter if it’s not necessary in your environment.
Running will create a set of exported configuration objects each time it’s run. Now I want to be able to compare one set to another to see what’s changed. This script will compare two of those configuration sets, and create objects that detail the objects and properties where it finds differences.
<#
.Synopsis
Compare two export object sets
.DESCRIPTION
Compares deserialized objects from two directories of .clixml files.
#>
[CmdletBinding()]
Param
(
# First export object set directory
[Parameter(Mandatory=$true)]
$ExportSetPath1,
# Second export object set directory
[Parameter(Mandatory=$true)]
$ExportSetPath2
)
Begin
{
$CompareSetTimestamps = Get-Item $ExportSetPath1,$ExportSetPath2 |
Select FullName,LastWriteTime |
Sort LastWriteTime
$ReferenceSetPath = $CompareSetTimestamps[0].FullName
$DifferenceSetPath = $CompareSetTimestamps[1].FullName
$ExcludedProperties = @(
'PSShowComputerName',
'RunspaceId',
'OriginatingServer',
'WhenChanged'
)
$ExcludeRegex = [regex]($ExcludedProperties -join '|')
}
Process
{
}
End
{
$ReferenceSetFolders =
Get-ChildItem $ReferenceSetPath -Directory
Write-Verbose "Found $($ReferenceSetFolders.count) Export set folders"
foreach ( $ReferenceSetFolder in $ReferenceSetFolders )
{
Write-Verbose "***** Comparing $ReferenceSetFolder Objects*****`n"
$ReferenceObjectTypeFolders =
Get-ChildItem $ReferenceSetFolder.FullName -Directory
foreach ( $ReferenceObjectTypeFolder in $ReferenceObjectTypeFolders )
{
Write-Verbose " *****Comparing $ReferenceObjectTypeFolder Objects*****`n"
$ReferenceObjectFiles =
Get-ChildItem $ReferenceObjectTypeFolder.FullName -File
$DifferenceObjectTypeFolderPath =
@(
$DifferenceSetPath,
$ReferenceSetFolder,
$ReferenceObjectTypeFolder
) -join '\'
$DifferenceObjectFiles =
Get-ChildItem $ReferenceObjectTypeFolder.FullName -File
$AddedObjectFiles =
$DifferenceObjectFiles |
Where { $ReferenceObjectFiles.name -notcontains $_.name }
$DeletedObjectFiles =
$ReferenceObjectFiles |
Where { $DifferenceObjectFiles.name -notcontains $_.name }
$ComparedObjectFiles =
$ReferenceObjectFiles |
Where { $DifferenceObjectFiles.name -Contains $_.name }
foreach ( $ComparedObjectFile in $ComparedObjectFiles )
{
$DifferenceObjectFilePath =
"$DifferenceObjectTypeFolderPath\$($ComparedObjectFile.Name)"
$DifferenceObject = Import-Clixml $DifferenceObjectFilePath
if ($DifferenceObject.WhenChanged -le $ComparedObjectFile.LastWriteTime)
{ Continue }
Write-Verbose " Change detected in $($ReferenceObjectTypeFolder.Name) $($DifferenceObject.Identity)"
$ReferenceObject = Import-Clixml $ComparedObjectFile.FullName
Write-Debug " Comparing properties of $($ReferenceObject.Identity)"
$Properties =
$ReferenceObject.psobject.properties.name -notmatch $ExcludeRegex
foreach ( $Property in $Properties )
{
if ([string]$ReferenceObject.$Property -ne [string]$DifferenceObject.$Property)
{
Write-Verbose " Found change in property $Property of $($ReferenceObjectTypeFolder.Name) $($ReferenceObject.Identity)`n"
Write-verbose "`n`nOld value = $($ReferenceObject.$Property)`n`nNew value = $($DifferenceObject.$Property)`n"
[PSCustomObject]@{
Guid = $ReferenceObject.Guid
RefExportObjectPath = $ComparedObjectFile.FullName
DiffExportObjectPath = $DifferenceObjectFilePath
ObjectClass = $ReferenceObject.ObjectClass
Identity = $ReferenceObject.Identity
Property = $Property
RefWhenChanged = $ReferenceObject.WhenChanged
RefPropertyValue = $ReferenceObject.$Property
DiffWhenChanged = $DifferenceObject.WhenChanged
DiffPropertyValue = $DifferenceObject.$Property
}
}
}#end property loop
}#end object loop
}#end object type folder loop
}#end export set folder loop
}#end End block
For testing, I’m using this script to select the export sets to compare:
$Exportsets = Get-ChildItem C:\testfiles\Ex_ConfigExport -directory |
select -expand fullname |
Out-GridView -OutputMode Multiple -Title "Select two export sets to compare."
If ($Exportsets.count -ne 2)
{
Write-Warning "Select two (and only two) export sets to compare"
Return
}
$changes = C:\scripts\Ex_Backup\Compare-ExportSets.ps1 $Exportsets[0] $Exportsets[1]
$changes
I still need to address added and deleted objects between sets, but that’s relatively trivial compared to finding any changes in all the existing objects. This is not intended to provide a complete report, but to create a collection of objects containing the information necessary to produce it and provide enough information to easily investigate further. It’s also useful for doing ad-hoc comparisons of exports sets from arbitrary days.
In my environment it takes about 5.5 MB to save an export set to disk. These are plain-text files, and using NTFS compression on the folder will drop it down to about 3 MB, so for about 100MB of disk space I can keep about a month’s worth.
This is still obviously work in progress, and I welcome an comments or suggestions.