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."