Fix Display sync loop, PPKG deployment, dnsmasq cron, dpkg configure

- Monitor-IntuneProgress: Display PCs skip DSC phases entirely (no SAS
  token, no DSCInstall.log), complete after Phase 1 identity. Renderer
  hides Phase 2-5 for Display type.
- Playbook: deploy PPKG files and run-enrollment.ps1 from USB to
  enrollment share. Bump dnsmasq restart cron from 15s to 30s.
- build-usb.sh: copy enrollment/ directory (PPKGs) onto USB if present.
- user-data: add dpkg --configure -a after offline .deb install to fix
  packages left in unconfigured state (cron, systemd-timesyncd).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-04-13 14:27:21 -04:00
parent 743bc91996
commit 855d501fc2
4 changed files with 91 additions and 36 deletions

View File

@@ -480,44 +480,51 @@ function Format-Snapshot {
$lines += " $(Mk $Snap.Phase1.IntuneEnrolled) Intune enrolled"
$lines += " $(Mk $Snap.Phase1.EmTaskExists) EnterpriseMgmt sync tasks"
$lines += " $(Mk $Snap.Phase1.PoliciesArriving) Policies arriving"
$lines += ""
$lines += " Phase 2: SFLD configuration"
$lines += " $(Mk $Snap.Phase2.SfldRoot) SFLD reg key"
$lines += " $(Mk $Snap.Phase2.FunctionOk) Function set"
$lines += " $(Mk $Snap.Phase2.SasTokenOk) DSC SAS token configured"
$lines += ""
$lines += " Phase 3: DSC deployment + install"
$lines += " $(Mk $Snap.Phase3.DeployLogExists) DSCDeployment.log present"
$lines += " $(Mk $Snap.Phase3.DeployComplete) Pre-reboot deployment complete"
$lines += " $(Mk $Snap.Phase3.InstallLogExists) DSCInstall.log present"
$lines += " $(Mk $Snap.Phase3.InstallComplete) Post-reboot install complete"
$lines += ""
$lines += " Phase 4: Custom scripts (auto-discovered)"
if (-not $Snap.Phase4 -or $Snap.Phase4.Count -eq 0) {
$lines += " (no Install-*.log files yet in C:\Logs\SFLD)"
} else {
foreach ($s in $Snap.Phase4) {
$mark = switch ($s.Status) {
'done' { '[v]' }
'running' { '[.]' }
'failed' { '[!]' }
'pending' { '[ ]' }
default { '[?]' }
if (-not $skipDsc) {
$lines += ""
$lines += " Phase 2: SFLD configuration"
$lines += " $(Mk $Snap.Phase2.SfldRoot) SFLD reg key"
$lines += " $(Mk $Snap.Phase2.FunctionOk) Function set"
$lines += " $(Mk $Snap.Phase2.SasTokenOk) DSC SAS token configured"
$lines += ""
$lines += " Phase 3: DSC deployment + install"
$lines += " $(Mk $Snap.Phase3.DeployLogExists) DSCDeployment.log present"
$lines += " $(Mk $Snap.Phase3.DeployComplete) Pre-reboot deployment complete"
$lines += " $(Mk $Snap.Phase3.InstallLogExists) DSCInstall.log present"
$lines += " $(Mk $Snap.Phase3.InstallComplete) Post-reboot install complete"
$lines += ""
$lines += " Phase 4: Custom scripts (auto-discovered)"
if (-not $Snap.Phase4 -or $Snap.Phase4.Count -eq 0) {
$lines += " (no Install-*.log files yet in C:\Logs\SFLD)"
} else {
foreach ($s in $Snap.Phase4) {
$mark = switch ($s.Status) {
'done' { '[v]' }
'running' { '[.]' }
'failed' { '[!]' }
'pending' { '[ ]' }
default { '[?]' }
}
$detail = switch ($s.Status) {
'done' { "done $(Format-Age $s.Age) ago" }
'running' { "running, last update $(Format-Age $s.Age) ago" }
'failed' { "FAILED $(Format-Age $s.Age) ago" }
'pending' { "pending" }
default { "" }
}
$name = $s.Name.PadRight(22)
$lines += " $mark $name $detail"
}
$detail = switch ($s.Status) {
'done' { "done $(Format-Age $s.Age) ago" }
'running' { "running, last update $(Format-Age $s.Age) ago" }
'failed' { "FAILED $(Format-Age $s.Age) ago" }
'pending' { "pending" }
default { "" }
}
$name = $s.Name.PadRight(22)
$lines += " $mark $name $detail"
}
$lines += ""
$lines += " Phase 5: Final"
$lines += " $(Mk $Snap.Phase5.ConsumeCredsTask) SFLD - Consume Credentials task"
} else {
$lines += ""
$lines += " (DSC phases not applicable for $pcType)"
}
$lines += ""
$lines += " Phase 5: Final"
$lines += " $(Mk $Snap.Phase5.ConsumeCredsTask) SFLD - Consume Credentials task"
$lines += ""
$sinceSync = ((Get-Date) - $LastSync).TotalSeconds
$untilNext = ($NextRetrigger - (Get-Date)).TotalSeconds
@@ -674,6 +681,20 @@ if ($AsTask -and (Test-Path -LiteralPath $syncCompleteMarker)) {
exit 0
}
# PC types that don't receive DSC (no SAS token, no DSCInstall.log).
# For these types, enrollment is "done" once Phase 1 (Identity) completes
# and policies start arriving - there is no Phase 2-5 to wait for.
$pcTypeFile = 'C:\Enrollment\pc-type.txt'
$noDscTypes = @('Display')
$skipDsc = $false
if (Test-Path $pcTypeFile) {
$pcType = (Get-Content $pcTypeFile -First 1).Trim()
if ($noDscTypes -contains $pcType) {
$skipDsc = $true
Write-Host "PC type '$pcType' does not use DSC - will complete after Phase 1."
}
}
try {
$qrText = Build-QRCodeText
$qrRefreshed = [bool]($qrText -notmatch 'not yet Azure AD joined')
@@ -705,6 +726,13 @@ try {
Invoke-SetupComplete
}
# No-DSC types (Display, Lab): complete once Phase 1 identity is solid
if ($skipDsc -and $snap.Phase1.AzureAdJoined -and $snap.Phase1.IntuneEnrolled -and $snap.Phase1.PoliciesArriving) {
Write-Host ""
Write-Host "Phase 1 complete - no DSC needed for $pcType. Finishing up." -ForegroundColor Green
Invoke-SetupComplete
}
# Reboot check (boot-loop-safe). Only prompt once Phase 1 (Identity),
# Phase 2 (SFLD config), and Phase 3's first two items (deploy log
# present + pre-reboot deployment complete) are all green. Don't rush