Powershell show users who are members of a group twice – once directly once indirectly

active-directorypowershell

I have started setting up groups like:

  • Department-staff
    • Department-supervisors
      • Department-director

Where Department-supervisors is a member of Department-staff, and Department-director is a member of Department-supervisors.

I know how to use powershell to list what groups a person is a member of, either directly assigned, or inherited though the nested groups.

But what I'm trying to figure out is a script that will show me all the users who are members of one of those groups BOTH directly and indirectly, eg. User A is a direct member of Department-staff and Department-director.

My googling fails me in trying to find or write a script that can do that.

Edit:

In reality, there are more layers and branches to the nesting, and many departments, so it gets confusing. I want to get a list of members who are part of any group directly, and also a member of the same group indirectly so I can remove the redundant group direct memberships.

Best Answer

The following will walk a given parent group (name must be compatible with Get-ADGroupMember syntax) and populate two hashes. One hash will have user sAMAccountName as the key and a array of direct group memberships as the value. The second hash will contain keys of all processed groups.

It will correctly detect, and skip, circular group nestings (e.g. parent is child of child). You can comment out the Write-Host lines for less line noise in the console.

Import-Module ActiveDirectory

function Walk-Group ( $group ) {

    Get-ADGroupMember $group | Group-Object objectClass -ash | Foreach-Object {

        # Add each user to $users hash, add current group to their collection
        if ( $_.user ) {
            foreach ( $u in $_.user.GetEnumerator() ) {
                if ( $users[$u.sAMAccountName] ) {
                    $users[$u.sAMAccountName] += $group
                } else {
                    $users.Add( $u.sAMAccountName, @($group) )
                }
            }
        }

        # Recurse into each child group, skip if group is circular member
        if ( $_.group ) {
            foreach ( $g in $_.group.GetEnumerator() ) {
                if ( $groups[$g.Name] ) {
                    Write-Host "Existing:" $g.Name
                } else {
                    Write-Host "New Group:" $g.Name
                    $groups.Add( $g.Name, $true )
                    Walk-Group $g.Name
                }
            }
        }

    }
}

# Hash to collect user/group info.
$users = @{}

# Hash to collect processed groups.
$groups = @{}

# Root group to walk
Walk-Group "Department-staff"

# Display users with mulitple direct memberships
$users.GetEnumerator() | Where-Object { $_.Value.Count -gt 1 }