Public/Compare-CwSnapshot.ps1
|
function Compare-CwSnapshot { <# .SYNOPSIS Diffs two snapshots (or snapshot sets): new, removed and approaching certificate expiries. .DESCRIPTION Certificates are matched by host + thumbprint. 'Approaching' means the severity escalated between baseline and current run (OK->WARNING, WARNING->CRITICAL, ...) - exactly the certificates that moved closer to expiry in an actionable way since the last scan. .EXAMPLE $baseline = Get-CwSnapshot -Latest -Path .\snapshots -Skip 1 $current = Get-CwSnapshot -Latest -Path .\snapshots Compare-CwSnapshot -ReferenceSnapshot $baseline -DifferenceSnapshot $current #> [CmdletBinding()] [OutputType([pscustomobject])] param( [Parameter(Mandatory, Position = 0)] [AllowEmptyCollection()] [object[]]$ReferenceSnapshot, [Parameter(Mandatory, Position = 1, ValueFromPipeline)] [object[]]$DifferenceSnapshot ) begin { $diffSnaps = [System.Collections.Generic.List[object]]::new() $sevRank = @{ OK = 0; WARNING = 1; CRITICAL = 2; UNKNOWN = 0 } function Get-CertMap([object[]]$Snapshots) { $map = @{} foreach ($snap in $Snapshots) { foreach ($cert in @($snap.Certificates)) { $map["$($snap.Host)|$($cert.Thumbprint)"] = [pscustomobject]@{ Host = $snap.Host Tenant = if ($snap.PSObject.Properties['Tenant']) { $snap.Tenant } else { $null } Subject = $cert.Subject Role = $cert.Role Thumbprint = $cert.Thumbprint NotAfter = $cert.NotAfter DaysRemaining = $cert.DaysRemaining Severity = $cert.Severity } } } $map } } process { foreach ($snap in $DifferenceSnapshot) { $diffSnaps.Add($snap) } } end { $refMap = Get-CertMap -Snapshots $ReferenceSnapshot $diffMap = Get-CertMap -Snapshots $diffSnaps.ToArray() $new = foreach ($key in $diffMap.Keys) { if (-not $refMap.ContainsKey($key)) { $diffMap[$key] } } $removed = foreach ($key in $refMap.Keys) { if (-not $diffMap.ContainsKey($key)) { $refMap[$key] } } $approaching = foreach ($key in $diffMap.Keys) { if ($refMap.ContainsKey($key)) { $before = $refMap[$key]; $after = $diffMap[$key] if ($sevRank[[string]$after.Severity] -gt $sevRank[[string]$before.Severity]) { $after | Select-Object *, @{ N = 'PreviousSeverity'; E = { $before.Severity } } } } } [pscustomobject]@{ PSTypeName = 'CertWatchtower.Trend' NewCertificates = @($new | Sort-Object DaysRemaining) RemovedCertificates = @($removed | Sort-Object Host, Subject) Approaching = @($approaching | Sort-Object DaysRemaining) Summary = [pscustomobject]@{ New = @($new).Count Removed = @($removed).Count Approaching = @($approaching).Count } } } } |