3

I have a script that recursively lists group members. The issue is that there are over 5K so I cannot use Get-ADGroupMember, and I also need to only get enabled users. UAC, despite microsoft documentation, does not show enabled users only. I have this but it does not filter enabled.

Function Get-MyLargeGroup {
[cmdletbinding()]
Param(
[Parameter(Position=0,ValueFromPipeline,ValueFromPipelineByPropertyName)]
[ValidateNotNullorEmpty()]
[string]$Name)

Begin {
    Write-Verbose "Starting $($MyInvocation.MyCommand)"
} #begin

Process {
Write-Verbose "Retrieving members from $Name"
$mygroup = Get-ADGroup -Identity $Name -Properties Members

foreach ($member in $mygroup.members) {
  $object = $member | Get-ADObject -Properties samaccountname,enabled
  if ($object.ObjectClass -eq 'Group') {
    Write-Verbose "Found nested group $($object.distinguishedname)"
    #recursively run this command for the nested group
    & $MyInvocation.MyCommand -name $object.Name
  } 
  else {
   Select-Object -InputObject $object -property ObjectClass,Name,SamAccountname,DistinguishedName,enabled
  }
} #foreach
} #process

End {
    Write-Verbose "Ending $($MyInvocation.MyCommand)"
} #end

} #end function
3

Unless there's some really old limitation with Get-ADUser I'm not aware of, there shouldn't be a problem using it for a query that would return more than 5k users. I just tested it from a 2008 R2 box running PowerShell 4 and my Get-ADUser query returned almost 7k users with nothing but a -Filter * and -SearchBase parameter. I'm also unclear why you think UAC has anything to do with being able to filter Enabled users.

Regardless, you don't really need a recursive script for this task. There's an LDAP filter you can use that will return the full nested list of a group's members called LDAP_MATCHING_RULE_IN_CHAIN.

# first, get the DN of the group
$groupDN = (Get-ADGroup $Name).DistinguishedName

# now use it to get the nested members
Get-ADUser -LDAPFilter "(memberOf:1.2.840.113556.1.4.1941:=$groupDN)" -Property SamAccountname,Enabled | select ObjectClass,Name,SamAccountname,DistinguishedName,enabled

# alternatively, you can filter out the disabled users in the same query
Get-ADUser -LDAPFilter "(&(memberOf:1.2.840.113556.1.4.1941:=$groupDN)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))" -Property SamAccountname,Enabled | select ObjectClass,Name,SamAccountname,DistinguishedName,enabled
  • Get-ADGroupMember cannot go beyond 5 K :-( – user206106 Jun 26 '17 at 16:49
  • Get-ADGroupMember not Get-ADUser, sorry! – user206106 Jun 26 '17 at 16:53
  • 1
    Gotcha. Yeah. The limitation of Get-ADGroupMember -Recursive can be worked around by using Get-ADUser query with a memberOf filter as I showed above. – Ryan Bolger Jun 26 '17 at 18:42
  • 1
    Keep in mind that while the LDAP Filter gets around the 5,000 object limitation, it is highly inefficient. You will be querying every user object in the domain and then looking for the group, vs querying only the group and looking for the members. Get-ADUser will be limited to returning only user objects. Use Get-ADObject if you need to return other object types. – twconnell Jun 27 '17 at 11:30
  • 1
    The LDAP matching rule transitive evaluation for recursive group membership does have a reputation for poor performance in very large environments, but I don't think that would apply here (a few thousand isn't very large). See: serverfault.com/a/848041/20701 – Greg Askew Jun 28 '17 at 15:05
0

The reason your script is not working for enabled users is because 'enabled' is not a valid property of the Get-ADObject cmdlet. It is valid for Get-ADUser and Get-ADComputer. With Get-ADObject, you need to decode the value from the userAccountControl attribute.

The 5,000 record limit on Get-ADGroupMember is a limitation set by AD Web Services running on your Domain Controllers. You can modify an ADWS parameter to allow larger results to be returned.

You can return more than 5,000 group members without modifying ADWS parameters using the below PowerShell:

(Get-ADGroup -Identity "SomeGroupName" -Properties Members).members

You would need to loop through those results with the Get-ADObject cmdlet to query additional object information.

  • Obviously data talks here. But my gut says the single larger Get-ADUser query with the embedded fetching of additional properties will ultimately be faster than multiple calls to Get-ADGroup to account for nesting as well as the additional Get-ADObject calls for more metadata. – Ryan Bolger Jun 27 '17 at 16:55
  • I managed to run a test in my lab environment against a group with 2 nested groups (one nested in the other, so 3 levels) that had a total of 4532 users in it. The single Get-ADUser call took on avg 10 seconds. The Get-ADGroup and subsequent Get-ADObject calls took on avg 90 seconds. – Ryan Bolger Jun 27 '17 at 18:27
  • The reason you see an "average of 10 seconds" is because Active Directory caches the results in memory so it can return them faster the next time the same query is used. This is exactly why ADWS has a limit on query size, to prevent a poorly written query from using up the servers memory. The amount of time the LDAP query method takes to return results will depend on how large your environment is. I would not use this in places that have over 20,000 users. If you know you need to query large groups, just modify ADWS to allow larger results to be returned. – twconnell Jun 28 '17 at 14:20
  • That's fair. There are always tradeoffs. But it didn't sound like the OPs environment was that large either. – Ryan Bolger Jun 28 '17 at 17:32
  • The title of the post is literally "List users recursively in large organization AD" - But I will admit the LDAP filter is the best alternative available when raising the ADWS limit is not an option. – twconnell Jun 28 '17 at 17:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.