A PowerShell Version 1.0 script to enumerate the direct members of a large Active Directory group. This script uses ADO range retrieval to overcome the limit of 1500 values that can be retrieved for a multi-valued attribute like the "member" attribute of a group. You can pass the "pre-Windows 2000" name of the group to the script as a parameter, or it will prompt for the group name. The script outputs the distinguished names of all direct members of the group, and then outputs the total number of members. The output should be redirected to a text file.

Note that the "member" attribute of group objects does not include any members that have the group designated as their "primary" group. For this reason, in most domains the group "Domain Users" will appear to be empty, since by default this is the "primary" group for all users. The same will be true for the group "Domain Computers", since this is the default "primary" group for all computers.

This script demonstrates how to use range retrieval with ADO to handle multi-valued attributes with many values. The script reports if the group could not be found. The script works properly if the group has only a few members, or even no members.

The dsget group command line utility fails if there are more than 1500 members in the group. However, if you have PowerShell version 2.0 and the Active Directory modules that come with Windows Server 2008 R2, you can use Get-ADGroupMember (or even Get-ADObject) to retrieve the membership of large groups.


# EnumLargeGroup.ps1 
# PowerShell program to enumerate the members of a large Active Directory group. 
# If the group has more than 1500 members, you must use range retrieval. 
# Author: Richard Mueller 
# PowerShell Version 1.0 
# November 272011 
# Read group sAMAccountName from the command line or prompt for value. 
Param ($Group) 
If ($Group -eq $Null) 
    $Group = Read-Host "Enter group ""pre-Windows 2000"" name" 
# Use ADO to search entire domain. 
$Domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() 
$Root = $Domain.GetDirectoryEntry() 
$adoConnection = New-Object -comObject "ADODB.Connection" 
$adoCommand = New-Object -comObject "ADODB.Command" 
$adoCommand.ActiveConnection = $adoConnection 
$adoCommand.Properties.Item("Page Size") = 200 
$adoCommand.Properties.Item("Timeout") = 30 
$adoCommand.Properties.Item("Cache Results") = $False 
$Base = $Root.distinguishedName 
$Scope = "subtree" 
$Filter = "(&(objectCategory=group)(sAMAccountName=$Group))" 
# Setup range limits. 
$Last = $False 
$RangeStep = 999 
$LowRange = 0 
$HighRange = $LowRange + $RangeStep 
$Total = 0 
$ExitFlag = $False 
    If ($Last -eq $True) 
        # Retrieve remaining members (less than 1000). 
        $Attributes = "member;range=$LowRange-*" 
        # Retrieve 1000 members. 
        $Attributes = "member;range=$LowRange-$HighRange" 
    $Query = "<LDAP://$Base>;$Filter;$Attributes;$Scope" 
    $adoCommand.CommandText = $Query 
    $adoRecordset = $adoCommand.Execute() 
    $Count = 0 
    $Members = $adoRecordset.Fields.Item("$Attributes").Value 
    If ($Members -eq $Null) 
        "Group $Group not found" 
        $Last = $True 
        # If $Members is not an array, no members were retrieved. 
        If ($Members.GetType().Name -eq "Object[]") 
            ForEach ($Member In $Members) 
                # Output the distinguished name of each direct member of the group. 
                $Count = $Count + 1 
    $Total = $Total + $Count 
    # If this is the last query, exit the Do loop. 
    If ($Last -eq $True) {$ExitFlag = $True} 
        # If the previous query returned no members, the query failed. 
        # Perform one more query to retrieve remaining members (less than 1000). 
        If ($Count -eq 0{$Last = $True} 
            # Retrieve the next 1000 members. 
            $LowRange = $HighRange + 1 
            $HighRange = $LowRange + $RangeStep 
} Until ($ExitFlag -eq $True) 
"Total: $Total"