Public/Test-HDTRunStateAbandoned.ps1
|
function Test-HDTRunStateAbandoned { <# .SYNOPSIS Reports whether a state document describes a run that is over or dead. .DESCRIPTION The boot reconcile: Start-HDTResume.ps1 reconciles on every boot: if the state document says the run is finished, failed, or missing, it clears autologon, the LSA secret, the RunOnce entry and C:\HDT\state.json before doing anything else." This is the question that reconcile asks. Abandoned when: status is Succeeded or Failed the run is over status is Running but updatedUtc is older than -MaxAgeHour the run died between legs updatedUtc is missing or unreadable a document whose timestamp cannot be read is not evidence that a run is alive The stale case is the one that matters. MDT's teardown is a task sequence step, so a failure before it leaves autologon armed forever; HDT's reconcile plus this check is the second of three backstops, the others being the finally-block teardown and AutoLogonCount. The time comes from the injected IClock, never from the wall clock, so a thirteen-hour-old deployment is provable in a unit test. .PARAMETER State An Import-HDTRunState or New-HDTRunState result. .PARAMETER Clock An IClock supplying "now". .PARAMETER MaxAgeHour How long a Running document may go without a save before it is taken for dead. Defaults to 12. .OUTPUTS System.Boolean .EXAMPLE if (Test-HDTRunStateAbandoned -State $state -Clock $clock) { # tear autologon down before doing anything else } #> [CmdletBinding()] [OutputType([bool])] param( [Parameter(Mandatory = $true, Position = 0)] [ValidateNotNull()] [object] $State, [Parameter(Mandatory = $true)] [ValidateNotNull()] [object] $Clock, [Parameter()] [ValidateRange(0, [int]::MaxValue)] [int] $MaxAgeHour = 12 ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $now = $Clock.GetUtcNow() if (@('Succeeded', 'Failed') -contains [string] $State.status) { return $true } # Import-HDTRunState normalises this to a round-trip string, but a document # parsed elsewhere under pwsh 7 arrives carrying a [datetime], and both name # the same instant. if ($State.updatedUtc -is [datetime]) { return ((($now - ([datetime] $State.updatedUtc).ToUniversalTime()).TotalHours) -gt $MaxAgeHour) } $updated = [string] $State.updatedUtc if ([string]::IsNullOrWhiteSpace($updated)) { return $true } $stamp = [datetime]::MinValue $parsed = [datetime]::TryParse( $updated, [System.Globalization.CultureInfo]::InvariantCulture, [System.Globalization.DateTimeStyles]::RoundtripKind, [ref] $stamp) if (-not $parsed) { return $true } return (($now - $stamp.ToUniversalTime()).TotalHours -gt $MaxAgeHour) } |