From b69d68f7b5f479c3648f367b5145df048ad82ad2 Mon Sep 17 00:00:00 2001 From: cproudlock Date: Fri, 10 Apr 2026 14:15:45 -0400 Subject: [PATCH] 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) --- .../shopfloor-setup/Run-ShopfloorSetup.ps1 | 89 ++++++++++---- playbook/shopfloor-setup/run-enrollment.ps1 | 115 +++--------------- 2 files changed, 83 insertions(+), 121 deletions(-) diff --git a/playbook/shopfloor-setup/Run-ShopfloorSetup.ps1 b/playbook/shopfloor-setup/Run-ShopfloorSetup.ps1 index 49adb43..a9ef6c2 100644 --- a/playbook/shopfloor-setup/Run-ShopfloorSetup.ps1 +++ b/playbook/shopfloor-setup/Run-ShopfloorSetup.ps1 @@ -180,23 +180,53 @@ if ($pcType -eq "Standard") { } } -# --- Run enrollment (PPKG install) --- -# Enrollment runs AFTER all our apps are installed. The PPKG installs -# Chrome, Office, CyberArk, Tanium, etc. and needs a reboot for file -# operations (Zscaler rename, PPKG cleanup). run-enrollment.ps1 waits -# for all PPKG steps to complete, registers sync_intune as a persistent -# @logon scheduled task, then reboots. -$enrollScript = Join-Path $enrollDir 'run-enrollment.ps1' -if (Test-Path -LiteralPath $enrollScript) { - Write-Host "" - Write-Host "=== Running enrollment (PPKG install) ===" +# --- Register sync_intune as persistent @logon scheduled task --- +# Must be registered BEFORE enrollment because Install-ProvisioningPackage +# triggers an immediate reboot that kills run-enrollment.ps1. The task +# registration must survive the PPKG reboot, so we do it here while +# Run-ShopfloorSetup.ps1 is still running. +# +# The task fires at every logon until sync_intune detects completion and +# unregisters itself. It monitors Intune enrollment (Phase 1-5), NOT BPRT +# app installs -- BPRT finishes on its own in the background after the +# 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 { - & $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 { - Write-Warning "run-enrollment.ps1 failed: $_" + Write-Warning "Failed to register sync task: $_" } } 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 @@ -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 Write-Host "Auto-logon set to 4 remaining logins." -Write-Host "" -Write-Host "================================================================" -Write-Host "=== Run-ShopfloorSetup.ps1 complete $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') ===" -Write-Host "================================================================" - -# Flush transcript before shutdown so the log file is complete on next boot -try { Stop-Transcript | Out-Null } catch {} - -# run-enrollment.ps1 already initiated the reboot. If it didn't run -# (no PPKG), reboot now. -if (-not (Test-Path -LiteralPath $enrollScript)) { +# --- Run enrollment (PPKG install) --- +# Enrollment is the LAST thing we do. Install-ProvisioningPackage triggers +# an immediate reboot -- everything after this call is unlikely to execute. +# The sync_intune task is already registered above, so the PPKG reboot +# can kill us and the chain continues on the next boot. +$enrollScript = Join-Path $enrollDir 'run-enrollment.ps1' +if (Test-Path -LiteralPath $enrollScript) { + Write-Host "" + Write-Host "=== Running enrollment (PPKG install) ===" + Write-Host "NOTE: PPKG will trigger an immediate reboot. This is expected." + 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..." shutdown /r /t 10 } diff --git a/playbook/shopfloor-setup/run-enrollment.ps1 b/playbook/shopfloor-setup/run-enrollment.ps1 index 7c6ce49..7af5a53 100755 --- a/playbook/shopfloor-setup/run-enrollment.ps1 +++ b/playbook/shopfloor-setup/run-enrollment.ps1 @@ -1,11 +1,9 @@ # run-enrollment.ps1 -# Installs GCCH enrollment provisioning package, waits for all PPKG apps -# to finish installing, registers sync_intune as a persistent @logon task, -# then reboots. +# Installs GCCH enrollment provisioning package. That's it. # -# Called by Run-ShopfloorSetup.ps1 AFTER all PreInstall + type-specific -# apps are already installed (not as a FirstLogonCommand -- that was the -# old flow). +# 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" @@ -35,107 +33,30 @@ $newName = "E$serial" Log "Setting computer name to $newName" 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 --- -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 { Install-ProvisioningPackage -PackagePath $ppkgFile.FullName -ForceInstall -QuietInstall - Log "Provisioning package installed successfully." + Log "Install-ProvisioningPackage returned (reboot may be imminent)." } 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)." + Log "Add-ProvisioningPackage returned." } catch { Log "ERROR: Fallback also failed: $_" - return } } -# --- 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. "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 +# If we get here, the PPKG didn't reboot immediately. Unlikely but handle it. +Log "PPKG did not trigger immediate reboot. Returning to caller."