Comparing AADConnect Rules or How Do I Tell What Changed?

As the rule base generated by AADConnect has gotten larger and larger and a simple configuration setting change can generate multiple rules, it has become a challenge to keep track of what is going on. So when the configuration changes or there is a version upgrade, how does one tell what changed? This is not as important in a “standard” configuration but if the “standard” rules are changed by disabling rules, copying and modifying rules, or adding new rules, it is important to be aware how an upgrade handles those modifications.

As I migrated companies from FIM or MIM to AADConnect, it was important to compare their configuration with what AADConect was going to create. To help with this, I wrote a little script to dump the AADConnect rules to a flat representation I could manipulate in Excel.

I realized that I could use the generated CSV file and Windiff.exe to track changes to the rules. Dump the rules before and after a change. Sort them both in whatever order makes sense to you with Excel. Write the files back out as CSVs and compare them with Windiff or a comparable utility. Wh-la, a few changes in thousands of rules pop right out.

I found that some attributes seem to change on all updates and mask real differences. These: “Precedence”,” Identifier”, and “TagVersion”, have been Rem’ed out to prevent that from happening. I have been using the sort sequence ‘Connector>Direction>Name>Source>Destination’.

Here is the script. No guarantees but use it if it helps. Let me know if it does.

##########################################################################################
# DumpRules.ps1
# Dump Adsync Rules -- Extracts each rule item to a TDF as an individual row
#
# Version 3.8
#
#   Written by Nick Cottrell Pennic Consulting Inc.
#
#      CopyRight © Pennic Consulting (2018-2019)  All Rights Reserved
#
###########################################################################################
 
$FileToWrite = Read-Host  "Enter the Output file including path"
 
$Tab = [char]9
$Lf = [char]10
$Cr = [char]13
 
$Output = @(); Get-ADSyncRule | ForEach-Object {
 
$rules = $_.AttributeFlowMappings;
 
foreach ($rule in $rules) {
 
$line = new-object system.object
 
$connector = $(get-adsyncconnector -identifier $_.connector).name
 
$Line | Add-Member  -MemberType NoteProperty -Name "Connector" -Value $Connector -Force
$Line | Add-Member  -MemberType NoteProperty -Name "Name" -Value $_.Name -Force
$Line | Add-Member  -MemberType NoteProperty -Name "Direction" -Value $_.direction -Force
$Line | Add-Member  -MemberType NoteProperty -Name "Disabled" -Value $_.disabled -Force
$Line | Add-Member  -MemberType NoteProperty -Name "SourceObjectType" -Value $_.sourceobjecttype -Force
$Line | Add-Member  -MemberType NoteProperty -Name "TargetObjectType" -Value $_.targetobjecttype -Force
$Line | Add-Member  -MemberType NoteProperty -Name "linktype" -Value $_.linktype -Force
# $Line | Add-Member  -MemberType NoteProperty -Name "Precedence" -Value $_.precedence -Force
 
$source = [string]::join(';',$rule.source)
 
$line | Add-Member  -MemberType NoteProperty -Name "Source" -Value $Source -Force
$line | Add-Member  -MemberType NoteProperty -Name "Destination" -Value $rule.Destination -Force
$line | Add-Member  -MemberType NoteProperty -Name "Flowtype" -Value $rule.flowtype -Force
$line | Add-Member  -MemberType NoteProperty -Name "Executeonce" -Value $rule.executeonce -Force
 
$Expression = $Rule.Expression
If ($Expression) {
    $ExpressionClean = $Rule.Expression -Replace "`r|`n",""
 
    } else {
 
    $ExpressionClean = ""}
 
$line | Add-Member  -MemberType NoteProperty -Name "Expression" -Value $ExpressionClean
$line | Add-Member  -MemberType NoteProperty -Name "ValueMergeType" -Value $rule.ValueMergeType -Force
 
$Mapping = $Rule.mappingsourceasstring
If ($Mapping) {
    $MappingClean = $Rule.mappingsourceasstring -Replace "`r|`n",""
 
    } else {
 
    $MappingClean = ""}
 
$line | Add-Member  -MemberType NoteProperty -Name "MappingSourceAsString" -Value $MappingCLean
 
# $Line | Add-Member  -MemberType NoteProperty -Name "Identifier" -Value $_.identifier -Force
$Line | Add-Member  -MemberType NoteProperty -Name "EnablePasswordSync" -Value $_.enablepasswordsync -Force
$Line | Add-Member  -MemberType NoteProperty -Name "IsStandardRule" -Value $_.isstandardrule -Force
$Line | Add-Member  -MemberType NoteProperty -Name "IsLegacyCustomRule" -Value $_.islegacycustomrule -Force
$Line | Add-Member  -MemberType NoteProperty -Name "VersionAgnosticTag" -Value $_.VersionAgnosticTag -Force
# $Line | Add-Member  -MemberType NoteProperty -Name "TagVersion" -Value $_.TagVersion -Force
 
$joinHash = $null
$csattribute = $null
$mvattribute = $null
$casesensative = $Null
$attribute = $null
$comparisonvalue = $null
$comparisonoperator = $null
 
$joinfilter = $_.JoinFilter
 
if ($joinfilter) {
$JoinHash = [string]::join(';',$joinfilter.Joinhash)
$Csattribute = [string]::join(';',$joinfilter.JoinConditionList.csattribute)
$mvattribute = [string]::join(';',$joinfilter.JoinConditionList.mvattribute)
$caseSensitive = [string]::join(';',$joinfilter.JoinConditionList.casesensitive)
}
 
$ScopeFilter = $_.ScopeFilter
 
If($scopefilter) {
$attribute = [string]::join(';',$scopefilter.scopeconditionlist.attribute)
$comparisonvalue = [string]::join(';',$scopefilter.scopeconditionlist.comparisonvalue)
$ComparisonOperator = [string]::join(';',$scopefilter.scopeconditionlist.ComparisonOperator)
}
 
$Line | Add-Member  -MemberType NoteProperty -Name "JoinFilter-JoinHash" -Value $joinhash -Force
$Line | Add-Member  -MemberType NoteProperty -Name "JoinFilter-CSAttribute" -Value $csattribute -Force
$Line | Add-Member  -MemberType NoteProperty -Name "JoinFilter-MVAttribute" -Value $mvattribute -Force
$Line | Add-Member  -MemberType NoteProperty -Name "JoinFilter-CaseSensitive" -Value $casesensitive -Force
 
$Line | Add-Member  -MemberType NoteProperty -Name "ScopeFilter-Attribute" -Value $attribute -Force
$Line | Add-Member  -MemberType NoteProperty -Name "ScopeFilter-Operator" -Value $comparisonoperator -Force
$Line | Add-Member  -MemberType NoteProperty -Name "ScopeFilter-Value" -Value $comparisonvalue -Force
 
$output +=$line
 
 
}
 
$output | export-csv -path $FileToWrite  -delimiter $Tab -NoTypeInformation
 
}
 
write-host "Successful!" -fore Green
Write-Host "Output File: " $FileToWrite

-Nick

Leave a Comment