src/Private/Get-AdForestAccountSignals.ps1
|
function Get-AdForestAccountSignals { <# .SYNOPSIS Collects AD account signals across every domain in the forest, not just the current one. .DESCRIPTION A single domain's subtree search only covers that domain's directory partition, so in a multi-domain forest the accounts in other child/sibling domains are missed. This discovers every domain (Get-AdForestDomain) and runs the per-domain collector (Get-AdAccountSignals) against each, then returns the union. Resilient by design: a domain that can't be reached or read is logged with Write-Warning and skipped - one unreachable child domain must not sink the whole scan (the total just excludes that domain, an under-count, never a false positive). Each domain's account tally is written to the verbose stream for transparency. No cross-domain de-duplication is needed: every account is a distinct objectGUID in its own domain partition. Each emitted account carries the Domain it came from (stamped by Get-AdAccountSignals), so the report and CSV can show origin. .OUTPUTS PSCustomObject[] - the same shape Get-AdAccountSignals emits, aggregated across domains. #> [CmdletBinding()] param( # Optional discovery anchor (a DC/GC host or forest-root DNS). Defaults to the local forest. [string] $Server, [int] $PageSize = 1000 ) # Pre-declared so the loop and return are strict-mode safe even if discovery yields nothing. $all = New-Object System.Collections.Generic.List[object] $domains = @(Get-AdForestDomain -Server $Server) Write-Verbose ("Forest discovery found {0} domain(s): {1}" -f $domains.Count, ($domains -join ', ')) foreach ($domain in $domains) { try { $accounts = @(Get-AdAccountSignals -Server $domain -PageSize $PageSize) Write-Verbose (" {0}: {1} account(s)" -f $domain, $accounts.Count) foreach ($a in $accounts) { $all.Add($a) } } catch { Write-Warning ("Could not scan domain '{0}': {1}. Its accounts are omitted from this run." -f $domain, $_.Exception.Message) } } return $all.ToArray() } |