Private/Computers/Helpers.ps1
|
function Get-PSIComputerSchema { [ordered]@{ computers=@('Computers',@('Name','Computer ID','DNS hostname','Domain','OS reported','OS family','Server release','Version reported','Build reported','Service pack reported','Enabled','DC role reported','Location DN','DN','Managed by','Created','Changed','Password last set','Last logon timestamp (replicated)','User account control raw','Primary group ID')) osSummary=@('OS breakdown',@('Name','OS family','OS reported','Server release','Version reported','Build reported','Computers observed')) locations=@('Directory locations',@('Name','Location DN','Computers observed')) spns=@('Reported SPNs',@('Name','Computer ID','SPN')) delegation=@('Delegation attributes',@('Name','Computer ID','Trusted for delegation','Trusted to authenticate for delegation','Account not delegated','Allowed target SPN','RBCD descriptor present')) collection=@('Collection coverage',@('Name','Status','Record count','Method','Collected at','Duration ms','Error ID','Message')) } } function Get-PSIComputerQuerySchema {[ordered]@{computers=@('Directory computers');collection=@('Collection coverage')}} function Get-PSIComputerValue($Object,[string]$Property){if($null -ne $Object -and $null -ne $Object.PSObject.Properties[$Property]){return $Object.$Property};return $null} function Get-PSIComputerParentDN([string]$DN){$escaped=$false;for($i=0;$i -lt $DN.Length;$i++){if($escaped){$escaped=$false;continue};if($DN[$i] -eq '\'){$escaped=$true;continue};if($DN[$i] -eq ','){return $DN.Substring($i+1)}};return $null} function Get-PSIComputerOs([string]$OS,[string]$Version){ $family='Unknown';$release=$null;$build=$null if(-not [string]::IsNullOrWhiteSpace($OS)){ if($OS -match '(?i)\bWindows\s+Server\b'){$family='Windows Server';$m=[regex]::Match($OS,'(?i)\bWindows\s+Server\s+(2000|2003|2008|2012|2016|2019|2022|2025)(?:\s+(R2))?\b');if($m.Success){$release=$m.Groups[1].Value;if($m.Groups[2].Success){$release+=' R2'}}} elseif($OS -match '(?i)\bWindows\s+(?:XP|Vista|7|8(?:\.1)?|10|11|Embedded)\b'){$family='Windows client'}else{$family='Other / unclassified'} } if($Version -match '^\d+\.\d+\s+\((\d+)\)$'){$build=$Matches[1]}elseif($Version -match '^\d+\.\d+\.(\d+)(?:\.\d+)?$'){$build=$Matches[1]} [pscustomobject]@{Family=$family;Release=$release;Build=$build} } function ConvertTo-PSIComputerModel($Context,[object[]]$Envelopes){ if($Envelopes.Count -ne 1 -or $Envelopes[0].Key -ne 'computers' -or $Envelopes[0].Status -notin @('Succeeded','Failed','TimedOut')){throw 'Expected one valid computer query outcome.'} $e=$Envelopes[0];$schema=Get-PSIComputerSchema;$data=[ordered]@{};$coverage=[ordered]@{} foreach($k in $schema.Keys){$data[$k]=New-Object collections.ArrayList;$coverage[$k]=$e.Status -eq 'Succeeded'} $seen=@{};$osGroups=New-Object 'System.Collections.Generic.Dictionary[string,object]' -ArgumentList ([StringComparer]::Ordinal);$locationGroups=@{} if($e.Status -eq 'Succeeded'){ foreach($source in @($e.Records)){ $id=([guid]$source.Id).ToString();$dn=[string]$source.DN if($seen.ContainsKey($id) -or -not $dn.EndsWith(','+$Context.DomainDN,[StringComparison]::OrdinalIgnoreCase)){throw 'Duplicate or out-of-domain computer record.'};$seen[$id]=$true $os=Get-PSIComputerOs $source.OS $source.Version;$location=Get-PSIComputerParentDN $dn $uac=Get-PSIComputerValue $source 'Uac';$primary=Get-PSIComputerValue $source 'PrimaryGroup';$dcRole='Unknown' if($null -ne $uac){$dcRole=if(([long]$uac -band 8192) -ne 0 -or $primary -eq 521){'Domain controller'}else{'Other computer'}} [void]$data.computers.Add([pscustomobject]@{Name=$source.Name;'Computer ID'=$id;'DNS hostname'=$source.Dns;Domain=$Context.Domain;'OS reported'=$source.OS;'OS family'=$os.Family;'Server release'=$os.Release;'Version reported'=$source.Version;'Build reported'=$os.Build;'Service pack reported'=$source.ServicePack;Enabled=$source.Enabled;'DC role reported'=$dcRole;'Location DN'=$location;DN=$dn;'Managed by'=$source.ManagedBy;Created=$source.Created;Changed=$source.Changed;'Password last set'=$source.PasswordLastSet;'Last logon timestamp (replicated)'=$source.LastLogonTimestamp;'User account control raw'=$uac;'Primary group ID'=$primary}) foreach($spn in @($source.Spns|Where-Object{$_})){[void]$data.spns.Add([pscustomobject]@{Name=$source.Name;'Computer ID'=$id;SPN=[string]$spn})} $targets=@($source.AllowedTargets|Where-Object{$_});if(-not $targets.Count){$targets=@($null)} foreach($target in $targets){[void]$data.delegation.Add([pscustomobject]@{Name=$source.Name;'Computer ID'=$id;'Trusted for delegation'=$source.TrustedForDelegation;'Trusted to authenticate for delegation'=$source.TrustedToAuthForDelegation;'Account not delegated'=$source.AccountNotDelegated;'Allowed target SPN'=$target;'RBCD descriptor present'=$source.RbcdPresent})} $key=@($os.Family,$source.OS,$os.Release,$source.Version,$os.Build)|ConvertTo-Json -Compress if(-not $osGroups.ContainsKey($key)){$osGroups[$key]=[pscustomobject]@{Name=if($source.OS){$source.OS}else{'Unknown OS'};'OS family'=$os.Family;'OS reported'=$source.OS;'Server release'=$os.Release;'Version reported'=$source.Version;'Build reported'=$os.Build;'Computers observed'=0}} $osGroups[$key].'Computers observed'++ if(-not $locationGroups.ContainsKey($location)){$locationGroups[$location]=[pscustomobject]@{Name=$location;'Location DN'=$location;'Computers observed'=0}};$locationGroups[$location].'Computers observed'++ } } foreach($row in @($osGroups.Values|Sort-Object Name,'Version reported')){[void]$data.osSummary.Add($row)} foreach($row in @($locationGroups.Values|Sort-Object Name)){[void]$data.locations.Add($row)} [void]$data.collection.Add([pscustomobject]@{Name='Domain computer enumeration';Status=$e.Status;'Record count'=$data.computers.Count;Method='ADWS / pinned domain DC';'Collected at'=$e.CollectedAt;'Duration ms'=$e.DurationMs;'Error ID'=$e.ErrorId;Message=$e.ErrorMessage}) $coverage.collection=$true;$arrays=[ordered]@{};foreach($k in $schema.Keys){$arrays[$k]=@($data[$k].ToArray())} [pscustomobject]@{Schema=$schema;Data=$arrays;Coverage=$coverage;Raw=[ordered]@{}} } |