3

PowerShell question for you savvy folks. Is it possible to filter a Get-ADGroup command based on group size (aka only return groups greater than x members)? I'm trying to filter in a more efficient way than: Get-ADGroup -Filter * and then running the member count check after, as in | where {$_.members.count -gt 10,000} or something.

But I'm having a hard time doing any kind of member count check on the initial filter so I don't have to return every group object in the individual domain and then check membership count. This is because the AD instance I'm querying against has a huge amount of AD groups that takes a long time to pull all groups first, then check.

I've tried variations of the below, but I'm guessing that the initial "members" property doesn't exist in the attribute set you can query against:

Get-ADGroup -Properties members -Filter {members -ge 10,000}

Any help is appreciated!

5
  • 2
    The only way I see for improving this is filtering for only groups that have any members instead of all groups and using a foreach loop + if for filtering instead of Where-Object. Commented Apr 12, 2022 at 21:38
  • The other alternative would be multi threading, you should also clarify if you're searching for groups with more than 10k members where members are user objects only or all object class, and if you're looking for direct membership or recursive. Commented Apr 12, 2022 at 22:09
  • 1
    Note: Don't use ScriptBlocks for the -Filter parameter. I also cover some additional gotcha's with using ScriptBlocks and relying on the cmdlet to expand your variables for you from string literals in my own answer here in the Things to Avoid section. Commented Apr 14, 2022 at 16:27
  • 1
    @SantiagoSquarzon good point about at least being able to remove 0 membership groups from the query. In my actual script for this, I am using foreach, if, etc as I am cycling through several domains and logging results. I'm really only concerned with direct membership user objects currently. Commented Apr 14, 2022 at 19:44
  • Mathias's answer covers the filter for groups having at least 1 member, the only thing that would need to be changed is the use of a foreach loop + if for filtering instead of the slow where-object. or you could use | & { process { if($_.members.count -gt 10,000) { $_ } } which would also be very fast compared to where-object Commented Apr 14, 2022 at 19:46

2 Answers 2

2

Is it possible to filter a Get-ADGroup command based on group size (aka only return groups greater than x members)?

No!

The LDAP query filter syntax supported by Active Directory does not have any facility for specifying the count of multi-valued attributes.

You need to query the directory for groups that have any members, then count the result set client-side:

Get-ADGroup -LDAPFilter '(&(objectClass=group)(member=*))' -Properties member |Where-Object { $_.member.Count -gt 10000 } 
Sign up to request clarification or add additional context in comments.

1 Comment

Like was also mentioned in the comments by Santiago, I like the addition of members=* here. At the scale I'm dealing with, even slight improvements like this help. Thanks for the info on the multi-valued as well. It is, unfortunately, what I expected.
1

This is how you can improve the speed of your query with multi-threading, using Runspaces in this example, the main idea is to get all the OUs in the Domain and let each runspace query a specific OU at the same time (as many queries as defined in $threads).

This should improve the speed of the script by a big amount, however, this requires tweaking, if you have too many threads running at the same time it is likely that it can fail.

$ErrorActionPreference = 'Stop' # define the params that will be passed to the runspaces $params = @{ LDAPFilter = "(member=*)" SearchScope = 'OneLevel' Properties = 'member' } # define the logic of the runspace $scriptblock = { param($params) foreach($group in Get-ADGroup @params) { if($group.Member.Count -gt 10000) { $group } } } try { # get all OUs $ous = (Get-ADOrganizationalUnit -Filter *).DistinguishedName # get all Domain Controllers available # we don't want to make too many queries to the same DC!! $dcs = (Get-ADDomainController -Filter *).Name # define the number of threads that can run at the same time # maybe you could use `$dcs.Count` as Threads # this depends on your server's resources and your network $threads = 10 $RunspacePool = [runspacefactory]::CreateRunspacePool(1, $threads) $RunspacePool.Open() $runspace = foreach($ou in $ous) { $params['SearchBase'] = $ou $params['Server'] = $dcs[$i++ % $dcs.Count] $ps = [powershell]::Create() $ps.AddScript($scriptblock).AddParameter('params', $params) $ps.RunspacePool = $RunspacePool [pscustomobject]@{ Instance = $ps Task = $ps.BeginInvoke() } } # capture the output from each runspace here! $result = foreach($r in $runspace) { $r.Instance.EndInvoke($r.Task) $r.Instance.foreach('Dispose') } } catch { Write-Warning $_.Exception.Message } finally { $runspace.foreach('Clear') $RunspacePool.foreach('Dispose') } $result | Format-Table 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.