Get AD group members containing large membership; two different AD forests

To start I have been successfully using the following script to get all the members of the AD groups in a specific OU including those from other forests:

$Groups = Get-ADGroup -Filter * -SearchBase "OU=User Groups,OU=Admin-Groups,DC=Site,DC=Local,DC=forest1,DC=blah,DC=com" -server

$Results = foreach( $Group in $Groups ){

Get-ADGroupMember -Identity $Group | foreach {


        GroupName = $Group.Name

        Name = $_.Name




$Results | Export-Csv -Path “\\Server Reports\user-tmp.csv”

This works great until you get AD groups with hundreds of members which results in a timeout. So what I am trying to do is use "Get-ADGroup $Group - properties Member | Select -expandproperty Member" to get each group same as the script above which I don't know how to do that. I need to do this because the users are in a different AD forest.

The output that I get when I enter one group is this:

I need to extract the SID in order to do the lookup but I need to have the ability to do this for each line:
$user = CN=S-1-5-21-1234567890-987654321-912837465-123456,CN=ForeignSecurityPrincipals,DC=Site,DC=Local,DC=forest1,DC=blah,DC=com
$user = -split ',' | Select-Object -First 1
$user = ($user).Trim('CN=')
Get-ADUser -Identity $user -Server
So at the end I can now support doing large lookups for members of our AD groups and still have it output to the CSV as originally defined in my beginning script.

To summarize. I need to extract the membership from all the AD groups to a CSV when dealing with two different AD forests while supporting large AD groups because of the timeouts that I am receiving and other solutions online would work only if they are in the same AD forest. I am thinking that there might need to be a loop inside of a loop because each group and its members needs to be processed/identified before moving on to the next group. This might be able to be done in a variable I suppose I just don’t know how to connect all the dots.

So, you have two AD forest, with the AD trusts in place. Meaning as discussed here:

Active Directory Trusts

Any time you have this use case, the real deal is that it’s not about just the number of users in a given group. It’s also about adjacent groups and nested groups …
(those users may be in - thus the token bloat issues cause by it, historically in challenged AD environments)

In AD, there is a filter: 1.2.840.113556.1.4.1941. It is called “matching rule in chain” and can be used to quickly find nested memberships.

# Example use 
$DN = 'place DN here!'
Get-ADGroup -LDAPFilter "(member:1.2.840.113556.1.4.1941:=$($DN))"

… as well as if these forest are geo-graphically dispersed, then you have potential bandwidth concerns.

Any time you are bringing back large datasets, you need to look to paging / chunking tat data to get it all. AD will not allow you to bring back unlimited datasets by default. You have to explicitly tell it to.

Yet, for a first step, why not just do this… (of course, this is just dumping to the screen, but easy to alter to send to a file)

$Banner = '#'*40
'','' | 
    "`n* Processing forest root domain controller $($PSItem) *`n"
    Get-ADGroup -Filter '*' -Server $PSItem | 
        "`n*** Processing $($PSItem.Name) ***`n"
        (Get-ADGroupMember -Identity $PSItem.Name | 
        Where { $PSItem.objectClass -eq 'user' }).SamAccountName


I am a fairly basic powershell user so I might need some help understanding what you are presenting to me. The script that I provided in my post was from the interwebs and a suggested script to improve from what I was doing before. Now that script does not work due to very large membership of groups. There are hardly any groups within a group that I am concerned with.

Looking through your post I am not quite understanding where the $PSItem is being set that is referenced in your last script and how the $DN value is referenced.

$PSItem is a default variable in PowerShell.
You will also see it written this way… $_
All it means is use whatever is being piped in. In this case, it’s these…


If you loop them by themselves, you’ll see what that means,

'','' | 
ForEach-Object { $PSItem }

# Results

'','' | 
ForEach-Object { $_ }

# Results

or see more details here:

Difference Between $_ and $PSItem in Windows PowerShell

Since you say you are new, see also the below to assist you in you ramp up and use.

Resource list

Microsoft Virtual Academy

Microsoft Channe9

' '

Windows PowerShell Survival Guide


Books Learn Windows PowerShell in a Month of Lunches

Windows PowerShell in Action, Third Edition

Windows PowerShell Cookbook, 2nd Edition

And start with lots of examples.
' '

Always use the help system directly as your primary starting point.

# All Help topics and locations
Get-Help about_*
Get-Help about_Functions

Get-Help about* | Select Name, Synopsis

Get-Help about* | 
Select-Object -Property Name, Synopsis |
Out-GridView -Title 'Select Topic' -OutputMode Multiple |
ForEach-Object { Get-Help -Name $_.Name -ShowWindow }

explorer "$pshome\$($Host.CurrentCulture.Name)"

# Get parameters, examples, full and Online help for a cmdlet or function

# Get a list of all functions
Get-Command -CommandType Function | 
Out-GridView -PassThru -Title 'Available functions'

# Get a list of all commandlets
Get-Command -CommandType Cmdlet | 
Out-GridView -PassThru -Title 'Available cmdlets'

# Get a list of all functions for the specified name
Get-Command -Name '*ADGroup*' -CommandType Function | 
Out-GridView -PassThru -Title 'Available named functions'

# Get a list of all commandlets for the specified name
Get-Command -Name '*ADGroup**'  -CommandType Cmdlet | 
Out-GridView -PassThru -Title 'Available named cmdlet'

# get function / cmdlet details
(Get-Command -Name Get-ADUser).Parameters
Get-help -Name Get-ADUser -Full
Get-help -Name Get-ADUser -Online
Get-help -Name Get-ADUser -Examples

Function Get-HelpExamples

        [string]$CmdletName = (
            Get-Command -Name '*' | 
            Out-GridView -PassThru -Title 'Select a cmdlet to see examples'

    If ((Get-Help -Name $CmdletName).Examples)
        (((Get-Help -Name $CmdletName).Examples | 
        Out-String -Stream) -match '.*\\>|C:\\PS>') -replace '.*\\>|C:\\PS>' | 
        Out-GridView -Title 'Select a sample to use' -PassThru
    Else {Write-Warning -Message "The were no help examples discovered"}