Private/Context.ps1
|
#Requires -Version 5.1 # Collection context: carries the notCollected list and the salt through a collection run, and turns # exceptions into typed notCollected records instead of crashes. Uncollectable fields are NEVER silently # absent (docs/08, contracts/*.schema.json notCollected). Set-StrictMode -Version Latest function New-DDContext { [CmdletBinding()] param( [string] $Salt = '', [string] $TenantSalt = '', [switch] $Anonymise, [string] $ComputerName = '' ) [pscustomobject]@{ Salt = $Salt TenantSalt = $(if ($TenantSalt) { $TenantSalt } else { $Salt }) Anonymise = [bool]$Anonymise ComputerName = $ComputerName NotCollected = (New-Object 'System.Collections.Generic.List[object]') } } function Get-DDNotCollectedReason { [CmdletBinding()] param([Parameter(Mandatory)] $ErrorRecord) $ex = $ErrorRecord.Exception $msg = [string]$ex.Message if ($ex -is [System.Management.Automation.CommandNotFoundException] -or $msg -match 'is not recognized as (the name of )?a cmdlet') { return 'cmdlet-missing' } if ($ex -is [System.UnauthorizedAccessException] -or $ex -is [System.Security.SecurityException] -or $msg -match '(?i)access (is )?denied|not authorized|requires elevation|administrator') { return 'insufficient-rights' } if ($ex -is [System.PlatformNotSupportedException] -or $ex -is [System.NotSupportedException] -or $msg -match '(?i)not supported|Windows only|invalid namespace|invalid class') { return 'unsupported-os' } if ($ex -is [System.TimeoutException] -or $ex -is [System.OperationCanceledException] -or $msg -match '(?i)time(d)? ?out') { return 'timeout' } 'error' } function Add-DDNotCollected { [CmdletBinding()] param( [Parameter(Mandatory)] $Context, [Parameter(Mandatory)][string] $Field, [Parameter(Mandatory)][ValidateSet('unsupported-os', 'insufficient-rights', 'cmdlet-missing', 'timeout', 'skipped-by-flag', 'error')][string] $Reason, [string] $Detail = '' ) foreach ($e in $Context.NotCollected) { if ($e['field'] -eq $Field) { return } } $rec = [ordered]@{ field = $Field; reason = $Reason } if ($Detail) { $d = Protect-DDLogText -Text $Detail $rec['detail'] = $d.Substring(0, [math]::Min(200, $d.Length)) } $Context.NotCollected.Add($rec) Write-DDVerboseStep -Step " not collected: $Field" -Result $Reason } function Invoke-DDStep { <# .SYNOPSIS Run one collection step. On failure record notCollected for -Field and return -Default. #> [CmdletBinding()] param( [Parameter(Mandatory)] $Context, [Parameter(Mandatory)][string] $Field, [Parameter(Mandatory)][scriptblock] $Script, $Default = $null ) try { & $Script } catch { Add-DDNotCollected -Context $Context -Field $Field -Reason (Get-DDNotCollectedReason -ErrorRecord $_) -Detail $_.Exception.Message $Default } } function Get-DDProp { # First non-null property among candidate names (strict-mode safe, case-insensitive). Works on objects and dictionaries. [CmdletBinding()] param($Object, [Parameter(Mandatory)][string[]] $Name) if ($null -eq $Object) { return $null } foreach ($n in $Name) { if ($Object -is [System.Collections.IDictionary]) { foreach ($k in $Object.Keys) { if ([string]$k -ieq $n -and $null -ne $Object[$k]) { return $Object[$k] } } } else { $p = $Object.PSObject.Properties[$n] if ($null -ne $p -and $null -ne $p.Value) { return $p.Value } } } $null } function Format-DDIso { [CmdletBinding()] param([Parameter(Mandatory)][datetime] $Value) $Value.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss'Z'", [System.Globalization.CultureInfo]::InvariantCulture) } |