Skip to content

Instantly share code, notes, and snippets.

@Bill-Stewart
Created July 16, 2021 18:23
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save Bill-Stewart/1e6fa7f1136be0c761b949abf4836fa3 to your computer and use it in GitHub Desktop.
Save Bill-Stewart/1e6fa7f1136be0c761b949abf4836fa3 to your computer and use it in GitHub Desktop.
# Get-ADGroupMember.ps1
# Written by Bill Stewart
#requires -version 2
# Version history:
# 1.0 (2019-12-04)
# * Initial version. Only searches the current domain.
#
# 1.1 (2021-07-16)
# * Substantial speed improvement by reducing Write-Progress calls.
<#
.SYNOPSIS
Gets the distinguished names of the Active Directory objects that are members of a specified group.
.DESCRIPTION
Gets the distinguished names of the Active Directory objects that are members of a specified group via the group's member attribute.
.PARAMETER Identity
Specifies an Active Directory group object. You can specify either the distinguishedName or the sAMAccountName of the object.
.PARAMETER Recursive
Specifies to include nested group memberships. With this parameter, intermediate groups are not included in the output.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true,ValueFromPipeline = $true)]
[String[]] $Identity,
[Switch] $Recursive
)
begin {
$CommandName = $MyInvocation.MyCommand.Name
# Set up Pathname COM object
$ADS_ESCAPEDMODE_ON = 2
$ADS_SETTYPE_DN = 4
$ADS_FORMAT_X500_DN = 7
$Pathname = New-Object -ComObject "Pathname"
if ( -not $Pathname ) {
return
}
[Void] $Pathname.GetType().InvokeMember("EscapedMode","SetProperty",$null,$Pathname,$ADS_ESCAPEDMODE_ON)
# Outputs correctly escaped distinguished name using Pathname object
function Get-EscapedName {
param(
[String] $distinguishedName
)
[Void] $Pathname.GetType().InvokeMember("Set","InvokeMethod",$null,$Pathname,@($distinguishedName,$ADS_SETTYPE_DN))
$Pathname.GetType().InvokeMember("Retrieve","InvokeMethod",$null,$Pathname,$ADS_FORMAT_X500_DN)
}
# Outputs the member attribute of an object using paged search
function Get-MemberAttribute {
param(
[String] $distinguishedName,
[Ref] $membership,
[Switch] $recursive
)
$searcher = [ADSISearcher] "(objectClass=*)"
$searcher.SearchRoot = [ADSI] "LDAP://$(Get-EscapedName $distinguishedName)"
$lastQuery = $false
$rangeStep = 1500
$rangeLow = 0
$rangeHigh = $rangeLow + ($rangeStep - 1)
$count = 0
do {
if ( -not $lastQuery ) {
$property = "member;range={0}-{1}" -f $rangeLow,$rangeHigh
}
else {
$property = "member;range={0}-*" -f $rangeLow
}
$searcher.PropertiesToLoad.Clear()
[Void] $searcher.PropertiesToLoad.Add($property)
$searchResults = $searcher.FindOne()
if ( $searchResults.Properties.Contains($property) ) {
foreach ( $searchResult in $searchResults.Properties[$property] ) {
if ( ($count -gt 0) -and (($count % $rangeStep) -eq 0) ) {
Write-Progress `
-Activity $CommandName `
-Status "Getting members of '$distinguishedName'" `
-CurrentOperation ("Count: {0:N0}" -f $count)
}
$memberDirEntry = [ADSI] "LDAP://$(Get-EscapedName $searchResult)"
if ( $recursive ) {
if ( $memberDirEntry.Properties["objectclass"][$memberDirEntry.Properties["objectclass"].Count - 1] -eq "group" ) {
Get-MemberAttribute $searchResult $membership -recursive
}
}
if ( -not $membership.Value.Contains($searchResult) ) {
if ( $recursive ) {
if ( $memberDirEntry.Properties["objectclass"][$memberDirEntry.Properties["objectclass"].Count - 1] -ne "group" ) {
$membership.Value.Add($searchResult)
$count++
}
}
else {
$membership.Value.Add($searchResult)
$count++
}
}
}
$done = $lastQuery
}
else {
if ( -not $lastQuery ) {
$lastQuery = $true
}
else {
$done = $true
}
}
if ( -not $lastQuery ) {
$rangeLow = $rangeHigh + 1
$rangeHigh = $rangeLow + ($rangeStep - 1)
}
}
until ( $done )
Write-Progress `
-Activity $CommandName `
-Status "Getting members of '$distinguishedName'" `
-Completed:$true
}
function Get-ADGroupMember {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[String] $identity,
[Switch] $recursive
)
$ldapString = $identity -replace '\\','\5c' -replace '\(','\28' -replace '\)','\29' -replace '\*','\2a' -replace '\/','\2f'
$searcher = [ADSISearcher] "(&(objectClass=group)(|(distinguishedName=$ldapString)(sAMAccountName=$ldapString)))"
try {
$searchResults = $searcher.FindAll()
if ( $searchResults.Count -gt 0 ) {
foreach ( $searchResult in $searchResults ) {
$Membership = New-Object Collections.Generic.List[String]
Get-MemberAttribute $searchResult.Properties["distinguishedname"][0] ([Ref] $Membership) -recursive:$recursive
$Membership
}
}
else {
Write-Error "Cannot find a group with identity '$identity'." -Category ObjectNotFound
}
}
catch {
Write-Error -ErrorRecord $_
}
finally {
$searchResults.Dispose()
}
}
}
process {
foreach ( $IdentityItem in $Identity ) {
Get-ADGroupMember $IdentityItem -recursive:$Recursive
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment