Use PowerShell to Determine the Differences in Group Membership between Active Directory Users

I recently saw a post on Reddit where someone was trying to create a function that takes an Active Directory user name as input for a manager who has direct reports (subordinates) specified in Active Directory. They wanted to determine if the Active Directory group membership of any of those subordinates is different than the others.

There are two different parts to this scenario. Returning a list of the manager's direct reports by querying that property from the manager's user account in Active Directory:

1Get-ADUser -Identity sbuchanan -Properties directReports |
2Select-Object -ExpandProperty directReports

adgroup-diff1.jpg

I decided to keep that portion separate since it would be easy enough to accomplish that part of the task and hard coding that functionality would limit the re-usability of the group comparison portion of the tool. I wanted the users id's (input for my tool) to be able to come from a query against Active Directory, a list of user id's stored in a text file, or a CSV file (maybe an auditor supplies a list of user id's to compare that he emails to you).

The following PowerShell function compares the Active Directory user groups of one or more users. The function gets a combined list of all groups that the specified users are in. It then determines what are considered to be common groups between the users by determining which of those groups have 50% or more of the specified users in them. Finally, it iterates through each user comparing their group membership to the common group list and returns the user's group membership where it differentiates from the list.

  1#Requires -Version 3.0
  2#Requires -Modules ActiveDirectory
  3function Compare-MrADGroup {
  4
  5<#
  6.SYNOPSIS
  7    Compares the groups of a the specified Active Directory users.
  8
  9.DESCRIPTION
 10    Compare-MrADGroup is a function that retrieves a list of all the Active
 11    Directory groups that the specified Active Directory users are a member
 12    of. It determines what groups are common between the users based on
 13    membership of 50% or more of the specified users. It then compares the
 14    specified users group membership to the list of common groups and returns
 15    a list of users whose group membership differentiates from that list. A
 16    minus (-) in the status column means the user is not a member of a common
 17    group and a plus (+) means the user is a member of an additional group.
 18
 19.PARAMETER UserName
 20    The Active Directory user(s) account object to compare. Can be specified
 21    in the form or SamAccountName, Distinguished Name, or GUID. This parameter
 22    is mandatory.
 23
 24.PARAMETER IncludeEqual
 25    Switch parameter to include common groups that the specified user is a
 26    member of. An equals (=) sign means the user is a member of a common group.
 27
 28.EXAMPLE
 29     Compare-MrADGroup -UserName 'jleverling', 'lcallahan', 'mpeacock'
 30
 31.EXAMPLE
 32     'jleverling', 'lcallahan', 'mpeacock' | Compare-MrADGroup -IncludeEqual
 33
 34.EXAMPLE
 35     Get-ADUser -Filter {Department -eq 'Sales' -and Enabled -eq 'True'} |
 36     Compare-MrADGroup
 37
 38.INPUTS
 39    String
 40
 41.OUTPUTS
 42    PSCustomObject
 43
 44.NOTES
 45    Author:  Mike F Robbins
 46    Website: http://mikefrobbins.com
 47    Twitter: @mikefrobbins
 48#>
 49
 50    [CmdletBinding()]
 51    param (
 52        [Parameter(Mandatory,
 53                   ValueFromPipeline)]
 54        [string[]]$UserName,
 55
 56        [switch]$IncludeEqual
 57    )
 58
 59    BEGIN {
 60        $Params = @{}
 61
 62        If ($PSBoundParameters['IncludeEqual']) {
 63            $Params.IncludeEqual = $true
 64        }
 65    }
 66
 67    PROCESS {
 68        foreach ($name in $UserName) {
 69            try {
 70                Write-Verbose -Message "Attempting to query Active Directory of user: '$name'."
 71                [array]$users += Get-ADUser -Identity $name -Properties MemberOf -ErrorAction Stop
 72            }
 73            catch {
 74                Write-Warning -Message "An error occured. Error Details: $_.Exception.Message"
 75            }
 76        }
 77    }
 78
 79    END {
 80        Write-Verbose -Message "The `$users variable currently contains $($users.Count) items."
 81
 82        $commongroups = ($groups = $users |
 83        Select-Object -ExpandProperty MemberOf |
 84        Group-Object) |
 85        Where-Object Count -ge ($users.Count / 2) |
 86        Select-Object -ExpandProperty Name
 87
 88        Write-Verbose -Message "There are $($commongroups.Count) groups with 50% or more of the specified users in them."
 89
 90        foreach ($user in $users) {
 91            Write-Verbose -Message "Checking user: '$($user.SamAccountName)' for group differences."
 92
 93            $differences = Compare-Object -ReferenceObject $commongroups -DifferenceObject $user.MemberOf @Params
 94
 95            foreach ($difference in $differences) {
 96                [PSCustomObject]@{
 97                    UserName = $user.SamAccountName
 98                    GroupName = $difference.InputObject -replace '^CN=|,.*$'
 99                    Status = switch ($difference.SideIndicator){'<='{'-';break}'=>'{'+';break}'=='{'=';break}}
100                    'RatioOfUsersInGroup(%)' = ($groups | Where-Object name -eq $difference.InputObject).Count / $users.Count * 100 -as [int]
101                }
102            }
103        }
104    }
105}

Now to use PowerShell to query the "Direct reports" of a manager in Active Directory and return those users as input for our group comparison tool:

adgroup-diff4.jpg

That task can be performed with this simple PowerShell one-liner:

1Get-ADUser -Identity sbuchanan -Properties directReports |
2Select-Object -ExpandProperty directReports |
3Compare-MrADGroup

adgroup-diff2a.jpg

As shown in the previous set of results, a minus in the status column means the user is not a member of a common group and a plus means they are a member of an extra group other than the common ones. The RatioOfUsersInGroup(%) column returns a percentage value of how many users are in the specified group, for example 50% (3 of the 6 users) are in both the Faculty and Staff groups and only 17% (1 of the users) is in the Test01 group.

1Compare-MrADGroup -UserName 'jleverling', 'lcallahan', 'mpeacock' -IncludeEqual

adgroup-diff3b.jpg

An equal sign will only show up in the status column when the IncludeEqual parameter is specified. It means that the users are included in the common group(s) as shown in the previous example.


Update:

The most recent version of the Compare-MrADGroup function shown in this blog article can be downloaded from my ActiveDirectory repository on GitHub.

µ