Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- [CmdletBinding()]Param(
- [Parameter( Position = 0,
- Mandatory = $true
- )]
- [String]$Identity
- )
- function QueryAD {
- [CmdletBinding()]Param (
- [Parameter(Mandatory = $false, Position = 0)]
- [ValidateSet('GC','LDAP')]
- [string]$queryType = 'GC',
- [Parameter(Mandatory = $true, Position = 1)]
- [string]$ComputerName,
- [Parameter(Mandatory = $true, Position = 2)]
- [string]$Filter,
- [Parameter(Mandatory = $false, Position = 3)]
- [string]$SearchBase,
- [Parameter(Mandatory = $false, Position = 4)]
- [string[]]$Properties
- )
- $queryString = '{0}://{1}' -f $queryType,$ComputerName
- if($SearchBase){
- $queryString = $queryString + '/' + $SearchBase
- }
- $queryRoot = [ADSI]"$queryString"
- $searcher = new-object System.DirectoryServices.DirectorySearcher($queryRoot)
- $searcher.Filter = $Filter
- if($Properties){
- $searcher.PropertiesToLoad.AddRange($Properties)
- }
- $searchArrayList = New-Object System.Collections.ArrayList
- $searcher.FindAll() | foreach { [void]$searchArrayList.Add($_) }
- Write-Output $searchArrayList
- }
- Function Get-GCList {
- [CmdletBinding()]Param(
- [Parameter( Position = 0,
- Mandatory = $true,
- ValueFromPipeline = $false,
- ValueFromPipelineByPropertyName = $false
- )]
- [String]$Forest,
- [Parameter( Position = 1,
- Mandatory = $false,
- ValueFromPipeline = $false,
- ValueFromPipelineByPropertyName = $false
- )]
- [String]$Site
- )
- # If Site is specified then we look for GC's in a specific site, otherwise we look forest wide
- if ($Site) {
- $lookup = nslookup -querytype=srv ('_ldap._tcp.{1}._sites.gc._msdcs.{0}.' -f $Forest, $Site)
- }
- else {
- $lookup = nslookup -querytype=srv ('_gc._tcp.{0}.' -f $Forest)
- }
- $gcList = New-Object System.Collections.ArrayList
- # Go through each result and pull out the host name and add to the array list.
- $lookup | where { $_ -match 'svr hostname' } | foreach { $_ -match '\w+\.\w+\.\w+\.\w+' | out-null; [void]$gcList.Add($matches[0].ToLower()) }
- Write-Verbose -Message ('Found {0} glocal catalogs.' -f $gcList.count)
- # Return the array list.
- Write-Output $gcList
- }
- Function GetMembers {
- [CmdletBinding()]Param(
- [Parameter( Position = 0,
- Mandatory = $true,
- ValueFromPipeline = $false,
- ValueFromPipelineByPropertyName = $false
- )]
- [String]$distinguishedName,
- [Parameter( Position = 1,
- Mandatory = $true,
- ValueFromPipeline = $false,
- ValueFromPipelineByPropertyName = $false
- )]
- [String]$GlobalCatalog
- )
- # Create an array to store members
- $memberList = New-Object System.Collections.ArrayList
- # Bind to the group and see if we need to page out the membership
- $listEntry = New-Object DirectoryServices.DirectoryEntry("GC://$GlobalCatalog/$distinguishedName")
- $listSearcher = New-Object DirectoryServices.DirectorySearcher($listEntry,'(objectClass=*)','member','Base')
- $listObject = $listSearcher.FindOne()
- # Check to see if we get a special return for the member property which tells us that we need
- # to page out the membership. If all we get back is just member than no paging is required, but
- # if we get back something like member;0-1499 in attition to the member property then we need
- # to page the membership.
- if ($listObject.Properties.PropertyNames -match '^member;'){
- # Define a starting point of 0 and init $done as $false so that we can set to $true
- # when we're done.
- $start = 0
- $done = $false
- while ($done -eq $false){
- # Set our page range for this query (starting at 0-999, the next time it would be 1000-1999, and so on).
- $end = $start + 999
- # Create a searcher object and then pull down the member;range property
- $directoryObject = New-Object DirectoryServices.DirectorySearcher($listEntry,'(objectClass=*)',"member;range=$start-$end",'Base')
- $getMembers = $directoryObject.FindOne()
- # Store the member property (for logging really..which is not used right now).
- # Then we pull out the members that were returned for that range and add them
- # to the array list.
- [string]$memberProperty = $getMembers.Properties.PropertyNames -match '^member.*/*$'
- $getMembers.Properties.$memberProperty | foreach { [void]$memberList.Add($_) }
- # Increment our starting point for the next batch.
- $start += 1000
- # When we get to the last batch Active Directory will return a special member property that looks like
- # this:
- #
- # member;range=6000-*
- #
- # The asterisk at the end signifies that there are no more results to page so we need
- # to stop. We look for that and when we hit that we set $done to $true
- if ($getMembers.Properties.PropertyNames -match '^member.*\*$'){
- $done = $true
- }
- }
- }
- else {
- # If we get back just member then we can drop the membership as it is.
- $listObject.Properties.member | foreach { [void]$memberList.Add($_) }
- }
- # Return the contents of memberList.
- Write-Output $memberList
- }
- #---------------------------------------
- #region Get a working global catalogs
- # Parse out the forest from where the script is being executed.
- $Forest = (([ADSI]$('LDAP://{0}/RootDSE' -f ($env:logonserver -replace '\\'))).ldapservicename -split ':')[0]
- # Fill in if you want GC's from a certain site.
- $dnsQuery = @{Forest=$Forest;Site=$Site}
- [array]$globalCatalogs = Get-GCList @dnsQuery
- if ($globalCatalogs.count -eq 0){
- Write-Warning -Message ('No global catalogs found!')
- return
- }
- # Randomize the array order
- $globalCatalogs = $globalCatalogs | Get-Random -Count $globalCatalogs.count
- # Find a working one.
- for($i=0; $i -lt $globalCatalogs.count;$i++){
- $rootDSEPath = 'GC://{0}/RootDSE' -f $globalCatalogs[$i]
- Write-Verbose -Message ('Trying {0}' -f $rootDSEPath)
- try {
- [ADSI]$rootDSEPath | Out-Null
- }
- catch {
- Write-Verbose -Message ('Unable to connect to {0}' -f $globalCatalogs[$i])
- continue
- }
- # If we got here then we found a working GC and we can stop.
- $gc = $globalCatalogs[$i]
- # Stop the loop since we have a working GC.
- break
- }
- #endregion Get a working global catalog
- #---------------------------------------
- #---------------------------------------
- #region Get the distinguished name of the group and write out the membership
- # Check to see if a distinguished name was passed. If so we use that, otherwise
- # we need to lookup the group name in the global catalog.
- if ($Identity -match '^CN='){
- $groupDN = $Identity
- }
- else {
- try {
- $filterString = '(&(objectClass=Group)(sAMAccountName={0}))' -f $Identity
- $groupDN = QueryAD -ComputerName $gc -Filter $filterString -Properties distinguishedName | foreach { [string]$_.Properties.distinguishedname }
- }
- catch {
- Write-Warning -Message ('Unable to get distinguishedName of {0}: {1}' -f $Identity,$_.exception.message)
- return
- }
- if ($groupDN.count -gt 1){
- Write-Warning -Message ('Too many matches for {0}' -f $Identity)
- $groupDN | foreach { Write-Warning -Message ('->{0}' -f $_.distinguishedname) }
- return
- }
- }
- GetMembers -GlobalCatalog $gc -distinguishedName $groupDN
- #endregion Get the distinguished name of the group and write out the membership
- #---------------------------------------
- <#
- .SYNOPSIS
- Return the membership of any list (small or large) from a global catalog.
- .DESCRIPTION
- Find a working global catalog and then search for the list provided. After it is found
- we return the membership. If the membership needs to be paged out we automatically determine
- that and then page that out and return it as an array list.
- .PARAMETER Identity
- The name of the list\group. This can be a friendly name (CN or sAMAccountName) or it can be a distinguishedName
- .EXAMPLE
- $listMembers = .\Get-ListMembers.ps1 -Identity companyWideList
- C:\PS>$listMembers.count
- 38897
- .NOTES
- This is free to use and expand on. Hope it was helpful to someone.
- #>
Advertisement
Add Comment
Please, Sign In to add comment