Skip to content

Instantly share code, notes, and snippets.

@IMJLA
Created March 3, 2021 06:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IMJLA/3e5fa452af8948e5d97e91207a08baba to your computer and use it in GitHub Desktop.
Save IMJLA/3e5fa452af8948e5d97e91207a08baba to your computer and use it in GitHub Desktop.
param (
# Name of the group whose members to retrieve
[string]$GroupName
)
function Search-Directory {
# Uses the .NET System.DirectoryServices Namespace to perform an LDAP search
param (
# LDAP Path to the node to search
[string]$DirectoryPath = (([adsisearcher]'').SearchRoot.Path),
# LDAP filter
[string]$Filter,
# LDAP result page size for large numbers of results
[int]$PageSize = 1000,
# LDAP attributes to return for the objects found in the search
[string[]]$PropertiesToLoad,
# Credentials to use to perform the LDAP bind
[pscredential]$Credential,
# LDAP search scope (Base, OneLevel, or Subtree [default])
[string]$SearchScope = 'Subtree'
)
if ($Credential) {
$DirectoryEntry = [System.DirectoryServices.DirectoryEntry]::new($DirectoryPath,$($Credential.UserName),$($Credential.GetNetworkCredential().password))
}
else {
$DirectoryEntry = [System.DirectoryServices.DirectoryEntry]::new($DirectoryPath)
}
$DirectorySearcher = [System.DirectoryServices.DirectorySearcher]::new($DirectoryEntry)
if ($Filter) {
$DirectorySearcher.Filter = $Filter
}
$DirectorySearcher.PageSize = $PageSize
$DirectorySearcher.SearchScope = $SearchScope
ForEach ($Property in $PropertiesToLoad) {
$null = $DirectorySearcher.PropertiesToLoad.Add($Property)
}
$SearchResultCollection = $DirectorySearcher.FindAll()
$null = $DirectorySearcher.Dispose()
$null = $DirectoryEntry.Dispose()
$Output = [System.DirectoryServices.SearchResult[]]::new($SearchResultCollection.Count)
$SearchResultCollection.CopyTo($Output,0)
$null = $SearchResultCollection.Dispose()
Write-Output $Output
}
function Get-DirectoryGroupMember {
# Uses the .NET System.DirectoryServices Namespace to perform an LDAP search to recursively find all members of a group
param (
# Groups (returned from an LDAP search) whose members need to be retrieved
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[System.DirectoryServices.SearchResult[]]$Group
)
begin{
<#
1.2.840.113556.1.4.1941 is the OID for LDAP_MATCHING_RULE_IN_CHAIN aka LDAP_MATCHING_RULE_TRANSITIVE_EVAL
LDAP_MATCHING_RULE_IN_CHAIN is a special Extensible Match operator that walks the chain of ancestry in objects all the way to the root until it finds a match.
This reveals group nesting.
LDAP_MATCHING_RULE_IN_CHAIN will only work when used with Distinguished Name (DN) type attributes.
LDAP_MATCHING_RULE_IN_CHAIN is available only on domain controllers with Windows Server 2003 R2 (or above).
LDAP_MATCHING_RULE_TRANSITIVE_EVAL may be the new name as it is in current MS documentation and supports Windows Server 2008 (or above) domain controllers
#>
$OID = '1.2.840.113556.1.4.1941'
}
process{
foreach ($ThisGroup in $Group) {
$GroupMemberSearch = Search-Directory -Filter "(memberof:$($OID):=$($ThisGroup.Properties['distinguishedname']))" -PropertiesToLoad @('operatingSystem','objectSid','samAccountName')
if ($GroupMemberSearch.Count -gt 0) {
$CurrentADGroupMembers = $GroupMemberSearch.GetDirectoryEntry()
}
Write-Debug "$($ThisGroup.Properties.name) has $(($CurrentADGroupMembers | Measure-Object).Count) members"
$ThisGroup |
Add-Member -MemberType NoteProperty -Name FullMembers -Value $CurrentADGroupMembers -Force -PassThru
}
}
end{}
}
$SearchParams = @{
Filter = "(&(objectClass=group)(cn=$GroupName))"
PropertiesToLoad = @('objectClass','distinguishedName','name','grouptype','description','managedby','member','objectClass')
}
Search-Directory @SearchParams |
Get-DirectoryGroupMember
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment