Quite often, projects come to a point when all members of a certain AD group have to be read out recursively.
Index
Recursive list of groups and members
What does recursive mean?
If an AD group has several other groups as members, then all the members should be displayed in a list and not the actual groups. Usually, in 99% of all cases the final result is supposed to be a “flat” list of all users.
Command Get-ADGroupMember for a flat users list
To achieve the 99%, you will only need the following line:
1 |
Get-ADGroupMember "My Group" -Recursive |
Unfortunately, the command may be very slow and therefore not suitable for frequently running scripts with multiple groups.
LDAP query and OIDs
Get-ADGroupMember uses a so-called OID named “LDAP_MATCHING_RULE_IN_CHAIN” in the background. Alternatively, you can also use the following command:
1 2 |
Get-ADObject -LDAPFilter "(objectCategory=person)(objectClass=user)(memberOf:1.2.840.113556.1.4.1941:=CN=MyGroup,DC=MyDomain,DC=net))" |
This type of LDAP query is much slower than a normal query. More information can be found here.
Easily resolving nested groups
FirstWare DynamicGroup software offers a faster option as it enables you to resolve nested groups within seconds.
With DynamicGroup, if members of the nested groups change, these changes are dynamically updated. There are only those members in the “final group” who are also in the “members groups”. Even if “members groups” change.
With the “Flat Group” feature you can disentangle all groups which are members of an AD group. Thanks to DynamicGroup, you are able to resolve nested groups and only display the actual members (users) of a group:
Thanks to this option you can operate faster and always stay up to date. As its name implies, the software creates dynamic groups which instantly reflect all the changes in AD.
PowerShell: Keep specific AD groups as direct members
In some cases, however, certain groups should not be resolved and should remain as “direct” members.
We can only achive this with our own little customised function. This function shall open the groups recursively and check whether one of the members is a group that should be “preserved”.
Set groups you do not want to resolve
Here is a (rough) sequence of the script:
- Search group in AD and go through all members (attribute “member”)
- Check:
- Member already in final member list? -> Skip
- Member in list of groups that should not be resolved? -> add it to a.
- Member of group-cache? -> To b.
- Return hashtable with <distinguishedName,AD-Object>
To run the script enter the following line:
PS C:\> $members = .\resolve-members-recursive.ps1 -groupName „MyGroup“
Complete script “Resolve selected AD groups recursively”
As described above, the return object is a hashtable in the format of <distinguishedName,AD-Object>. If you only need one list of distinguishedNames, you can just use $members.Keys. If you need a list of AD objects (similar to “Get-ADGroupMember”), you can just use $members.Values.
And finally, here is the script to resolve selected nested groups:
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 46 47 48 49 50 51 52 53 54 55 56 57 |
param([Parameter(Mandatory = $true)][String]$groupName) $groupsHT = @{} # This is our group cache $membersHT = @{} # These are our members function groupShouldNotBeResolved { param($member) $groupsToNotResolve = @( # These are CNs! Make sure that your sAMAccountNames and CNs match! "Domain Users" # Feel free to edit these! "SomeGroup" ) foreach($group in $groupsToNotResolve) { # We iterate through our list of groups... if($member.StartsWith(("CN=" + $group + ","), "CurrentCultureIgnoreCase") -eq $true) { # ...and check if our member matches $groupToNotResolveAD = Get-ADObject -Identity $member # If we find a match, we get it from AD $groupsHT.Add($member, $groupToNotResolveAD) # And add it to our list of groups, so we know it next time return $true # Let caller know this group should not be resolved } } return $false # This group should be resolved! } function resolve-members-recursive { param($members) # The input is a list of members (distinguishedNames) foreach($member in $members) { # We look at each member / distinguishedName if($membersHT.Contains($member) -eq $true) { # If the distinguishedName is already in our list of members, we skip it continue } elseif((groupShouldNotBeResolved $member) -eq $true) { # If the member is a group that should not be resolved.... $membersHT.Add($member, $groupsHT.$member) # We add it to our members list } elseif($groupsHT.Contains($member) -eq $true) { # If the distinguishedName is already in our group cache... resolve-members-recursive $groupsHT.$member # Resolve its members recursively! } else { # If the distinguishedName is in neither cache, we find out what it is... $memberAD = Get-ADObject -Identity $member -Properties member # ... from AD! if($memberAD.objectClass -eq "group") { # If it's a group... $groupsHT.Add($memberAD.distinguishedName, $memberAD.member) # We add it to our group cache resolve-members-recursive $groupsHT.$member # And resolve its members recursively } else { # If it's not a group, it must be a user... $membersHT.Add($member, $memberAD) # So we add it to our members list } } } } $groupToResolve = Get-ADObject -LDAPFilter ("(&(objectClass=group)(objectCategory=group)(sAMAccountName=" + $groupName + "))") -Properties member if($groupToResolve -eq $null) { Write-Host ($groupName + " could not be found in AD!") return $null } else { resolve-members-recursive $groupToResolve.member return $membersHT } |
Please note comparisson with $Null:
The script includes if($groupToResolve -eq $null), according to our reader pamkkkk you should avoid using comparisons against $Null. For more information, please read the links in the comment section.
FirstAttribute AG – Identity Management & IAM Cloud Services
We would be happy to present our services and solutions to you. Get in touch and find out how we can help you.
6 Comments
Leave your reply.