Compare Active Directory group membership with PowerShell

Lately it seems a number of people are trying to compare group membership between Active Directory groups. Often they are trying to use PowerShell (a good choice) so I thought I’d share some techniques you can use.

For this article I’m going to assume you are using the Microsoft Active Directory module from a member desktop running at least PowerShell 3.0. If you are using the Quest Active Directory cmdlets, you should be able to use the concepts but naturally change the cmdlet names.

For the sake of the demonstration I have two small global security groups, although it doesn’t really matter the scope or type.

I’m using the Name property, but you might want to use samAccountName as that should be unique. Visually, you can probably identify members in both groups or in only one group.

Remember, $a and $b are collections (or arrays) of Active Directory user objects. As such, I can compare the objects.

Here, $a is the reference object and $b is the difference object. The SideIndicator shows which object only belongs in a particular group. In this example, the displayed inputobject makes it pretty clear which user account belongs to which group. But you can fine tune the comparison by specifying a property name.

Much better.

But I expect you need more than that. You probably want to find users in Group A that are also in Group B.

This expression uses a trick introduced in PowerShell 3 that allows you to enumerate a single property (e.g. Name) for an array of objects. The command says “Go through all the names from $a where the collection of names from $b contains a matching name.” I’m using $psitem but you can also use $_.

Or maybe you need to opposite: users in Group A that are NOT in Group B.

My groups only have a small number of users but it wouldn’t matter if there were 10 or 1000 users. And certainly you can flip this around.

These are the users in Group B that are NOT in Group A. What you do with this information is up to you. If you are working in a pipeline expression, it might be more useful to get the full user object. I’ve just been filtering on the name but there’s no reason I can’t filter on the full user object.

Here then are the user objects for members of Group A that are not in Group B. For example, perhaps you need to disable accounts that aren’t in Group B.

Once you’ve learned the syntax of working with arrays of objects it doesn’t matter if they are AD users, files or processes. If you are still struggling to grasp some PowerShell fundamentals, I encourage you to pick up a copy of Learn Windows PowerShell 3 in a Month of Lunches or my video courses at


Join the 4sysops PowerShell group!

Your question was not answered? Ask in the forum!

  1. Wighty05 4 years ago

    I'm using this method and trying to get the output formatted to "SamAccountName, employeeid, displayname, mail" to no success, any help would be great

    $Results1 = $a.samaccountname | where {$b.samaccountname -notcontains $psitem}

    $Results1 | Sort-Object DisplayName | ForEach-Object {Add-Member -InputObject $_ -Type NoteProperty -Name SamAccountName -Value $_; $_} | ConvertTo-Html -Head $head -Body $strMail1 -Property SamAccountName | Out-File $Report1

    and it emails the $Report1


  2. Jay 4 years ago

    Thanks for your blog post.  Saved me a lot of time!  One question though.

    I'm trying to output the names of people in Group B that are not in Group A into a CSV file with:

    $ | where {$ -notcontains $psitem} | export-csv -Path c:\file.csv -NoTypeInformation

    but the output appears to just be the length of each name rather than the names themselves.


Leave a reply

Your email address will not be published. Required fields are marked *


© 4sysops 2006 - 2020


Please ask IT administration questions in the forums. Any other messages are welcome.


Log in with your credentials


Forgot your details?

Create Account