Files
pxe-server/playbook/shopfloor-setup/run-enrollment.ps1
cproudlock 67845372b2 Harvest provtool diagnostics, enable ETW channel, skip Timeclock machine#
run-enrollment.ps1:
- Enable Provisioning-Diagnostics-Provider/Admin event log before invoking
  provtool (was disabled by default; no diagnostics survived early runs).
- After provtool returns, copy C:\ProgramData\Microsoft\Provisioning\*
  into C:\Logs\PPKG\ and snapshot HKLM\...\Sessions\* as
  provisioning-sessions.json, plus export the Admin event channel to
  Provisioning-Diagnostics-Admin.evtx. Gives us reviewable state
  without relying on provtool's failure-only diagnostic bundle.
- provtool arg order is positional path + /quiet + /source BPRT (verified
  against ProvEventLog from the PS cmdlet internal call).

startnet.cmd / startnet-template.cmd:
- Standard-Timeclock sub-type skips the machine-number prompt. Timeclock
  PCs do not use a machine number so forcing a prompt wasted tech time
  and left MACHINENUM at the 9999 default anyway. Machine sub-type is
  unaffected.
2026-04-15 14:22:43 -04:00

153 lines
6.9 KiB
PowerShell
Executable File

# run-enrollment.ps1
# Installs GCCH enrollment provisioning package. That's it.
#
# Install-ProvisioningPackage triggers an immediate reboot -- nothing after
# that call executes. The sync_intune task and all other post-enrollment
# setup are registered by Run-ShopfloorSetup.ps1 BEFORE calling this script.
$ErrorActionPreference = 'Continue'
$logFile = "C:\Logs\enrollment.log"
New-Item -ItemType Directory -Path "C:\Logs" -Force -ErrorAction SilentlyContinue | Out-Null
function Log {
param([string]$Message)
$ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$line = "$ts $Message"
Write-Host $line
Add-Content -Path $logFile -Value $line
}
Log "=== GE Aerospace GCCH Enrollment ==="
# --- Find the .ppkg ---
$ppkgFile = Get-ChildItem "C:\Enrollment\*.ppkg" -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $ppkgFile) {
Log "No .ppkg found in C:\Enrollment\ - skipping enrollment."
return
}
Log "Package: $($ppkgFile.Name)"
# --- Set computer name to E<serial> ---
$serial = (Get-CimInstance Win32_BIOS).SerialNumber
$newName = "E$serial"
Log "Setting computer name to $newName"
Rename-Computer -NewName $newName -Force -ErrorAction SilentlyContinue
# --- Install provisioning package ---
# IMPORTANT: The PPKG must be installed BEFORE OOBEComplete is set. Bulk
# enrollment PPKGs are designed to run during OOBE; on Windows 11 22H2+ they
# can hang indefinitely if OOBE is already marked complete.
#
# We invoke provtool.exe directly instead of Install-ProvisioningPackage.
# The PowerShell cmdlet enforces a hardcoded 180-second timeout on the
# underlying provtool call, which a 7-8 GB GCCH PPKG often exceeds on
# slower disks. When the cmdlet times out it throws, and the Add-
# ProvisioningPackage fallback has been observed to invoke provtool with
# an empty packagePathsToAdd (session registered but never started),
# leaving the PC un-enrolled. provtool.exe directly has no caller-side
# timeout; Start-Process -Wait waits on the actual child process.
#
# The PPKG triggers an IMMEDIATE reboot once fully applied. Nothing below
# that point executes on the current boot. BPRT app installs (Chrome,
# Office, Tanium, etc.) happen on the next boot. The sync_intune
# scheduled task (registered by Run-ShopfloorSetup.ps1 before calling us)
# fires at the next logon to monitor Intune enrollment.
$ppkgLogDir = "C:\Logs\PPKG"
New-Item -ItemType Directory -Path $ppkgLogDir -Force -ErrorAction SilentlyContinue | Out-Null
$provtool = Join-Path $env:SystemRoot 'System32\provtool.exe'
# Arg order matches what the Install-ProvisioningPackage cmdlet invokes
# internally (observed in ProvEventLog.txt): positional path, then /quiet,
# then /source. No /log: or /ppkg: prefix - those are not valid provtool
# flags and caused 0x80004005 E_FAIL in the first test.
#
# /source BPRT keeps us in the bulk-provisioning code path used by the
# automatic OOBE PPKG runtime. PSCmdlet would give richer ETW/event-log
# diagnostics, but may change which actions in the PPKG actually execute
# (not verified against Microsoft docs). Stay on BPRT and enable logging
# manually below.
$provArgs = @("`"$($ppkgFile.FullName)`"", "/quiet", "/source", "BPRT")
# Enable the Provisioning-Diagnostics-Provider Admin channel so events
# from the BPRT run land somewhere we can export afterward. This is
# idempotent - running each time is safe.
wevtutil.exe set-log 'Microsoft-Windows-Provisioning-Diagnostics-Provider/Admin' /enabled:true 2>$null | Out-Null
Log "Installing provisioning package via provtool.exe (no PowerShell timeout)..."
Log "Command: $provtool $($provArgs -join ' ')"
Log "PPKG diagnostic logs -> $ppkgLogDir (provtool writes them automatically)"
try {
$p = Start-Process -FilePath $provtool -ArgumentList $provArgs -Wait -PassThru -NoNewWindow -ErrorAction Stop
Log "provtool.exe exit code: $($p.ExitCode)"
if ($p.ExitCode -ne 0) {
$hex = '0x{0:X8}' -f $p.ExitCode
Log "WARNING: provtool.exe returned non-zero exit code ($hex). Check $ppkgLogDir for diagnostic bundle."
}
} catch {
Log "ERROR: Failed to launch provtool.exe: $_"
}
# --- Harvest Windows' own provisioning diagnostics into $ppkgLogDir ---
# provtool.exe /quiet does not drop a zip bundle on success, so the real
# detail lives under C:\ProgramData\Microsoft\Provisioning\, in the
# registry under HKLM\Software\Microsoft\Provisioning\Sessions\*, and in
# the Provisioning-Diagnostics-Provider event log. Copy/export all three
# into our log dir so they ride back with the shopfloor logs bundle.
Log "Harvesting Windows provisioning diagnostics to $ppkgLogDir..."
try {
$provData = 'C:\ProgramData\Microsoft\Provisioning'
if (Test-Path $provData) {
Copy-Item -Path (Join-Path $provData '*') -Destination $ppkgLogDir `
-Recurse -Force -ErrorAction SilentlyContinue
Log " copied $provData -> $ppkgLogDir"
} else {
Log " $provData not present (provtool may not have touched it)"
}
} catch {
Log " WARN: ProgramData copy threw: $_"
}
try {
$sessions = Get-ChildItem 'HKLM:\Software\Microsoft\Provisioning\Sessions' -ErrorAction SilentlyContinue
if ($sessions) {
$snap = $sessions | ForEach-Object {
$props = Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue
if ($props) {
[pscustomobject]@{
Session = $_.PSChildName
BeginTime = $props.BeginTime
LastRunTime = $props.LastRunTime
RebootCount = $props.RebootCount
State = $props.State
StateValue = $props.StateValue
}
}
}
$snap | ConvertTo-Json -Depth 3 |
Out-File -FilePath (Join-Path $ppkgLogDir 'provisioning-sessions.json') -Encoding UTF8
Log " wrote provisioning-sessions.json ($($snap.Count) session(s))"
foreach ($s in $snap) {
Log " session $($s.Session): State=$($s.State) RebootCount=$($s.RebootCount)"
}
} else {
Log " no sessions under HKLM:\Software\Microsoft\Provisioning\Sessions"
}
} catch {
Log " WARN: session snapshot threw: $_"
}
try {
$evtx = Join-Path $ppkgLogDir 'Provisioning-Diagnostics-Admin.evtx'
if (Test-Path $evtx) { Remove-Item $evtx -Force -ErrorAction SilentlyContinue }
$null = & wevtutil.exe epl 'Microsoft-Windows-Provisioning-Diagnostics-Provider/Admin' $evtx 2>&1
if (Test-Path $evtx) { Log " exported $evtx" }
} catch {
Log " WARN: wevtutil export threw: $_"
}
# --- Set OOBE complete (only reached if PPKG didn't trigger immediate reboot) ---
Log "Setting OOBE as complete..."
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\OOBE" /v OOBEComplete /t REG_DWORD /d 1 /f | Out-Null
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\OOBE" /v SetupDisplayedEula /t REG_DWORD /d 1 /f | Out-Null
# If we get here, the PPKG didn't reboot immediately. Unlikely but handle it.
Log "PPKG did not trigger immediate reboot. Returning to caller."