- Use PowerShell splatting and PSBoundParameters to pass parameters - Wed, Nov 9 2022
- Using PowerShell with $PSStyle - Mon, Jan 24 2022
- Clean up user profiles with PowerShell - Mon, Jun 9 2014
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.
PS C:\> $a= Get-ADGroupMember GroupA PS C:\> $a | Select name name ---- Ahmad Castelum Amber Kasey Alexis Hembrey Andra Papallo Anderson Lefurgy PS C:\> $b = Get-ADGroupMember GroupB PS C:\> $b| Select name name ---- Catherina Olexy Amber Kasey Bettina Tamburri Berry Tototzintle Ashley Abramian Andra Papallo Anderson Lefurgy
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.
PS C:\> compare-object $a $b InputObject SideIndicator ----------- ------------- CN=Catherina Olexy,OU=Demo,OU=Employ... => CN=Bettina Tamburri,OU=Demo,OU=Emplo... => CN=Berry Tototzintle,OU=Demo,OU=Empl... => CN=Ashley Abramian,OU=Demo,OU=Employ... => CN=Ahmad Castelum,OU=Demo,OU=Employe... <= CN=Alexis Hembrey,OU=Demo,OU=Employe... <=
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.
PS C:\> compare-object $a $b -Property name name SideIndicator ---- ------------- Catherina Olexy => Bettina Tamburri => Berry Tototzintle => Ashley Abramian => Ahmad Castelum <= Alexis Hembrey <=
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.
PS C:\> $a.name | where {$b.name -contains $psitem} Amber Kasey Andra Papallo Anderson Lefurgy
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.
PS C:\> $a.name | where {$b.name -notcontains $psitem} Ahmad Castelum Alexis Hembrey
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.
PS C:\> $b.name | where {$a.name -notcontains $psitem} Catherina Olexy Bettina Tamburri Berry Tototzintle Ashley Abramian
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.
PS C:\> $a | where {$b.samaccountname -notcontains $psitem.samaccountname} distinguishedName : CN=Ahmad Castelum,OU=Demo,OU=Employees,DC=GLOBOMANTICS,DC=local name : Ahmad Castelum objectClass : user objectGUID : 5632694a-37c1-44e2-8666-2e1da10bd4f7 SamAccountName : ACastelum SID : S-1-5-21-2552845031-2197025230-307725880-8629 distinguishedName : CN=Alexis Hembrey,OU=Demo,OU=Employees,DC=GLOBOMANTICS,DC=local name : Alexis Hembrey objectClass : user objectGUID : ee5d1689-6e17-4cf5-880b-5889ea929301 SamAccountName : AHembrey SID : S-1-5-21-2552845031-2197025230-307725880-8650
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.
Subscribe to 4sysops newsletter!
PS C:\> $a | where {$b.samaccountname -notcontains $psitem.samaccountname} | Disable-ADAccount -whatif What if: Performing the operation "Set" on target "CN=Ahmad Castelum,OU=Demo,OU=Employees,DC=GLOBOMANTICS,DC=local". What if: Performing the operation "Set" on target "CN=Alexis Hembrey,OU=Demo,OU=Employees,DC=GLOBOMANTICS,DC=local".
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 Pluralsight.com.
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
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:
$b.name | where {$a.name -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.
Hi Jeffery !
Very useful. Thanks
Regards