Last Update: Sep 04, 2024 | Published: Jan 21, 2015
In a recent PowerShell Problem Solver article I demonstrated how you can use PowerShell to enumerate members of an Active Directory group. I believe that for most of you the code samples in that article will suffice. But as I pointed out at the end of the article, there is a potential issue with very large groups.
A large group will be anything with more than 5000 members. It is still possible to enumerate these groups, but it will take a few extra steps. We will still be using the Active Directory cmdlets, but we will need to process the group members individually, especially if you want to recurse through any nested groups. I’m going to demonstrate with a group called Test Group 1 that I know has more than 5000 members, including a few nested groups.
Get-adgroup 'Test Group 1' –properties Member | select –expandproperty Member | measure-object
The problem is that I can’t use Get-ADGroupmember.
So I’ll have to take matters into my own hands with a recursive function.
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 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 } } #foreach } #process End { Write-Verbose "Ending $($MyInvocation.MyCommand)" } #end } #end function
The function takes the name of a group and retrieves the group along with its Members property. This will be a collection of distinguishednames for ever member. It might be a user, a computer or another group. I can use Get-ADObject to retrieve the member from Active Directory.
$object = $member | Get-ADObject -Properties samaccountname
All members of a group will have a samaccountname property, so I’ll grab that as well. From here I can check the object class to determine if it is a user, computer or another group. If it is a group then I can recursively call the function and enumerate the nested group. Otherwise, I’ll select some properties from the user or computer object.
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
}
I called the function Get-MyLargeGroup, but you can name it anything you want. To use, simply specify the name of a group.
I have saved the results to a variable $r2. From the verbose output, you can see that it found nested groups and enumerated those as well. How many members did I find?
However, there is a potential wrinkle. It is possible that a user account might be a member or more than one group inside this group.
For example, a user could be a direct member of Test Group 1 and a member of the nested group, Project Black. Let’s filter those duplicates out. I know for a fact there are duplicates in this group.
>$r3 = $r2 | Select-Object * -Unique
Not a lot, but a few. Here are the final results.
$r3 | Out-Gridview -Title "Test Group 1 Members"
The intent from my previous post was to export results to a CSV file, which I can still accomplish:
$r3 | export-csv c:workTest-Group-1.csv -NoTypeInformation
I can use this function actually for any size group and come up with a one line command to export group members to a CSV file.
Get-MyLargeGroup -Name "Chicago Engineering" | Select * -unique | sort name | export-csv c:workengineering.csv -NoTypeInformation
There won’t be any Groups in the output because they were enumerated. But there could be computer accounts and perhaps you only really care about user accounts. Here’s an example of what I’m talking about.
To filter them out I could simply pipe to Where object.
Or for the sake of simplicity, I could modify my function. Here’s a variation that limits the output to user accounts. And because you know you are querying for user accounts, you can specify additional properties that only apply to users.
Function Get-MyLargeGroup2 { [cmdletbinding()] Param( [Parameter(Position=0,ValueFromPipeline,ValueFromPipelineByPropertyName)] [ValidateNotNullorEmpty()] [string]$Name, [string[]]$Properties = @("SamAccountname","Title","Description","Department") ) Begin { Write-Verbose "Starting $($MyInvocation.MyCommand)" #add default properties $Properties += "ObjectClass" $Properties += "Name" $Properties += "DistinguishedName" Write-verbose "retrieving these user properties" Write-Verbose ( $Properties -join "," | Out-String) } #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 $properties 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 } elseif ($object.objectclass -eq 'User') { Select-Object -InputObject $object -property $Properties } } #foreach } #process End { Write-Verbose "Ending $($MyInvocation.MyCommand)" } #end } #end function
This version includes a parameter to specify additional user properties. In the Begin scriptblock, I add some standard properties.
Now, the only output from the function is if the object is a user.
elseif ($object.objectclass -eq 'User') {
Select-Object -InputObject $object -property $Properties
}
Again, I can use this for any size group.
Get-mylargegroup2 "Project Black" | out-gridview –title "Project Black"
Now you should have several ways to enumerate members of an Active Directory group. No matter how you enumerate the group, exporting the results to a CSV or XML file is all the same, That’s the great thing about PowerShell: learn the basics once and apply it everywhere.