Compare all properties of two objects in Windows PowerShell


I’ve always said that the Compare-Object cmdlet, which has been around forever in Windows PowerShell, is one of the most flexible and useful cmdlets out there. If you’re an IT Professional of any kind, whether you work with Exchange Server or manage file servers, Compare-Object can make you look like a genius. In the past, I’ve used it for a number of random tasks, from comparing Active Directory group memberships to detecting changed files/folders on a file server.

The Compare-Object cmdlet really shines when you’re comparing collections of like objects, and, although it allows you to also compare based on specific object properties, that sometimes leaves a little to be desired. Take for instance, the comparison of two Active Directory user objects. It is fairly common for an administrator to come across scenarios where you need to compare two Active Directory users and find the specific differences. Usually, you’re troubleshooting something strange that happens for one user but not the other. Often times it may come down to how particular attributes have been configured on the user object. So, Compare-Object to the rescue, right? Not so fast.

$ad1 = Get-ADUser amelia.mitchell -Properties *
$ad2 = Get-ADUser carolyn.quinn -Properties *
Compare-Object $ad1 $ad2

Running this code gives you the following output. Not exactly helpful. Of course, we know that the identity of the reference object and difference object will not be the same.

InputObject						SideIndicator
-----------						-------------
CN=carolyn.quinn,OU=Users,DC=contosocorp,DC=us		=>           
CN=amelia.mitchell,OU=Users,DC=contosocorp,DC=us	<=

To take the comparison one step further, you can use the -Property parameter. This allows you to compare one or more specific parameters. More helpful than the last example, but it assumes you know where the differences between the objects lie.

$ad1 = Get-ADUser amelia.mitchell -Properties *
$ad2 = Get-ADUser carolyn.quinn -Properties *
Compare-Object $ad1 $ad2 -Property title,department

Okay, this is definitely more helpful. We can see by the output below that the user objects have different title and department attributes. However, we had to specify the attributes we wanted to compare. If we wanted to compare all attributes, this would be a very long command or a very manual process to compare each attribute.

title             department SideIndicator
-----             ---------- -------------
Finance Associate Finance    =>           
Accountant II     Accounting <=

So, with a little extra logic, we can do this pretty easily. First, we define the Compare-ObjectProperties function. That function will take any two source objects and get a unique list of all of the property names of both objects we’re comparing. This is necessary because objects aren’t always going to have the same set of attributes. When that is the case, we want to see where one has a null value and the other is populated. With the list of unique property names, our function can iteratively process them through Compare-Object and only return the properties that are different.

Function Compare-ObjectProperties {
    Param(
        [PSObject]$ReferenceObject,
        [PSObject]$DifferenceObject  
    )
    $objprops = $ReferenceObject | Get-Member -MemberType Property,NoteProperty | % Name
    $objprops += $DifferenceObject | Get-Member -MemberType Property,NoteProperty | % Name
    $objprops = $objprops | Sort | Select -Unique
    $diffs = @()
    foreach ($objprop in $objprops) {
        $diff = Compare-Object $ReferenceObject $DifferenceObject -Property $objprop
        if ($diff) {            
            $diffprops = @{
                PropertyName=$objprop
                RefValue=($diff | ? {$_.SideIndicator -eq '<='} | % $($objprop))
                DiffValue=($diff | ? {$_.SideIndicator -eq '=>'} | % $($objprop))
            }
            $diffs += New-Object PSObject -Property $diffprops
        }        
    }
    if ($diffs) {return ($diffs | Select PropertyName,RefValue,DiffValue)}     
}

$ad1 = Get-ADUser amelia.mitchell -Properties *
$ad2 = Get-ADUser carolyn.quinn -Properties *
Compare-ObjectProperties $ad1 $ad2

Running the above code produces the following output. This is definitely what we’re looking for.

PropertyName			RefValue						DiffValue
------------			--------						---------
CanonicalName			contosocorp.us/Users/amelia.mitchell			contosocorp.us/Users/carolyn.quinn
CN				amelia.mitchell						carolyn.quinn
Created				2/16/2017 9:31:12 AM					2/16/2017 9:31:15 AM
createTimeStamp			2/16/2017 9:31:12 AM					2/16/2017 9:31:15 AM
Department			Accounting						Finance
DisplayName			Amelia Mitchell						Carolyn Quinn
DistinguishedName		CN=amelia.mitchell,OU=Users,DC=contosocorp,DC=us	CN=carolyn.quinn,OU=Users,DC=contosocorp,DC=us
dSCorePropagationData		{2/16/2017 9:53:26 AM, 2/16/2017 9:31:12 AM,...		{2/16/2017 9:53:25 AM, 2/16/2017 9:31:15 AM, 12/31/1600 6:00:0...
employeeType			FullTime						Contractor
GivenName			Amelia							Carolyn
Manager				CN=christopher.walsh,OU=Users,DC=contosocorp,DC=us
MemberOf			{CN=Group8842,OU=Groups,DC=contosocorp,DC=us...		{CN=Group8896,OU=Groups,DC=contosocorp,DC=us...
Modified			4/24/2017 7:51:46 AM					4/24/2017 7:51:56 AM
modifyTimeStamp			4/24/2017 7:51:46 AM					4/24/2017 7:51:56 AM
Name				amelia.mitchell						carolyn.quinn
ObjectGUID			e3436c96-ac59-4b0a-a2cd-cea586676089			27abcbb9-57f8-4e4d-8f87-a5ee0e3d6ddd
objectSid			S-1-5-21-827217070-865584383-1086049070-1208 		S-1-5-21-827217070-865584383-1086049070-1249
Office				LON							NYC
PasswordLastSet			2/16/2017 9:31:12 AM					2/16/2017 9:31:15 AM
physicalDeliveryOfficeName	LON							NYC
pwdLastSet			131317326721610425					131317326757222285
SamAccountName			amelia.mitchell						carolyn.quinn
SID				S-1-5-21-827217070-865584383-1086049070-1208		S-1-5-21-827217070-865584383-1086049070-1249
sn				Mitchell						Quinn
Surname				Mitchell						Quinn
Title				Accountant II						Finance Associate
UserPrincipalName		amelia.mitchell@contosocorp.us				carolyn.quinn@contosocorp.us
uSNChanged			372797							372798
uSNCreated			17224							17511
whenChanged			4/24/2017 7:51:46 AM					4/24/2017 7:51:56 AM
whenCreated			2/16/2017 9:31:12 AM					2/16/2017 9:31:15 AM

One of the best things about this function is that you’re not limited to comparing Active Directory user objects. You can use any two objects you want, and they don’t even have to be the same type of object! Try for yourself using the example below, which compares a file object to a service. Granted, there probably aren’t many use cases for such a scenario, but you never know. The capability is there nonetheless.

$file = Get-Item -Path C:\Windows\System32\notepad.exe
$service = Get-Service -Name WinRM
Compare-ObjectProperties $file $service

Happy PowerShell’ing. See you next time.

Comments (0)

Skip to main content