# 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.
# Copyright (c) 2011 Richard L. Mueller
# PowerShell Version 1.0
# November 27, 2011
#
# You have a royalty-free right to use, modify, reproduce, and
# distribute this script file in any way you find useful, provided that
# you agree that the copyright owner above has no warranty, obligations,
# or liability for such use.

# 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"
$adoConnection.Open("Provider=ADsDSOObject;")
$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

Do
{
    If ($Last -eq $True)
    {
        # Retrieve remaining members (less than 1000).
        $Attributes = "member;range=$LowRange-*"
    }
    Else
    {
        # 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
    }
    Else
    {
        # 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.
                $Member
                $Count = $Count + 1
            }
        }
    }
    $adoRecordset.Close()
    $Total = $Total + $Count

    # If this is the last query, exit the Do loop.
    If ($Last -eq $True) {$ExitFlag = $True}
    Else
    {
        # 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}
        Else
        {
            # Retrieve the next 1000 members.
            $LowRange = $HighRange + 1
            $HighRange = $LowRange + $RangeStep
        }
    }
} Until ($ExitFlag -eq $True)

$adoConnection.Close()
"Total: $Total"