Public/Get-NSPADUser.ps1
|
function Get-NSPADUser { <# .SYNOPSIS Reads typed user identities through paged LDAP without RSAT or AD Web Services. .DESCRIPTION Identity is an exact sAMAccountName. Filter is an assertion value searched across sAMAccountName, UPN, displayName, mail and proxyAddresses; only asterisks are wildcards. All enumerates users. Every mode uses paging (500 by default). DomainInfo pins a previously selected domain/DC. Server discovers a domain; when both are given, the server must belong to that domain. SearchBase must be within that domain. A domain search does not follow referrals. Forest is a separate scope, requires a seed Server and cannot take DomainInfo or SearchBase. It discovers active domain naming contexts in that forest and reads each over LDAP using its own DC. Identical sAMAccountNames in different domains are returned separately. Directory errors terminate; discard partial output after a failed inventory. Visibility depends on directory permissions, including access to all domain crossRefs. ConsistencyGuidBytes contains raw bytes or null when absent/unreadable; null does not establish a source-anchor strategy. No directory writes or account changes occur. .PARAMETER Identity Exact sAMAccountName, with LDAP metacharacters treated literally. .PARAMETER Filter Wildcard assertion value, not a raw LDAP filter. Only * is a wildcard. .PARAMETER All Read all user objects in the chosen scope. .PARAMETER DomainInfo Domain and DC returned by Get-NSPADDomainInfo. .PARAMETER SearchBase Domain DN or an OU/container DN within that domain. .PARAMETER Server Domain server, or required forest seed DC. Global-catalog ports are not supported. .PARAMETER Forest Explicitly enumerate every active domain in the seed DC's forest. .PARAMETER PageSize LDAP page size, from 1 to 1000; defaults to 500. Does not limit the total result count. .EXAMPLE Get-NSPADUser -Identity 'one' -DomainInfo (Get-NSPADDomainInfo -Server 'dc01.contoso.example') .EXAMPLE Get-NSPADUser -Filter '*example*' -Server 'dc01.contoso.example' .EXAMPLE Get-NSPADUser -All -SearchBase 'OU=People,DC=contoso,DC=example' -Server 'dc01.contoso.example' .EXAMPLE Get-NSPADUser -All -Forest -Server 'dc01.contoso.example' #> [CmdletBinding(DefaultParameterSetName = 'Identity')] [OutputType('NSP.ActiveDirectory.User')] param( [Parameter(Mandatory, ParameterSetName = 'Identity')] [Parameter(Mandatory, ParameterSetName = 'ForestIdentity')] [ValidateNotNullOrEmpty()][string]$Identity, [Parameter(Mandatory, ParameterSetName = 'Filter')] [Parameter(Mandatory, ParameterSetName = 'ForestFilter')] [ValidateNotNullOrEmpty()][string]$Filter, [Parameter(Mandatory, ParameterSetName = 'All')] [Parameter(Mandatory, ParameterSetName = 'ForestAll')] [switch]$All, [Parameter(ParameterSetName = 'Identity')] [Parameter(ParameterSetName = 'Filter')] [Parameter(ParameterSetName = 'All')] [PSTypeName('NSP.ActiveDirectory.DomainInfo')]$DomainInfo, [Parameter(ParameterSetName = 'Identity')] [Parameter(ParameterSetName = 'Filter')] [Parameter(ParameterSetName = 'All')] [ValidateNotNullOrEmpty()][string]$SearchBase, [Parameter(ParameterSetName = 'Identity')] [Parameter(ParameterSetName = 'Filter')] [Parameter(ParameterSetName = 'All')] [Parameter(Mandatory, ParameterSetName = 'ForestIdentity')] [Parameter(Mandatory, ParameterSetName = 'ForestFilter')] [Parameter(Mandatory, ParameterSetName = 'ForestAll')] [ValidateNotNullOrEmpty()][string]$Server, [Parameter(Mandatory, ParameterSetName = 'ForestIdentity')] [Parameter(Mandatory, ParameterSetName = 'ForestFilter')] [Parameter(Mandatory, ParameterSetName = 'ForestAll')] [switch]$Forest, [ValidateRange(1, 1000)][int]$PageSize = 500 ) $ErrorActionPreference = 'Stop' if ($PSBoundParameters.ContainsKey('All') -and -not $All) { throw '-All must be true.' } if ($PSBoundParameters.ContainsKey('Forest') -and -not $Forest) { throw '-Forest must be true.' } if ($Server -match '[:/\\]') { throw 'Server must be a DC or domain DNS name, without a protocol or port.' } $query = '(&(objectCategory=person)(objectClass=user)' if ($PSBoundParameters.ContainsKey('Identity')) { $value = ConvertTo-NSPADLdapFilterValue -Value $Identity $query += "(sAMAccountName=$value)" } elseif ($PSBoundParameters.ContainsKey('Filter')) { $value = ConvertTo-NSPADLdapFilterValue -Value $Filter -AllowWildcard $query += '(|' + ((@('sAMAccountName','userPrincipalName','displayName','mail','proxyAddresses') | ForEach-Object { "($_=$value)" }) -join '') + ')' } $query += ')' $domains = if ($Forest) { @(Get-NSPADForestDomainInfo -Server $Server -PageSize $PageSize) } else { if ($Server -or -not $DomainInfo) { $selected = Get-NSPADDomainInfo -Server $Server if ($DomainInfo -and ($DomainInfo.DistinguishedName -ne $selected.DistinguishedName -or $DomainInfo.DNSRoot -ne $selected.DNSRoot)) { throw 'Server resolves a different domain than DomainInfo.' } $DomainInfo = $selected } @($DomainInfo) } foreach ($domain in $domains) { if (-not $domain.Server -or -not $domain.DistinguishedName -or -not $domain.DNSRoot) { throw 'DomainInfo must contain Server, DistinguishedName and DNSRoot.' } $base = if ($SearchBase) { $SearchBase } else { [string]$domain.DistinguishedName } $domainDn = [string]$domain.DistinguishedName if ($base -ne $domainDn -and -not $base.EndsWith(",$domainDn", [StringComparison]::OrdinalIgnoreCase)) { throw 'SearchBase must be within the selected domain naming context.' } $entries = @(Read-NSPADLdap -Server $domain.Server -SearchBase $base -Filter $query -PageSize $PageSize -Property distinguishedName,sAMAccountName,userPrincipalName,displayName,givenName,sn,mail,proxyAddresses,objectGUID,mS-DS-ConsistencyGuid,objectSid,userAccountControl -MultiValued proxyAddresses) if ($PSBoundParameters.ContainsKey('Identity') -and $entries.Count -gt 1) { throw 'LDAP returned multiple users for an exact identity in one domain.' } foreach ($entry in $entries) { ConvertTo-NSPADUser -Entry $entry -DomainInfo $domain } } } |