FIPS-enforced PCs (System cryptography GPO) reject non-approved algorithms at the .NET crypto API level. MD5 throws "This implementation is not part of the Windows Platform FIPS validated cryptographic algorithms" on .Create(), which aborts Register-GEEnforce before the scheduled task is built. SHA-256 is FIPS 180-4 approved and its default .NET provider is validated, so SHA256.Create() works under FIPS mode. Functionally equivalent for the 0-4 minute modulo we need for jitter. Hit this live on the first production retrofit. Enforcer runtime files were copied and legacy tasks were unregistered, but the new task creation aborted. Rerunning Deploy-GEEnforce.ps1 is idempotent and recovers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
105 lines
4.4 KiB
PowerShell
105 lines
4.4 KiB
PowerShell
# Register-GEEnforce.ps1 - Registers "GE Shopfloor Enforce" scheduled task.
|
|
#
|
|
# Idempotent: deletes an existing task with the same name first. Also
|
|
# unregisters the legacy per-type enforcer tasks so they don't race.
|
|
#
|
|
# Triggers (all run as SYSTEM, RunLevel=Highest):
|
|
# - AtLogOn for first-boot catch-up
|
|
# - Repetition every 5 min for fleet auto-update (jittered start offset
|
|
# in the first 5 min window to spread 200 PCs)
|
|
# - Shift-change windows 05:45, 13:45, 21:45 EST (30-min window each;
|
|
# handled by running the task at window start
|
|
# and letting the lib run anything not yet
|
|
# applied - Stage 2b will gate ApplyMode=Nightly
|
|
# entries to only fire inside a window).
|
|
#
|
|
# Called from Run-ShopfloorSetup.ps1 at imaging time. Also usable manually
|
|
# to re-register on an existing PC.
|
|
|
|
param(
|
|
[string]$EnforcerPath = 'C:\Program Files\GE\Shopfloor\GE-Enforce.ps1'
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$taskName = 'GE Shopfloor Enforce'
|
|
$legacyTasks = @(
|
|
'GE Common Enforce',
|
|
'GE Acrobat Enforce',
|
|
'GE Shopfloor Machine Apps Enforce',
|
|
'GE Machine Enforce',
|
|
'GE CMM Enforce',
|
|
'GE Keyence Enforce'
|
|
)
|
|
|
|
function Write-RegisterLog { param([string]$m) Write-Host "[Register-GEEnforce] $m" }
|
|
|
|
# Unregister legacy per-type enforcers so only GE-Enforce drives the fleet.
|
|
foreach ($name in $legacyTasks) {
|
|
$t = Get-ScheduledTask -TaskName $name -ErrorAction SilentlyContinue
|
|
if ($t) {
|
|
Write-RegisterLog "Unregistering legacy task: $name"
|
|
Unregister-ScheduledTask -TaskName $name -Confirm:$false -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
# Drop an existing copy of our own task (re-imaging idempotency).
|
|
$existing = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
|
|
if ($existing) {
|
|
Write-RegisterLog "Removing existing scheduled task: $taskName"
|
|
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
# --- Action ---
|
|
$action = New-ScheduledTaskAction `
|
|
-Execute 'powershell.exe' `
|
|
-Argument "-NoProfile -ExecutionPolicy Bypass -File `"$EnforcerPath`""
|
|
|
|
# --- Triggers ---
|
|
# Per-PC random offset [0, 5) min so 200 PCs don't all fire on :00/:05/:10/...
|
|
# Derived from hostname hash so the same PC always picks the same offset.
|
|
# SHA-256 instead of MD5 because FIPS-enforced PCs (System Cryptography
|
|
# Group Policy) disable MD5 entirely and would throw here; SHA-256 is
|
|
# FIPS 180-4 approved.
|
|
$hostHash = [System.BitConverter]::ToUInt32(
|
|
[System.Security.Cryptography.SHA256]::Create().ComputeHash(
|
|
[System.Text.Encoding]::UTF8.GetBytes($env:COMPUTERNAME)), 0)
|
|
$offsetMin = $hostHash % 5 # 0..4
|
|
|
|
$startToday = (Get-Date -Hour 0 -Minute $offsetMin -Second 0).AddSeconds(0)
|
|
|
|
$logonTrigger = New-ScheduledTaskTrigger -AtLogOn
|
|
# Periodic every 5 minutes, repeating indefinitely, starting at the offset
|
|
$periodicTrigger = New-ScheduledTaskTrigger -Once -At $startToday -RepetitionInterval (New-TimeSpan -Minutes 5)
|
|
|
|
$shift1Trigger = New-ScheduledTaskTrigger -Daily -At '05:45' # 3-to-1 shift
|
|
$shift2Trigger = New-ScheduledTaskTrigger -Daily -At '13:45' # 1-to-2
|
|
$shift3Trigger = New-ScheduledTaskTrigger -Daily -At '21:45' # 2-to-3
|
|
|
|
$triggers = @($logonTrigger, $periodicTrigger, $shift1Trigger, $shift2Trigger, $shift3Trigger)
|
|
|
|
# --- Principal + Settings ---
|
|
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
|
|
$settings = New-ScheduledTaskSettingsSet `
|
|
-AllowStartIfOnBatteries `
|
|
-DontStopIfGoingOnBatteries `
|
|
-StartWhenAvailable `
|
|
-ExecutionTimeLimit (New-TimeSpan -Hours 1) `
|
|
-MultipleInstances IgnoreNew
|
|
|
|
$description = "GE Shopfloor unified enforcer. Reads common + pc-type manifests from " +
|
|
"\\tsgwp00525.wjs.geaerospace.net\shared\dt\shopfloor\, runs " +
|
|
"Install-FromManifest.ps1 against each. Periodic 5-min jitter + " +
|
|
"AtLogOn + 3x shift-change windows. Log: C:\Logs\Shopfloor\enforce-*.log"
|
|
|
|
Register-ScheduledTask `
|
|
-TaskName $taskName `
|
|
-Action $action `
|
|
-Trigger $triggers `
|
|
-Principal $principal `
|
|
-Settings $settings `
|
|
-Description $description | Out-Null
|
|
|
|
Write-RegisterLog "Registered '$taskName' (offset $offsetMin min)"
|
|
Write-RegisterLog " Triggers: AtLogOn, every 5min, 05:45, 13:45, 21:45"
|
|
Write-RegisterLog " Action: powershell -File $EnforcerPath"
|