Register sync task BEFORE enrollment (PPKG reboot kills run-enrollment)

Install-ProvisioningPackage triggers an immediate reboot that kills
run-enrollment.ps1 before it can register the sync_intune task or do
any post-install work. BPRT app installs happen on the NEXT boot, not
before the reboot.

Fix: move sync task registration into Run-ShopfloorSetup.ps1, executed
BEFORE calling run-enrollment.ps1. The task is safely registered while
we still have control. Then enrollment installs the PPKG and lets it
reboot. After reboot, BPRT finishes in background, sync task fires at
logon, monitors Intune enrollment (which is independent of BPRT).

Run-ShopfloorSetup.ps1:
  - Registers "Shopfloor Intune Sync" @logon task after desktop tool
    copies but BEFORE enrollment
  - Flushes transcript before calling enrollment (since PPKG reboot
    will kill us, ensures log is complete)
  - Enrollment is the absolute last call

run-enrollment.ps1:
  - Stripped to essentials: find PPKG, rename computer, set OOBE,
    Install-ProvisioningPackage
  - No BPRT polling (irrelevant - happens after reboot)
  - No task registration (already done by caller)
  - No shutdown call (PPKG handles it)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-04-10 14:15:45 -04:00
parent db3f126fb3
commit b69d68f7b5
2 changed files with 83 additions and 121 deletions

View File

@@ -180,23 +180,53 @@ if ($pcType -eq "Standard") {
} }
} }
# --- Run enrollment (PPKG install) --- # --- Register sync_intune as persistent @logon scheduled task ---
# Enrollment runs AFTER all our apps are installed. The PPKG installs # Must be registered BEFORE enrollment because Install-ProvisioningPackage
# Chrome, Office, CyberArk, Tanium, etc. and needs a reboot for file # triggers an immediate reboot that kills run-enrollment.ps1. The task
# operations (Zscaler rename, PPKG cleanup). run-enrollment.ps1 waits # registration must survive the PPKG reboot, so we do it here while
# for all PPKG steps to complete, registers sync_intune as a persistent # Run-ShopfloorSetup.ps1 is still running.
# @logon scheduled task, then reboots. #
$enrollScript = Join-Path $enrollDir 'run-enrollment.ps1' # The task fires at every logon until sync_intune detects completion and
if (Test-Path -LiteralPath $enrollScript) { # unregisters itself. It monitors Intune enrollment (Phase 1-5), NOT BPRT
Write-Host "" # app installs -- BPRT finishes on its own in the background after the
Write-Host "=== Running enrollment (PPKG install) ===" # PPKG reboot, and is irrelevant to the Intune lifecycle.
$taskName = 'Shopfloor Intune Sync'
$monitorScript = Join-Path $setupDir 'Shopfloor\lib\Monitor-IntuneProgress.ps1'
$configureScript = Join-Path $setupDir 'Shopfloor\Configure-PC.ps1'
if (Test-Path -LiteralPath $monitorScript) {
try { try {
& $enrollScript $action = New-ScheduledTaskAction `
-Execute 'powershell.exe' `
-Argument "-NoProfile -NoExit -ExecutionPolicy Bypass -File `"$monitorScript`" -AsTask -ConfigureScript `"$configureScript`""
$trigger = New-ScheduledTaskTrigger -AtLogOn
$principal = New-ScheduledTaskPrincipal `
-GroupId 'S-1-5-32-545' `
-RunLevel Limited
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-ExecutionTimeLimit (New-TimeSpan -Hours 2)
Register-ScheduledTask `
-TaskName $taskName `
-Action $action `
-Trigger $trigger `
-Principal $principal `
-Settings $settings `
-Force `
-ErrorAction Stop | Out-Null
Write-Host "Registered '$taskName' logon task."
} catch { } catch {
Write-Warning "run-enrollment.ps1 failed: $_" Write-Warning "Failed to register sync task: $_"
} }
} else { } else {
Write-Host "run-enrollment.ps1 not found - skipping enrollment." Write-Warning "Monitor-IntuneProgress.ps1 not found at $monitorScript"
} }
# Set auto-logon to expire after 4 more logins (2 needed for sync_intune # Set auto-logon to expire after 4 more logins (2 needed for sync_intune
@@ -205,17 +235,28 @@ if (Test-Path -LiteralPath $enrollScript) {
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v AutoLogonCount /t REG_DWORD /d 4 /f | Out-Null reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v AutoLogonCount /t REG_DWORD /d 4 /f | Out-Null
Write-Host "Auto-logon set to 4 remaining logins." Write-Host "Auto-logon set to 4 remaining logins."
Write-Host "" # --- Run enrollment (PPKG install) ---
Write-Host "================================================================" # Enrollment is the LAST thing we do. Install-ProvisioningPackage triggers
Write-Host "=== Run-ShopfloorSetup.ps1 complete $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') ===" # an immediate reboot -- everything after this call is unlikely to execute.
Write-Host "================================================================" # The sync_intune task is already registered above, so the PPKG reboot
# can kill us and the chain continues on the next boot.
# Flush transcript before shutdown so the log file is complete on next boot $enrollScript = Join-Path $enrollDir 'run-enrollment.ps1'
try { Stop-Transcript | Out-Null } catch {} if (Test-Path -LiteralPath $enrollScript) {
Write-Host ""
# run-enrollment.ps1 already initiated the reboot. If it didn't run Write-Host "=== Running enrollment (PPKG install) ==="
# (no PPKG), reboot now. Write-Host "NOTE: PPKG will trigger an immediate reboot. This is expected."
if (-not (Test-Path -LiteralPath $enrollScript)) { try { Stop-Transcript | Out-Null } catch {}
& $enrollScript
# If we get here, the PPKG didn't reboot (maybe no PPKG file found).
Write-Host "Enrollment returned without rebooting. Rebooting now..."
shutdown /r /t 10
} else {
Write-Host "run-enrollment.ps1 not found - skipping enrollment."
Write-Host ""
Write-Host "================================================================"
Write-Host "=== Run-ShopfloorSetup.ps1 complete $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') ==="
Write-Host "================================================================"
try { Stop-Transcript | Out-Null } catch {}
Write-Host "Rebooting in 10 seconds..." Write-Host "Rebooting in 10 seconds..."
shutdown /r /t 10 shutdown /r /t 10
} }

View File

@@ -1,11 +1,9 @@
# run-enrollment.ps1 # run-enrollment.ps1
# Installs GCCH enrollment provisioning package, waits for all PPKG apps # Installs GCCH enrollment provisioning package. That's it.
# to finish installing, registers sync_intune as a persistent @logon task,
# then reboots.
# #
# Called by Run-ShopfloorSetup.ps1 AFTER all PreInstall + type-specific # Install-ProvisioningPackage triggers an immediate reboot -- nothing after
# apps are already installed (not as a FirstLogonCommand -- that was the # that call executes. The sync_intune task and all other post-enrollment
# old flow). # setup are registered by Run-ShopfloorSetup.ps1 BEFORE calling this script.
$ErrorActionPreference = 'Continue' $ErrorActionPreference = 'Continue'
$logFile = "C:\Logs\enrollment.log" $logFile = "C:\Logs\enrollment.log"
@@ -35,107 +33,30 @@ $newName = "E$serial"
Log "Setting computer name to $newName" Log "Setting computer name to $newName"
Rename-Computer -NewName $newName -Force -ErrorAction SilentlyContinue Rename-Computer -NewName $newName -Force -ErrorAction SilentlyContinue
# --- Set OOBE complete (must happen before PPKG 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
# --- Install provisioning package --- # --- Install provisioning package ---
Log "Installing provisioning package..." # This triggers an IMMEDIATE reboot. Nothing below this line executes.
# 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.
Log "Installing provisioning package (PPKG will reboot immediately)..."
try { try {
Install-ProvisioningPackage -PackagePath $ppkgFile.FullName -ForceInstall -QuietInstall Install-ProvisioningPackage -PackagePath $ppkgFile.FullName -ForceInstall -QuietInstall
Log "Provisioning package installed successfully." Log "Install-ProvisioningPackage returned (reboot may be imminent)."
} catch { } catch {
Log "ERROR: Install-ProvisioningPackage failed: $_" Log "ERROR: Install-ProvisioningPackage failed: $_"
Log "Attempting fallback with Add-ProvisioningPackage..." Log "Attempting fallback with Add-ProvisioningPackage..."
try { try {
Add-ProvisioningPackage -PackagePath $ppkgFile.FullName -ForceInstall -QuietInstall Add-ProvisioningPackage -PackagePath $ppkgFile.FullName -ForceInstall -QuietInstall
Log "Provisioning package added successfully (fallback)." Log "Add-ProvisioningPackage returned."
} catch { } catch {
Log "ERROR: Fallback also failed: $_" Log "ERROR: Fallback also failed: $_"
return
} }
} }
# --- Wait for PPKG provisioning to finish --- # If we get here, the PPKG didn't reboot immediately. Unlikely but handle it.
# Install-ProvisioningPackage is async -- it queues the provisioning engine Log "PPKG did not trigger immediate reboot. Returning to caller."
# and returns immediately. The actual app installs (Chrome, Office, Tanium,
# CyberArk, etc.) run in the background as BPRT steps. "Remove Staging
# Locations" is the LAST step -- when its Log.txt exists, all provisioning
# is done.
$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..."
while (-not (Test-Path -LiteralPath $bprtMarker) -and $elapsed -lt $maxWait) {
Start-Sleep -Seconds $pollInterval
$elapsed += $pollInterval
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."
} else {
Log "WARNING: PPKG provisioning timeout after $maxWait s."
}
# --- 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
# --- Register sync_intune as persistent @logon scheduled task ---
# sync_intune monitors the Intune enrollment lifecycle (5-phase status
# table). It stays registered as a logon task until:
# - Pre-reboot: monitors until Phase 1+2+3 done -> reboots
# - Post-reboot: monitors until DSC install complete -> unregisters
# itself, launches Configure-PC
#
# Runs as BUILTIN\Users (logged-in user) so it can show GUI (QR code,
# status table). Needs the interactive session, NOT SYSTEM.
$taskName = 'Shopfloor Intune Sync'
$monitorScript = 'C:\Enrollment\shopfloor-setup\Shopfloor\lib\Monitor-IntuneProgress.ps1'
$configureScript = 'C:\Enrollment\shopfloor-setup\Shopfloor\Configure-PC.ps1'
if (Test-Path -LiteralPath $monitorScript) {
try {
$action = New-ScheduledTaskAction `
-Execute 'powershell.exe' `
-Argument "-NoProfile -NoExit -ExecutionPolicy Bypass -File `"$monitorScript`" -AsTask -ConfigureScript `"$configureScript`""
$trigger = New-ScheduledTaskTrigger -AtLogOn
$principal = New-ScheduledTaskPrincipal `
-GroupId 'S-1-5-32-545' `
-RunLevel Limited
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-ExecutionTimeLimit (New-TimeSpan -Hours 2)
Register-ScheduledTask `
-TaskName $taskName `
-Action $action `
-Trigger $trigger `
-Principal $principal `
-Settings $settings `
-Force `
-ErrorAction Stop | Out-Null
Log "Registered '$taskName' logon task (persists until sync complete)."
} catch {
Log "WARNING: Failed to register sync task: $_"
}
} else {
Log "WARNING: Monitor-IntuneProgress.ps1 not found at $monitorScript"
}
Log "=== Enrollment complete. Rebooting... ==="
# Reboot -- PPKG file operations (Zscaler rename, cleanup) happen on next boot.
# sync_intune fires at next logon via the scheduled task.
shutdown /r /t 10