Skip to content

Instantly share code, notes, and snippets.

@maravedi
Last active July 29, 2020 00:18
Show Gist options
  • Save maravedi/52734cd74f56ea2a821e64db793d797c to your computer and use it in GitHub Desktop.
Save maravedi/52734cd74f56ea2a821e64db793d797c to your computer and use it in GitHub Desktop.
A PowerShell script to recurse an AD group to find users
<#
.SYNOPSIS
This script will iterate through the nested levels of an AD group to find sub-groups until it gets to a list of actual users.
.DESCRIPTION
The script requires the exact and full name of the AD group. Future editions could include a menu-driven command line interface to allow users to input a wildcard string for the name and then select from the results of that query.
.PARAMETER PrimaryGroupName
This is the name of the top-level group. The script will determine if there are any nested groups under this group and return the members of the first group with users as the members.
.PARAMETER FindUser
This parameter is not mandatory. The parameter expects a users' AD username. The script will query AD for the user's full name and then check the group member list to see if that user is a member. If you also would like the full member list returned, make sure to use the -ListAllMembers switch.
.PARAMETER MaxIterations
Since there's not really a way to control the iteration based solely on the group properties, we need to define a maximum number of iterations. The script will break out of the loop if it reaches a member list with users.
.PARAMETER ListAllMembers
This parameter is a switch, so no value is expected. If it is used in combination with the -FindUser parameter, it will return the members that were found in the subgroup. Otherwise, the -FindUser parameter will not return the full list.
.EXAMPLE
.\Get-NestedADGroupMembers.ps1 -PrimaryGroupName 'Domain Users' -MaxIterations = 2
.\Get-NestedADGroupMembers.ps1 -PrimaryGroupName 'Domain Users'
.\Get-NestedADGroupMembers.ps1 -PrimaryGroupName 'Domain Users' -FindUser 'demouser'
$MemberList = .\Get-NestedADGroupMembers.ps1 -PrimaryGroupName 'Domain Users' -FindUser 'demouser' -ListAllMembers
.\Get-NestedADGroupMembers.ps1 -PrimaryGroupName 'Domain Users' | ConvertTo-CSV -NoTypeInformation | Out-File Output.csv
.NOTES
Author: David Frazer
Date: 09/25/2017
Updates:
[09/25/2017]: Added two additional parameters to allow the user to specify a user to find in the list and also to control whether the full list is returned when a search is performed for a user.
#>
Param(
[Parameter(Mandatory=$True,Position=0)]
$PrimaryGroupName,
[Parameter(Position=1)]
$FindUser,
[Parameter(Position=2)]
$MaxIterations = 10,
[Parameter(Position=3)]
[Switch]$ListAllMembers
)
$OldErrorActionPreference = $ErrorActionPreference
# If any of the checks below fail, we want to script to stop execution
$ErrorActionPreference = 'Stop'
Try {
$ImportADModule = Import-Module ActiveDirectory
}
Catch {
Write-Host "You must have Remote Server Administration Tools (RSAT) installed to run this script."
Exit
}
Try {
$TestUserInput = (Get-ADGroup -Identity $PrimaryGroupName).Name
}
Catch {
Write-Host "That is not a valid AD group name"
}
# From this point on, we want the script to run with the users' erroractionpreference
$ErrorActionPreference = $OldErrorActionPreference
# This will hold the resulting subgroups with their respective members.
[System.Collections.ArrayList]$SubGroupList = @()
For($Level = 0; $Level -lt $MaxIterations; $Level++){
$Group = Get-ADGroup -Identity $PrimaryGroupName -Property Member
# The first element should be a container, and none of the elements in this list should have the Users container in it.
If($Group.Member[0] -like "CN*" -And (($Group.Member | Foreach-Object { $_ -like "CN*,CN=Users*"}) -notcontains $True )){
$SubGroup = "" | Select Name, Member
# Got the regex below from this article: http://mikefrobbins.com/2014/07/10/extract-the-name-from-an-active-directory-distinguished-name-with-powershell-and-a-regular-expression/
$SubGroup.Name = "$($Group.Name)"
$SubGroup.Member = $Group.Member -Replace '^CN=|,.*$'
[Void]$SubGroupList.Add($SubGroup)
$PrimaryGroupName = [string]$SubGroup.Member
} ElseIf(($Group.Member | Foreach-Object { $_ -like "CN*,CN=Users*"}) -contains $True) {
# There is an element in the result that has the Users container in it. So we will get the members of this particular group, add them to the Subgroup hash and then stop the loop.
$SubGroup = "" | Select Name, Member
$SubGroup.Name = $Group.Name
# Got the regex below from this article: http://mikefrobbins.com/2014/07/10/extract-the-name-from-an-active-directory-distinguished-name-with-powershell-and-a-regular-expression/
$SubGroup.Member = $Group.Member -Replace '^CN=|,.*$'
[Void]$SubGroupList.Add($SubGroup)
break
}
Else {
# Either the first member of this group is not a container or there are no users in the member list. Either way, we'll exit the loop.
break
}
}
If($SubgroupList.Count -gt 0) {
Write-Host "AD Group hierarchy"
Write-Host "$($SubGroupList.Name -Join ' => ')"
If($FindUser){
# This assumes the user is inputting the username, not the full name of the user.
# We look-up the username in AD to get the users' full name since the member list is the full names of users
$UserFullName = (Get-ADUser -Filter {SamAccountName -eq $FindUser}).Name
If($SubGroupList[-1].Member -contains $UserFullName){
Write-Host "$($FindUser) is a member of the subgroup $($SubGroupList[-1].Name) and is contained within the $($PrimaryGroupName) AD group"
} Else {
Write-Host "$($FindUser) is not a member of the subgroup $($SubGroupList[-1].Name)."
}
# If the user passed through the -ListAllMembers switch, then we'll also output all the returned members, regardless of a match for the -FindUser parameter
If($ListAllMembers){
Return $SubGroupList[-1].Member
}
} Else {
# The very last row in the SubGroup hash will hold the users, so we're returning only that.
Return $SubGroupList[-1].Member
}
}
Else {
Write-Host "That group has no members"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment