Private/Trusts/Model.ps1
|
function Get-PSITrustSchema { [ordered]@{ domains=@('Observed domains',@('Name','Source DC','Query status','Observed TDO records','Collected at')) trusts=@('Trust observations',@('Name','Observed domain','Partner','Direction','Relationship flags','Protocol type reported','Selective authentication reported','Forest transitive reported','Within forest reported','Non-transitive flag','DN','Source DC','GUID','Provider source')) directions=@('Direction descriptions',@('Name','Observed domain','Partner','Direction','Raw direction','Meaning','DN')) settings=@('Trust settings',@('Name','Observed domain','Partner','Selective authentication reported','SIDFilteringForestAware reported','SIDFilteringQuarantined reported','TGT delegation reported','Uses AES keys reported','Uses RC4 encryption reported','Trust attributes raw','Supported encryption types raw','Treat as external flag','Quarantined domain flag','Cross-organization flag','Partner SID reported','Flat name','Created','Changed','DN')) partners=@('Observed partners',@('Name','Observed TDO records','Observed local domains','Observed directions')) collection=@('Collection ledger',@('Name','Target','Source DC','Status','Record count','Method','Collected at','Duration ms','Error ID','Message')) } } function ConvertTo-PSITrustModel($Context,[object[]]$Envelopes){ $schema=Get-PSITrustSchema;$data=[ordered]@{};foreach($k in $schema.Keys){$data[$k]=New-Object collections.ArrayList} $required=@($Context.Domains|ForEach-Object{'trust:'+$_});$queries=@{};$raw=@{};$partners=@{};$collected=0 foreach($e in $Envelopes){ if($e.Key -notin $required -or $queries.ContainsKey($e.Key)){throw 'Unknown or duplicate trust outcome.'} if($e.Status -notin @('Succeeded','Failed','TimedOut')){throw 'Invalid trust collection status.'} $queries[$e.Key]=$e;$raw[$e.Key]=@();if($e.Status -eq 'Succeeded'){$raw[$e.Key]=@($e.Records|Where-Object{$null -ne $_})} } function V($o,$p){Get-PSISiteValue $o $p} function Flag($value,[long]$mask){if($null -eq $value -or [string]::IsNullOrWhiteSpace([string]$value)){return $null};[long]$number=0;if(-not [long]::TryParse([string]$value,[ref]$number)){return $null};return ($number -band $mask) -ne 0} foreach($domainName in @($Context.Domains)){ $key='trust:'+$domainName;if(-not $queries.ContainsKey($key)){throw "Missing trust outcome: $key"};$e=$queries[$key] if($e.Status -eq 'Succeeded'){$collected++} [void]$data.domains.Add([pscustomobject][ordered]@{Name=$domainName;'Source DC'=$e.SourceDC;'Query status'=$e.Status;'Observed TDO records'=if($e.Status -eq 'Succeeded'){$raw[$key].Count}else{$null};'Collected at'=$e.CollectedAt}) [void]$data.collection.Add([pscustomobject][ordered]@{Name=$key;Target=$domainName;'Source DC'=$e.SourceDC;Status=$e.Status;'Record count'=if($e.Status -eq 'Succeeded'){$raw[$key].Count}else{$null};Method=$e.Method;'Collected at'=$e.CollectedAt;'Duration ms'=$e.DurationMs;'Error ID'=$e.ErrorId;Message=if($e.Status -eq 'Succeeded'){'Local domain TDO read completed; zero rows is a valid empty result.'}else{$e.ErrorMessage}}) foreach($r in $raw[$key]){ $partner=V $r 'trustPartner';if(-not $partner){$partner=V $r 'Target'} $label=$domainName+' / '+$(if($partner){[string]$partner}else{'Partner not returned'}) $directionRaw=V $r 'trustDirection';$direction='Unknown';$meaning='Direction was not returned or could not be interpreted.' if($null -ne $directionRaw){ switch([string]$directionRaw){ '0'{$direction='Disabled (0)';$meaning='No trust-direction bits are set on this observed object.'} '1'{$direction='Inbound';$meaning='Partner trusts the observed domain (configured direction only).'} '2'{$direction='Outbound';$meaning='Observed domain trusts the partner (configured direction only).'} '3'{$direction='Bidirectional';$meaning='Both directions are configured on this observed object.'} default{$direction='Unknown ('+[string]$directionRaw+')'} } }else{ switch([string](V $r 'Direction')){ 'Inbound'{$direction='Inbound';$meaning='Partner trusts the observed domain (provider-reported direction).'} 'Outbound'{$direction='Outbound';$meaning='Observed domain trusts the partner (provider-reported direction).'} 'Bidirectional'{$direction='Bidirectional';$meaning='Both directions are reported on this observed object.'} } } $attrs=V $r 'trustAttributes';$within=V $r 'IntraForest';$forest=V $r 'ForestTransitive' $relationship=if($within -eq $true){'Within-forest flag reported'}elseif($forest -eq $true){'Forest-transitive flag reported'}elseif($null -ne $attrs){'See raw attributes and protocol type'}else{'Not returned'} [void]$data.trusts.Add([pscustomobject][ordered]@{Name=$label;'Observed domain'=$domainName;Partner=$partner;Direction=$direction;'Relationship flags'=$relationship;'Protocol type reported'=V $r 'NativeProtocolType';'Selective authentication reported'=V $r 'SelectiveAuthentication';'Forest transitive reported'=$forest;'Within forest reported'=$within;'Non-transitive flag'=Flag $attrs 1;DN=V $r 'DistinguishedName';'Source DC'=$e.SourceDC;GUID=V $r 'ObjectGUID';'Provider source'=V $r 'Source'}) [void]$data.directions.Add([pscustomobject][ordered]@{Name=$label;'Observed domain'=$domainName;Partner=$partner;Direction=$direction;'Raw direction'=$directionRaw;Meaning=$meaning;DN=V $r 'DistinguishedName'}) [void]$data.settings.Add([pscustomobject][ordered]@{Name=$label;'Observed domain'=$domainName;Partner=$partner;'Selective authentication reported'=V $r 'SelectiveAuthentication';'SIDFilteringForestAware reported'=V $r 'SIDFilteringForestAware';'SIDFilteringQuarantined reported'=V $r 'SIDFilteringQuarantined';'TGT delegation reported'=V $r 'TGTDelegation';'Uses AES keys reported'=V $r 'UsesAESKeys';'Uses RC4 encryption reported'=V $r 'UsesRC4Encryption';'Trust attributes raw'=$attrs;'Supported encryption types raw'=V $r 'msDS-SupportedEncryptionTypes';'Treat as external flag'=Flag $attrs 64;'Quarantined domain flag'=Flag $attrs 4;'Cross-organization flag'=Flag $attrs 16;'Partner SID reported'=V $r 'SID';'Flat name'=V $r 'flatName';Created=V $r 'whenCreated';Changed=V $r 'whenChanged';DN=V $r 'DistinguishedName'}) if($partner){$pkey=([string]$partner).ToLowerInvariant();if(-not $partners.ContainsKey($pkey)){$partners[$pkey]=New-Object collections.ArrayList};[void]$partners[$pkey].Add([pscustomobject]@{Partner=$partner;Domain=$domainName;Direction=$direction})} } } foreach($key in @($partners.Keys|Sort-Object)){$rows=@($partners[$key].ToArray());[void]$data.partners.Add([pscustomobject][ordered]@{Name=$rows[0].Partner;'Observed TDO records'=$rows.Count;'Observed local domains'=(@($rows.Domain|Sort-Object -Unique) -join '; ');'Observed directions'=(@($rows.Direction|Sort-Object -Unique) -join '; ')})} $complete=$collected -eq @($Context.Domains).Count;$coverage=[ordered]@{domains=$true;trusts=$complete;directions=$complete;settings=$complete;partners=$complete;collection=$true} $plain=[ordered]@{};foreach($k in $data.Keys){$plain[$k]=@($data[$k].ToArray())} [pscustomobject]@{Schema=$schema;Data=$plain;Raw=$raw;Coverage=$coverage;DomainsCollected=$collected} } |