Files
pxe-server/playbook/imaging-status/Send-ImagingStage.ps1
cproudlock 9d75d03b2d Standard/Engineer builds are done at OOBE on the imaging floor
Everything after OOBE needs the employee to log in, which happens off the
PXE network, so the floor waited forever at step 3 of 5. Now 3 steps:
WinPE, Windows setup installing apps, ready at OOBE (succeeded).

- both unattends: specialize Order 49 posts step 3 as succeeded
  (Path 182 chars; lint clean). Only that line changed.
- Send-ImagingStage.ps1: StageTotal 3; steps above the total (the old
  first-logon steps 4 and 5 still in the unattends) are logged and not
  posted, so they cannot reopen a finished build
- winpe-status-push.ps1: total 3 for gea-standard / gea-engineer
- floor tile step 3: "Ready at OOBE"

Tested in the win11 VM against the test server: steps 2 and 3 post,
step 3 shows succeeded, step 4 is skipped. Not deployed: the unattends
go out only through scripts/deploy-unattend.sh.
2026-10-16 10:09:40 -04:00

97 lines
3.9 KiB
PowerShell

# Send-ImagingStage.ps1 - posts one progress step of a gea-standard /
# gea-engineer build to the PXE webapp's /imaging/status endpoint, so the
# imaging floor follows these builds past WinPE (the shopfloor build has its
# own reporting in Shopfloor\lib\Send-PxeStatus.ps1).
#
# Called from the image's FlatUnattendW10.xml (specialize RunSynchronous and
# oobeSystem FirstLogonCommands). Keep those command lines short: Windows
# rejects the whole unattend if a RunSynchronous Path exceeds 259 characters
# (check with scripts/lint-unattend.py --local). Deployed to
# _shared\Applications\extra\pxe-status\ on the PXE server, which PESetup
# copies to C:\Deploy\Applications\extra\pxe-status\.
#
# Best-effort: never blocks or fails Windows setup. Every error is swallowed,
# the script always exits 0, and each attempt is logged to
# C:\Logs\send-imaging-stage.log.
#
# Three steps: 1 WinPE, 2 Windows setup installing apps, 3 ready at OOBE
# (succeeded). Everything after OOBE needs the employee to log in, which
# happens off the PXE network, so PXE cannot see it. Steps above
# StageTotal (the old first-logon steps 4 and 5 still in the unattends) are
# logged and not posted, so they cannot reopen a finished build.
param(
[Parameter(Mandatory = $true)][string]$Stage,
[int]$StageIndex = 0,
[int]$StageTotal = 3,
[string]$PCType = '',
[ValidateSet('in_progress', 'succeeded', 'failed')][string]$Status = 'in_progress',
[string]$PxeServer = '172.16.9.1',
[int]$Port = 9009,
[int]$TimeoutSec = 5,
# Copy this script to C:\ProgramData\PxeStatus first: the bulk-enrollment
# task deletes C:\Deploy before first logon, when the later steps run.
[switch]$Persist
)
$logFile = 'C:\Logs\send-imaging-stage.log'
function Log([string]$msg) {
try {
if (-not (Test-Path 'C:\Logs')) { New-Item -ItemType Directory -Path 'C:\Logs' -Force | Out-Null }
"$(Get-Date -Format s) $msg" | Out-File -FilePath $logFile -Append -Encoding utf8
} catch { }
}
if ($Persist) {
try {
New-Item -ItemType Directory -Path 'C:\ProgramData\PxeStatus' -Force | Out-Null
Copy-Item -LiteralPath $PSCommandPath -Destination 'C:\ProgramData\PxeStatus\Send-ImagingStage.ps1' -Force
} catch { Log "persist failed: $($_.Exception.Message)" }
}
if ($StageIndex -gt $StageTotal) {
Log "SKIP stage $StageIndex of $StageTotal (after OOBE; PXE does not track it): $Stage"
exit 0
}
try {
$serial = $null
try { $serial = (Get-CimInstance -ClassName Win32_BIOS -ErrorAction Stop).SerialNumber } catch {
try { $serial = (Get-WmiObject -Class Win32_BIOS -ErrorAction Stop).SerialNumber } catch { }
}
if ([string]::IsNullOrWhiteSpace($serial)) { $serial = $env:COMPUTERNAME }
$serial = ($serial -as [string]).Trim()
$mac = $null
try {
$nic = Get-NetAdapter -Physical -ErrorAction Stop |
Where-Object { $_.Status -eq 'Up' -and $_.MediaType -eq '802.3' } | Select-Object -First 1
if ($nic) { $mac = $nic.MacAddress -replace '-', ':' }
} catch { }
$payload = @{
serial = $serial
hostname_target = $env:COMPUTERNAME
current_stage = $Stage
stage_index = $StageIndex
stage_total = $StageTotal
status = $Status
}
if ($PCType) { $payload.pctype = $PCType }
if ($mac) { $payload.mac = $mac }
$body = $payload | ConvertTo-Json -Compress
$uri = "http://${PxeServer}:${Port}/imaging/status"
try {
$resp = Invoke-WebRequest -Uri $uri -Method POST -Body $body -ContentType 'application/json' `
-UseBasicParsing -TimeoutSec $TimeoutSec -ErrorAction Stop
Log "OK $($resp.StatusCode) $body"
} catch {
# Off the imaging network (or the server is down): nothing to report to.
Log "FAIL $($_.Exception.Message) $body"
}
} catch {
Log "ERROR $($_.Exception.Message)"
}
exit 0