Em dashes (U+2014) and arrows (U+2192) break PowerShell 5.1 on Windows when the file has no UTF-8 BOM -- byte 0x94 gets read as a right double quote in Windows-1252, silently closing strings mid-parse. This caused run-enrollment.ps1 to fail on PXE-imaged machines with "string is missing the terminator" at line 113. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
142 lines
5.4 KiB
PowerShell
Executable File
142 lines
5.4 KiB
PowerShell
Executable File
# 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.
|
|
#
|
|
# Called by Run-ShopfloorSetup.ps1 AFTER all PreInstall + type-specific
|
|
# apps are already installed (not as a FirstLogonCommand -- that was the
|
|
# old flow).
|
|
|
|
$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 ---
|
|
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: $_"
|
|
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
|