List Forest-wide Active Directory Group Memberships using PowerShell

How to list a user's forest-wide group memberships in Active Directory using PowerShell.  It's not as easy as you think.  Sure, you can just get the contents of the user's memberOf attribute, but that only contains groups from the domain that the user belongs to.  What if your AD forest has multiple domains, and the user belongs to groups from more than one domain?  In that case, you have to get the user's tokenGroups attribute.

The tokenGroups attribute is a multi-valued attribute that contains a list of SIDs of the groups the user belongs to, stored as byte arrays.   The attribute is populated behind the scenes by AD.  To get the list of token groups, you first have to explicitly retrieve the contents of the attribute by calling GetInfoEX.  Then, for each token in the list, we create a security principal object that we can translate into a user name.  I toss the results into a hashtable so that I can sort the output.

The Perl version (see Enumerate TokenGroups using Perl) is slightly more complicated in that you have to convert the bytes yourself.  PowerShell does the conversion for you automatically.

$username=$args[0]
if($args.count -lt 1){
 "Usage: ./tokenGroups.ps1 <username>"
}
$groups=@{}
$gc="GC://" + $([adsi] "LDAP://RootDSE").Get("RootDomainNamingContext")
$filter = "(&(objectCategory=User)(|(cn=" + $username + ")(samaccountname=" + $username + ")(displayName=" + $username + ")(distinguishedName=" + $username + ")))"
$domain = New-Object System.DirectoryServices.DirectoryEntry($gc)
$searcher = New-Object System.DirectoryServices.DirectorySearcher
$searcher.SearchRoot = $domain
$searcher.Filter = $filter
$results = $searcher.FindAll()
if($results.count -eq 0){ "User Not Found"; }else{
 foreach ($result in $results){
  $user=$result.GetDirectoryEntry();
  $user.GetInfoEx(@("tokenGroups"),0)
  $tokenGroups=$user.Get("tokenGroups")
  foreach ($token in $tokenGroups){
   $principal = New-Object System.Security.Principal.SecurityIdentifier($token,0)
   $group = $principal.Translate([System.Security.Principal.NTAccount])
   $groups[$group]=1
  }
 }
}
$groups.keys | sort-object

4 comments:

Anonymous said...

I have not been able to successfully run this PowerShell script. I understand the explanation of why it is difficult to gather cross-domain memberships, but I am struggling with running the PowerShell.

Please excuse my ignorance, but would you be so kind as to provide instructions on how I need to save and execute this script?

Thank you!

Brian said...

Sorry, posting the script in html broke the code. It's fixed now.

Anonymous said...

Marvelous script it is ! , thanks a lot.

Anonymous said...

Very handy script. Thanks a lot.

Post a Comment

Related Posts Plugin for WordPress, Blogger...