List all of a users group memberships

Download the script here

Auditing user access is hard, usually you audit from resource out - eg, finding all Domain Admins, or finding all users with full acecss to SecretShare$ share on SecretServer01. But occasionally want to audit from user out.. this is hard.. even impossible (if you have a very big environment). Lets talk about the first step Enumerating Nested Groups.

Token Groups

Each user has a constructed attribute called tokengroups that returns a list of all transitive group memberships. You can query tokengroups using get-adobject like this…

$userDN = 'CN=Tom,OU=sales,DC=wrish,DC=com'
get-adobject -SearchBase $userDN -SearchScope Base -Properties TokenGroups  -filter *

… but there are a couple of problems, you get back SIDs and only SIDs, that means no Distribution Only groups and you have to manually convert them into objects or DNs if you want useful visual reporting. You can do that pretty easily (but slowly) like this…

function get-TokenGroups ($objectDN) {
    $ADObject = get-adobject -SearchBase $objectDN -SearchScope Base -Properties TokenGroups  -filter *
    foreach ($Sid in $ADObject.tokengroups){
        ([ADSI]"LDAP://<SID=$SID>").distinguishedname
    }       
}
get-TokenGroups  'CN=Tom,OU=sales,DC=wrish,DC=com'

… also, some groups are missing in a multi-domain environment - Domain Local groups in remote domains. Domain Local group memberships aren’t replicated to the Global Catalog, so the GC doesn’t add them to the tokengroups value when queried.

MemberOf Evaluation

Each user has a MemberOf attribute which you can query recursively to get at all those juicy remote domain groups. There are plenty of examples of these scripts available they usually commit the various sins of PowerShelling like exporting to csv or not generating objects at all or a particularly good one that even outputs some nice verbose info… but breaks when group memberships traverse domains.

They are also all incredibly complicated, sigh.

function EnumerateMemberOf($Object, $ResultSoFar=@())
{ 
#Helper function to walk $object's memberof attribute and list out all group memberships    
    if ($object.memberof){
        $Results =  @();        
        foreach ($group in $Object.memberof){
            #prevent nesting loops trapping by checking to make sure the group hasn't been searched already
            if ($ResultSoFar -notcontains $Group) {
                #Bind directly to the group with ADSI - this will automatically follow referrals and work with 
                #multi domain forests
                $TempGroup = [ADSI]"LDAP://$Group" ;
                $ResultSoFar += $Group.ToString();
                #Enumerate the next level of memberof
                $Results += EnumerateMemberOf $TempGroup $ResultSoFar ;
                $Results += $Group;
            }            
         }
        return $Results
    } 
}

function get-ADNestedMembership ($Identity) 
    $ADuser = get-aduser $Identity -Properties memberof,distinguishedname,primaryGroup
    write-output (new-object psobject -property @{distinguishedname=$aduser.distinguishedname;'NestedMemberOf'=(@(enumerateMemberof $ADuser)+(enumerateMemberof (get-adgroup $AdUser.primaryGroup -properties memberof)))})
}

For the advanced functions (with pipline goodness) you’ll need to download it https://gallery.technet.microsoft.com/Get-nested-group-e9ce3687.