# 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