Ever needed to get all nested groups a user belongs in Active Directory?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
#Get all recursive groups a user belongs. Function Get-ADUserNestedGroups { Param ( [string]$DistinguishedName, [array]$Groups = @() ) #Get the AD object, and get group membership. $ADObject = Get-ADObject -Filter "DistinguishedName -eq '$DistinguishedName'" -Properties memberOf, DistinguishedName; #If object exists. If($ADObject) { #Enummurate through each of the groups. Foreach($GroupDistinguishedName in $ADObject.memberOf) { #Get member of groups from the enummerated group. $CurrentGroup = Get-ADObject -Filter "DistinguishedName -eq '$GroupDistinguishedName'" -Properties memberOf, DistinguishedName; #Check if the group is already in the array. If(($Groups | Where-Object {$_.DistinguishedName -eq $GroupDistinguishedName}).Count -eq 0) { #Add group to array. $Groups += $CurrentGroup; #Get recursive groups. $Groups = Get-ADUserNestedGroups -DistinguishedName $GroupDistinguishedName -Groups $Groups; } } } #Return groups. Return $Groups; } #The user to check. $User = "<SamAccountName/DN/UserPrincipal>"; #Get all groups. $Groups = Get-ADUserNestedGroups -DistinguishedName (Get-ADUser -Identity $User).DistinguishedName; #Output all groups. $Groups | Select-Object Name | Sort-Object -Property Name; |
Thank you very much. The script really helped me a lot.
very nice presentation and good DFS implementation
I encountered the need as well because of RBAC and external trusts.
I developped as well a powershell function but based on a BFS and set parameters to take into account the scope search forest, domain, domain trusts forest trusts or explicit domains. I used the.net classes so no need for the RSAT and activedirectory module. I shared the function on my github for anyone who might have some interest as well
https://github.com/criffo/getADObjectMEmberOfCustom
Criffo, your script is wonderful, big thanks
What is the need for the line : If($ADObject) ?
Because if the value is $null the foreach loop does not iterate. That is okay.
you are forgetting the PrimaryGroup – it is not always domain users.
Hi Henrik,
What are you referring to?
What Henrik is referring to is that the memberof attribute does not contain the Primary Group. Most often this is Domain Users but it cannot be assumed. Compare output from get-adprincipalgroupmembership versus the contents of the memberof attribute and you can see the discrepancy.
This function doesn’t work for Irish people 🙂
If the DistinguishedName has a ‘ in the person’s name (like O’Reilly) then it will not work.
I had a similar problem to what Perica describes, but for the names of OUs. Some of our OUs have ‘single quote’ characters in their names. This script does not like them.
Great job otherwise. Thanks.
Thank you for sharing your function, it helped me out!
Pingback: AD Nested User Permissions – ? About Tech
Hello,
Thank you for this PowerShell script. However, I am not as good as the members that commented here – I would need help in adding the samaccountname in this script for which I need to get a report of all group memberships including nested ones.
What do I add and where do I add in the script to indicate this is the samaccountname that I am looking for to get a report of group memberships?
Thank you
Sorry. I retried again. I was able to do that. It was so simple. Thank you for putting this one together and sharing this.
I had the same problem Perica and Gwyn noticed, this can be fixed by removing the single quotes around $DistinguishedName at line 11 and around $GroupDistinguishedName at line 21 AND put a back tick in front of it.
I found it here: https://social.technet.microsoft.com/wiki/contents/articles/30682.powershell-tip-passing-a-variable-in-powershell-that-contains-an-apostrophe.aspx
Recently I found out about tokenGroups[1][2] attribute, which is way faster than LDAP_MATCHING_RULE_IN_CHAIN[3], so I’m spreading the word:
To get all AD object groups recursively:
(Get-ADUser username | Get-ADUser -Properties tokenGroups).tokenGroups.ForEach({
($_ | Get-ADGroup).Name
})
Or, if you don’t need an ADGroup object, this returns a String instead, but is way faster:
(Get-ADUser username | Get-ADUser -Properties tokenGroups).tokenGroups.ForEach({
$_.Translate([System.Security.Principal.NTAccount]).Value
})
It’s almost instantaneous in our directory:
PS C:\windows\System32> (Get-ADUser -Filter *).Count
86816
PS C:\windows\System32> (Get-ADGroup -filter *).Count
1808
PS C:\windows\System32> (Get-ADComputer -filter *).Count
2666
Just for reference, here how much time it takes in this instance:
PS C:\Windows\System32>
# this returns String objects
1..10 | % {
Measure-Command {
(Get-ADUser marcossantos | Get-ADUser -Properties tokenGroups).tokenGroups.ForEach({$_.Translate([System.Security.Principal.NTAccount]).Value})
}
} | Measure-Object -Average TotalSeconds | select @{l=”Type”;e={“String”}},Average
# this returns ADGroup objects
1..10 | % {
Measure-Command {
(Get-ADUser marcossantos | Get-ADUser -Properties tokenGroups).tokenGroups.ForEach({($_ | Get-ADGroup).Name})
}
} | Measure-Object -Average TotalSeconds | select @{l=”Type”;e={“ADGroup”}},Average
Type Average
—- ——-
String 0.01926139
ADGroup 0.28920375
One downside of this approach is that it does not retrieve distribution groups.
[1]https://ldapwiki.com/wiki/TokenGroups
[2]https://learn.microsoft.com/en-us/windows/win32/adschema/a-tokengroups
[3]https://ldapwiki.com/wiki/LDAP_MATCHING_RULE_IN_CHAIN