Files
pxe-server/playbook/shopfloor-setup/run-enrollment.ps1
cproudlock fb5841eb20 run-enrollment: wait for PPKG provisioning before staging chain
Install-ProvisioningPackage is async — it queues the provisioning engine
and returns immediately. The actual BPRT app installs (Chrome, Office,
Tanium, CyberArk, etc.) run in the background. Without waiting, the
PPKG reboot fires while installs are still in progress, leaving apps
partially installed.

Fix: poll for C:\Logs\BPRT\Remove Staging Locations\Log.txt — the last
BPRT step. When that file exists, all provisioning steps have completed.
Polls every 10 seconds for up to 15 minutes (Office install can be slow).
Progress logged every 30 seconds showing which steps have finished.

If the timeout fires (15 min), logs a warning and proceeds — the SYSTEM
logon task from 06-OrganizeDesktop.ps1 provides self-healing on the next
boot for anything that was incomplete.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 12:31:36 -04:00

114 lines
4.9 KiB
PowerShell
Executable File

# run-enrollment.ps1
# Installs GCCH enrollment provisioning package via Install-ProvisioningPackage
# Called by FirstLogonCommands as SupportUser (admin) after imaging
$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 "ERROR: No .ppkg found in C:\Enrollment\"
exit 1
}
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 ---
Log "Installing provisioning package..."
try {
Install-ProvisioningPackage -PackagePath $ppkgFile.FullName -ForceInstall -QuietInstall
Log "Provisioning package installed successfully."
} catch {
Log "ERROR: Install-ProvisioningPackage failed: $_"
Log "Attempting fallback with Add-ProvisioningPackage..."
try {
Add-ProvisioningPackage -PackagePath $ppkgFile.FullName -ForceInstall -QuietInstall
Log "Provisioning package added successfully (fallback)."
} catch {
Log "ERROR: Fallback also failed: $_"
exit 1
}
}
# --- Wait for PPKG provisioning to finish ---
# Install-ProvisioningPackage is async — it queues the provisioning engine
# and returns immediately. The actual app installs (Chrome, Office, Tanium,
# CyberArk, etc.) run in the background as BPRT steps. Each step writes a
# Log.txt under C:\Logs\BPRT\<step name>\. "Remove Staging Locations" is
# the LAST step — when its Log.txt exists, all provisioning is done.
# We poll for that marker before writing the stage file, so the PPKG reboot
# doesn't fire while installs are still in progress.
$bprtMarker = 'C:\Logs\BPRT\Remove Staging Locations\Log.txt'
$maxWait = 900 # 15 minutes (Office install can be slow)
$pollInterval = 10
$elapsed = 0
Log "Waiting for PPKG provisioning to complete (polling for $bprtMarker)..."
while (-not (Test-Path -LiteralPath $bprtMarker) -and $elapsed -lt $maxWait) {
Start-Sleep -Seconds $pollInterval
$elapsed += $pollInterval
# Show progress every 30 seconds
if ($elapsed % 30 -eq 0) {
$completedSteps = @(Get-ChildItem 'C:\Logs\BPRT' -Directory -ErrorAction SilentlyContinue |
Where-Object { Test-Path (Join-Path $_.FullName 'Log.txt') } |
Select-Object -ExpandProperty Name)
Log " $elapsed s elapsed, $($completedSteps.Count) BPRT steps done: $($completedSteps -join ', ')"
}
}
if (Test-Path -LiteralPath $bprtMarker) {
Log "PPKG provisioning complete after $elapsed s."
$allSteps = @(Get-ChildItem 'C:\Logs\BPRT' -Directory -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name)
Log " Completed steps: $($allSteps -join ', ')"
} else {
Log "WARNING: PPKG provisioning timeout after $maxWait s — some apps may not be installed."
Log " Proceeding anyway. The SYSTEM logon task will retry incomplete items on next boot."
}
# --- Set OOBE complete ---
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
# --- Stage the imaging chain for next boot ---
# The PPKG schedules a reboot (PendingFileRenameOperations for Zscaler
# rename, PPKG self-cleanup, etc). Instead of canceling it and cramming
# Run-ShopfloorSetup into this same session, we let the reboot happen
# and register a RunOnce entry that fires Stage-Dispatcher.ps1 on the
# next autologon. The dispatcher reads setup-stage.txt and chains
# through: shopfloor-setup -> sync-intune -> configure-pc.
$stageFile = 'C:\Enrollment\setup-stage.txt'
$dispatcherPath = 'C:\Enrollment\Stage-Dispatcher.ps1'
$runOnceKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce'
Log "Writing stage file: shopfloor-setup"
Set-Content -LiteralPath $stageFile -Value 'shopfloor-setup' -Force
if (Test-Path -LiteralPath $dispatcherPath) {
Log "Registering RunOnce for Stage-Dispatcher.ps1"
Set-ItemProperty -Path $runOnceKey -Name 'ShopfloorSetup' `
-Value "powershell.exe -NoProfile -ExecutionPolicy Bypass -File `"$dispatcherPath`"" `
-Type String -Force
} else {
Log "WARNING: Stage-Dispatcher.ps1 not found at $dispatcherPath - RunOnce not set"
}
Log "=== Enrollment complete. PPKG reboot will fire and Stage-Dispatcher picks up on next logon. ==="