geenforce: the shadow task actually runs, and says so on disk

Registered fine and never fired once. Three faults, all found on the win11 VM.

A `-Once -At (Get-Date)` trigger does NOT run immediately: its first run is the
start boundary PLUS the repetition interval, 15 minutes out. This ships as a
DetectionMethod=Always entry, so it ran every enforce cycle, 5 minutes apart,
and each Register-ScheduledTask -Force reset the start boundary to "now" -
pushing the first run back to +15 before the previous +15 could elapse. 5 < 15,
so the task sat Ready at LastTaskResult 267011 (SCHED_S_TASK_HAS_NOT_RUN)
forever. It now registers only when the task is missing or its arguments
changed, and starts it once on first registration rather than waiting out the
first interval.

A bay provisioned by the broken version carries a task with correct arguments
that has never run, so "leave it alone if it matches" would have stranded
exactly the machines that hit the bug. If the task has never run it is kicked
once; after that LastRunTime is set and the check is a no-op.

None of this was visible. The engine records only "ps1: <path>" and an exit
code for a PS1 entry, so Write-Host reached nothing, and with the fail-safe
`exit 0` on every path a silent early-out was indistinguishable from success.
It now also writes C:\Logs\Shopfloor\shadow-setup-<date>.log.

Scope is no longer hardcoded either: this script is shipped by more than one
scope now, and a wrong value would shadow the wrong manifest silently. It is
derived from the script's own directory, the same way the share manifest path
already was, so the two cannot disagree.
This commit is contained in:
cproudlock
2026-08-13 13:19:59 -04:00
parent 3d83806135
commit 6dc363411d

View File

@@ -20,12 +20,28 @@ $ErrorActionPreference = 'Continue'
$TaskName = 'ShopDB GE-Enforce (shadow)'
$InstallDir = 'C:\Program Files\GE\Shopfloor'
$BaseUrl = 'https://tsgwp00525.wjs.geaerospace.net/shopdb'
$Scope = 'gea-shopfloor-nocollections'
# Scope is NOT hardcoded: this script is shipped by more than one scope
# (collections and nocollections today), and a wrong value here would shadow the
# wrong manifest silently. It runs from <drive>:\<scope>\shopdb-client, so the
# directory it sits under IS the scope name - the same derivation used below for
# the share manifest, so the two cannot disagree.
$Scope = Split-Path -Leaf (Split-Path -Parent $PSScriptRoot)
function Write-ShadowLog {
# Write-Host ALONE is not enough: the engine records only "ps1: <path>" and
# the exit code for a PS1 entry, so nothing this script says reaches the
# enforce log. Combined with the fail-safe `exit 0` on every path, a silent
# early-out was indistinguishable from success - which is exactly how the
# never-firing task went unnoticed. Write to a file as well so the next
# failure is answerable from disk.
param([string]$Message)
$line = "[{0}] [shadow-setup] {1}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Message
Write-Host $line
try {
$dir = 'C:\Logs\Shopfloor'
if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
Add-Content -LiteralPath (Join-Path $dir ('shadow-setup-{0}.log' -f (Get-Date -Format yyyyMMdd))) -Value $line
} catch { }
}
try {
@@ -60,6 +76,37 @@ try {
$arguments = '-NoProfile -ExecutionPolicy Bypass -File "{0}" -Scope "{1}" -EnginePath "{2}" -ShadowMode -ShareManifestPath "{3}"' `
-f $runner, $Scope, $engine, $shareManifest
# ONLY register when it is missing or its arguments changed.
#
# Registering unconditionally is what stopped this working the first time.
# A `-Once -At (Get-Date)` trigger does NOT fire immediately: the first run
# is start-boundary PLUS the repetition interval, so 15 minutes out. This
# entry is DetectionMethod=Always and runs every enforce cycle, 5 minutes
# apart, and each Register-ScheduledTask -Force reset the start boundary to
# "now" - pushing the first run back to +15 before the previous +15 could
# elapse. 5 < 15, so the task sat at Ready with LastTaskResult 267011
# (SCHED_S_TASK_HAS_NOT_RUN) indefinitely. Measured on the win11 VM.
$existing = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if ($existing) {
$currentArgs = ($existing.Actions | Select-Object -First 1).Arguments
if ($currentArgs -eq $arguments) {
# Correct arguments, but a bay provisioned by the BROKEN version of
# this script carries a task that was reset every cycle and so has
# never run. Leaving it alone would strand exactly the bays that hit
# the bug. Kick it once; after that LastRunTime is set and this is a
# no-op forever.
$info = $existing | Get-ScheduledTaskInfo
$neverran = ($null -eq $info.LastRunTime) -or
($info.LastRunTime -lt (Get-Date '2000-01-01'))
if ($neverran) {
Write-ShadowLog ("task exists but has never run (LastTaskResult $($info.LastTaskResult)) - starting it once.")
Start-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
}
return # schedule is correct; do not reset the start boundary
}
Write-ShadowLog 'task arguments changed - re-registering.'
}
$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument $arguments
# RepetitionInterval ALONE - passing RepetitionDuration serializes to a
# Duration the Task Scheduler schema rejects.
@@ -69,6 +116,9 @@ try {
Register-ScheduledTask -TaskName $TaskName -Action $action -Trigger $trigger `
-Principal $principal -Settings $settings -Force | Out-Null
# Kick it once rather than waiting out the first 15-minute interval, so a
# freshly provisioned bay reports on this cycle instead of the next.
Start-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
Write-ShadowLog "registered '$TaskName' (every 15 min), shadowing $shareManifest"
exit 0
}