sync_intune: full lifecycle gate, lockdown phase, creds verification
Add Phase 6 (Lockdown) and tighten Phase 5 so the 5-min Intune sync loop doesn't declare success until the device is genuinely operator-ready. - Phase 6 watches two HKLM-level signals confirmed in the 2026-04-15 pre/post lockdown state diff: Winlogon\DefaultUserName flipped to 'ShopFloor', and local Administrator renamed to 'SFLDAdmin'. Both land via MDM PolicyCSP after DSCInstall.log finishes. - Phase 5 was just checking that the Consume Credentials scheduled task existed; that only proves DSC scheduled it. Now also verifies creds actually landed under HKLM:\SOFTWARE\GE\SFLD\Credentials\* with TargetHost+Username+Password populated -- which is what Machine/Acrobat/ CMM-Enforce actually consume. - Final completion gate: DscInstallComplete && CredsPopulated && LockdownComplete (was just DscInstallComplete). Display PCs unchanged -- they exit early via the no-DSC Phase 1 path. - Invoke-SetupComplete now issues shutdown /r /t 10 in AsTask mode after writing the sync-complete marker and running the Configure-PC machine# prompt. Next boot triggers ShopFloor autologon, which materializes the ShopFloor profile from C:\Users\Default (where 03-ShellDefaults already baked in TaskbarAl=0, etc.). - Phase 1->2 gap (waiting for tech to assign device category in Intune portal) now shows an explicit ACTION hint instead of empty checkboxes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,10 +29,20 @@
|
|||||||
# true, they don't go back to false)
|
# true, they don't go back to false)
|
||||||
#
|
#
|
||||||
# EXIT CODES (consumed by sync_intune.bat):
|
# EXIT CODES (consumed by sync_intune.bat):
|
||||||
# 0 = all done, post-reboot install complete, no further action needed
|
# 0 = all done, lockdown landed, system rebooting to ShopFloor autologon
|
||||||
# 2 = reboot required (deployment phase done, install phase pending)
|
# 2 = PPKG-reboot required (DSC deployment phase done, install phase pending)
|
||||||
# 1 = error
|
# 1 = error
|
||||||
#
|
#
|
||||||
|
# PHASE 6 (Lockdown) was added 2026-04-15 after a pre/post state capture
|
||||||
|
# showed that "DSCInstall.log complete" is NOT the true end-of-line. After
|
||||||
|
# DSC finishes, MDM/PolicyCSP continues delivering the kiosk baseline which
|
||||||
|
# ends with two observable machine-level signals:
|
||||||
|
# - HKLM Winlogon DefaultUserName flipped from SupportUser -> ShopFloor
|
||||||
|
# - Local 'Administrator' account renamed to 'SFLDAdmin'
|
||||||
|
# Once both land, the script writes sync-complete.txt, runs the tech-facing
|
||||||
|
# Configure-PC machine-number prompt, and issues shutdown /r. The next boot
|
||||||
|
# auto-logs in as ShopFloor, materializing its profile from Default User.
|
||||||
|
#
|
||||||
# DETECTION REFERENCES (decoded from a real run captured at /home/camp/pxe-images/Logs/):
|
# DETECTION REFERENCES (decoded from a real run captured at /home/camp/pxe-images/Logs/):
|
||||||
# Phase A (pre-reboot, ~08:35-08:52):
|
# Phase A (pre-reboot, ~08:35-08:52):
|
||||||
# - enrollment.log ppkg + computer name
|
# - enrollment.log ppkg + computer name
|
||||||
@@ -282,6 +292,22 @@ function Get-CustomScriptStatuses {
|
|||||||
# 'in-progress' - pre-reboot done AND we already rebooted (just waiting for
|
# 'in-progress' - pre-reboot done AND we already rebooted (just waiting for
|
||||||
# post-reboot DSCInstall.log to finish)
|
# post-reboot DSCInstall.log to finish)
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
function Get-LockdownState {
|
||||||
|
# Machine-level signals that the kiosk/lockdown baseline has finished
|
||||||
|
# being applied. Both are HKLM/SAM changes pushed by MDM PolicyCSP after
|
||||||
|
# DSCInstall.log finishes, so they land independently of which user is
|
||||||
|
# currently logged in. See pre/post state diff 2026-04-15 for rationale.
|
||||||
|
$wl = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon'
|
||||||
|
$defUser = Read-RegValue $wl 'DefaultUserName'
|
||||||
|
$autoUser = ($defUser -eq 'ShopFloor')
|
||||||
|
$adminRenamed = [bool](Get-LocalUser -Name 'SFLDAdmin' -ErrorAction SilentlyContinue)
|
||||||
|
return @{
|
||||||
|
AutologonShopfloor = $autoUser
|
||||||
|
AdminRenamed = $adminRenamed
|
||||||
|
Complete = ($autoUser -and $adminRenamed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function Test-RebootState {
|
function Test-RebootState {
|
||||||
$deployLog = 'C:\Logs\SFLD\DSCDeployment.log'
|
$deployLog = 'C:\Logs\SFLD\DSCDeployment.log'
|
||||||
$installLog = 'C:\Logs\SFLD\DSCInstall.log'
|
$installLog = 'C:\Logs\SFLD\DSCInstall.log'
|
||||||
@@ -338,7 +364,23 @@ function Get-Snapshot {
|
|||||||
if ($task) { $consumeCredsTask = $true }
|
if ($task) { $consumeCredsTask = $true }
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
|
# Real completion signal: creds actually landed in HKLM. The scheduled
|
||||||
|
# task existing just means DSC scheduled it; this checks it RAN and
|
||||||
|
# populated the share creds that the logon enforcers depend on.
|
||||||
|
$credsPopulated = $false
|
||||||
|
$credsBase = 'HKLM:\SOFTWARE\GE\SFLD\Credentials'
|
||||||
|
if (Test-Path $credsBase) {
|
||||||
|
foreach ($entry in (Get-ChildItem -Path $credsBase -ErrorAction SilentlyContinue)) {
|
||||||
|
$p = Get-ItemProperty -Path $entry.PSPath -ErrorAction SilentlyContinue
|
||||||
|
if ($p -and $p.TargetHost -and $p.Username -and $p.Password) {
|
||||||
|
$credsPopulated = $true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$customScripts = Get-CustomScriptStatuses -DscInstallLog $installLog
|
$customScripts = Get-CustomScriptStatuses -DscInstallLog $installLog
|
||||||
|
$lockdown = Get-LockdownState
|
||||||
|
|
||||||
return [PSCustomObject]@{
|
return [PSCustomObject]@{
|
||||||
Function = $function
|
Function = $function
|
||||||
@@ -357,8 +399,11 @@ function Get-Snapshot {
|
|||||||
Phase4 = $customScripts
|
Phase4 = $customScripts
|
||||||
Phase5 = @{
|
Phase5 = @{
|
||||||
ConsumeCredsTask = $consumeCredsTask
|
ConsumeCredsTask = $consumeCredsTask
|
||||||
|
CredsPopulated = $credsPopulated
|
||||||
}
|
}
|
||||||
|
Phase6 = $lockdown
|
||||||
DscInstallComplete = $installComplete
|
DscInstallComplete = $installComplete
|
||||||
|
LockdownComplete = $lockdown.Complete
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,6 +532,16 @@ function Format-Snapshot {
|
|||||||
$lines += " $(Mk $Snap.Phase2.SfldRoot) SFLD reg key"
|
$lines += " $(Mk $Snap.Phase2.SfldRoot) SFLD reg key"
|
||||||
$lines += " $(Mk $Snap.Phase2.FunctionOk) Function set"
|
$lines += " $(Mk $Snap.Phase2.FunctionOk) Function set"
|
||||||
$lines += " $(Mk $Snap.Phase2.SasTokenOk) DSC SAS token configured"
|
$lines += " $(Mk $Snap.Phase2.SasTokenOk) DSC SAS token configured"
|
||||||
|
|
||||||
|
# Phase-1-done-but-Phase-2-stuck is the classic "tech needs to go
|
||||||
|
# set device category in Intune portal" state. Surface it loud
|
||||||
|
# rather than leaving the user staring at empty checkboxes.
|
||||||
|
$phase1Done = ($Snap.Phase1.AzureAdJoined -and $Snap.Phase1.IntuneEnrolled)
|
||||||
|
$phase2Done = ($Snap.Phase2.SfldRoot -and $Snap.Phase2.FunctionOk -and $Snap.Phase2.SasTokenOk)
|
||||||
|
if ($phase1Done -and -not $phase2Done) {
|
||||||
|
$lines += " --> ACTION: assign device category in Intune portal"
|
||||||
|
$lines += " (main / cmm / displaypcs / waxtrace)"
|
||||||
|
}
|
||||||
$lines += ""
|
$lines += ""
|
||||||
$lines += " Phase 3: DSC deployment + install"
|
$lines += " Phase 3: DSC deployment + install"
|
||||||
$lines += " $(Mk $Snap.Phase3.DeployLogExists) DSCDeployment.log present"
|
$lines += " $(Mk $Snap.Phase3.DeployLogExists) DSCDeployment.log present"
|
||||||
@@ -518,8 +573,13 @@ function Format-Snapshot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$lines += ""
|
$lines += ""
|
||||||
$lines += " Phase 5: Final"
|
$lines += " Phase 5: SFLD credentials"
|
||||||
$lines += " $(Mk $Snap.Phase5.ConsumeCredsTask) SFLD - Consume Credentials task"
|
$lines += " $(Mk $Snap.Phase5.ConsumeCredsTask) Consume Credentials task scheduled"
|
||||||
|
$lines += " $(Mk $Snap.Phase5.CredsPopulated) Share creds present in HKLM"
|
||||||
|
$lines += ""
|
||||||
|
$lines += " Phase 6: Lockdown"
|
||||||
|
$lines += " $(Mk $Snap.Phase6.AutologonShopfloor) Winlogon autologon = ShopFloor"
|
||||||
|
$lines += " $(Mk $Snap.Phase6.AdminRenamed) Administrator renamed -> SFLDAdmin"
|
||||||
} else {
|
} else {
|
||||||
$lines += ""
|
$lines += ""
|
||||||
$lines += " (DSC phases not applicable for $pcType)"
|
$lines += " (DSC phases not applicable for $pcType)"
|
||||||
@@ -581,10 +641,11 @@ function Wait-ForAnyKey {
|
|||||||
function Invoke-SetupComplete {
|
function Invoke-SetupComplete {
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host "========================================" -ForegroundColor Green
|
Write-Host "========================================" -ForegroundColor Green
|
||||||
Write-Host " Setup complete - no reboot needed" -ForegroundColor Green
|
Write-Host " Setup + lockdown complete" -ForegroundColor Green
|
||||||
Write-Host "========================================" -ForegroundColor Green
|
Write-Host "========================================" -ForegroundColor Green
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host "The post-reboot DSC install phase is finished. The device is ready."
|
Write-Host "DSC install is finished and the kiosk baseline has fully landed"
|
||||||
|
Write-Host "(Winlogon flipped to ShopFloor autologon, Administrator renamed)."
|
||||||
|
|
||||||
if ($AsTask) {
|
if ($AsTask) {
|
||||||
# Write completion marker so future logon-triggered runs exit
|
# Write completion marker so future logon-triggered runs exit
|
||||||
@@ -601,16 +662,26 @@ function Invoke-SetupComplete {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Machine number prompt only (startup items are auto-applied by
|
# Machine number prompt only (startup items are auto-applied by
|
||||||
# 06-OrganizeDesktop from the PC profile). Tech can re-open
|
# 06-OrganizeDesktop from the PC profile). Runs in SupportUser
|
||||||
# sync_intune.bat manually to see QR code for inventory.
|
# session while tech is still near the PC; skipped silently if
|
||||||
|
# UDC/eDNC already hold a real number (tech typed at PXE or
|
||||||
|
# restore-from-.reg already populated them).
|
||||||
if ($ConfigureScript -and (Test-Path -LiteralPath $ConfigureScript)) {
|
if ($ConfigureScript -and (Test-Path -LiteralPath $ConfigureScript)) {
|
||||||
try { & $ConfigureScript -MachineNumberOnly } catch { Write-Warning "Configure-PC failed: $_" }
|
try { & $ConfigureScript -MachineNumberOnly } catch { Write-Warning "Configure-PC failed: $_" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Reboot so Winlogon's new DefaultUserName=ShopFloor kicks in -
|
||||||
|
# autologon only fires at the logon boundary. Next boot brings up
|
||||||
|
# a clean ShopFloor session; this task will fire again for that
|
||||||
|
# user, see the marker, and exit in <1s.
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Rebooting in 10 seconds for ShopFloor autologon..." -ForegroundColor Yellow
|
||||||
|
& shutdown.exe /r /t 10
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($Unattended) {
|
if ($Unattended) {
|
||||||
Write-Host "(Unattended mode - exiting)"
|
Write-Host "(Unattended mode - exiting; reboot left to caller)"
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
Wait-ForAnyKey
|
Wait-ForAnyKey
|
||||||
@@ -721,8 +792,17 @@ try {
|
|||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host $qrText
|
Write-Host $qrText
|
||||||
|
|
||||||
# Final state: post-reboot install complete
|
# Final state: every phase landed. The gate is intentionally strict
|
||||||
if ($snap.DscInstallComplete) {
|
# because each piece is needed for the device to function:
|
||||||
|
# - DscInstallComplete: device-config.yaml apps + custom scripts ran
|
||||||
|
# - CredsPopulated: SFLD share creds in HKLM (Machine-Enforce,
|
||||||
|
# Acrobat-Enforce, CMM-Enforce all need these)
|
||||||
|
# - LockdownComplete: kiosk policy baseline + Winlogon flipped to
|
||||||
|
# ShopFloor autologon + admin renamed
|
||||||
|
# Display PCs skip this whole branch via $skipDsc above.
|
||||||
|
if ($snap.DscInstallComplete -and
|
||||||
|
$snap.Phase5.CredsPopulated -and
|
||||||
|
$snap.LockdownComplete) {
|
||||||
Invoke-SetupComplete
|
Invoke-SetupComplete
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user