From 3385bc87aad13c1ea386f075fd943c03ebb30c11 Mon Sep 17 00:00:00 2001 From: cproudlock Date: Fri, 15 May 2026 07:39:20 -0400 Subject: [PATCH] Monitor + imaging: per-phase sub-stages within idx=7 Monitor's Get-Snapshot already tracks Phase 1-5 (Intune Registration, Device Configuration, Software Deployment, Credentials, Lockdown). The webapp dashboard only saw a single idx=7 push for the entire post-PPKG / pre-lockdown window, so the friendly label couldn't reflect "where is this bay actually". Operator looking at the dashboard had no idea whether to assign category or hit ARTS for lockdown next. Monitor now pushes additional idx=7 entries as it crosses Phase boundaries: - On DeviceId capture: "Intune Device ID captured" (existing) - On Phase 2 done (SFLD policy delivered = category was assigned): "Phase 2 SFLD policy delivered (device configuration)" - On Phase 1-4 all complete: "Phases 1-4 complete - ready for lockdown (ARTS request)" - On lockdown done: idx=8 (existing) imaging.html maps the stage_string substring to friendly labels: - default idx=7 -> "Registered - assign category" - 'sfld policy' / 'phase 2' -> "Phase 2 - device configuration" - 'credentials' / 'phase 4' -> "Phase 3 / 4 - DSC + credentials" - 'ready for lockdown' / 'request lockdown' -> "Ready - request lockdown" (hint: click ARTS request) Operator now knows exactly when to act vs when to wait. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Shopfloor/lib/Monitor-IntuneProgress.ps1 | 31 +++++++++++++++++++ webapp/templates/imaging.html | 20 ++++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/playbook/shopfloor-setup/Shopfloor/lib/Monitor-IntuneProgress.ps1 b/playbook/shopfloor-setup/Shopfloor/lib/Monitor-IntuneProgress.ps1 index 30ba257..7821cfb 100644 --- a/playbook/shopfloor-setup/Shopfloor/lib/Monitor-IntuneProgress.ps1 +++ b/playbook/shopfloor-setup/Shopfloor/lib/Monitor-IntuneProgress.ps1 @@ -186,6 +186,8 @@ $script:cache = @{ EnrollmentId = $null DeviceId = $null DeviceIdReported = $false + SfldPolicyPushed = $false + CredsReadyPushed = $false LockdownCompletePushed = $false InternetAccessDeleted = $false } @@ -1240,6 +1242,35 @@ try { while ($true) { $snap = Get-Snapshot + # Push sub-stage transitions to PXE dashboard so the operator sees + # whether the bay is waiting on category assignment, or has + # progressed past it. idx stays 7 across all three; the stage + # string drives the friendly label in imaging.html. + if (-not $script:cache.SfldPolicyPushed -and + $snap.Phase2.SfldRoot -and $snap.Phase2.FunctionOk -and $snap.Phase2.SasTokenOk) { + if (Get-Command Send-PxeStatus -ErrorAction SilentlyContinue) { + try { + Send-PxeStatus -Stage 'Monitor-IntuneProgress: Phase 2 SFLD policy delivered (device configuration)' ` + -StageIndex 7 -StageTotal 8 ` + -IntuneDeviceId $script:cache.DeviceId -ErrorAction SilentlyContinue + $script:cache.SfldPolicyPushed = $true + } catch {} + } + } + if (-not $script:cache.CredsReadyPushed -and + $snap.Phase4.CredsPopulated -and + $snap.Phase3.InstallComplete -and + $snap.Phase2.SfldRoot -and $snap.Phase2.FunctionOk -and $snap.Phase2.SasTokenOk) { + if (Get-Command Send-PxeStatus -ErrorAction SilentlyContinue) { + try { + Send-PxeStatus -Stage 'Monitor-IntuneProgress: Phases 1-4 complete - ready for lockdown (ARTS request)' ` + -StageIndex 7 -StageTotal 8 ` + -IntuneDeviceId $script:cache.DeviceId -ErrorAction SilentlyContinue + $script:cache.CredsReadyPushed = $true + } catch {} + } + } + # Retry QR code every cycle until it actually renders. dsregcmd # may report AzureAdJoined=YES before DeviceId is populated, so # a single-shot refresh misses the window. diff --git a/webapp/templates/imaging.html b/webapp/templates/imaging.html index dc39664..c58f177 100644 --- a/webapp/templates/imaging.html +++ b/webapp/templates/imaging.html @@ -49,8 +49,8 @@ window.addEventListener('DOMContentLoaded', scheduleImagingReload); 4: ('Apps installed', 'Type-specific scripts complete. Preparing for Intune enrollment.'), 5: ('Enrolling in Intune', 'PPKG installing - device joining Azure AD + Intune. ~5-10 min, reboot to follow.'), 6: ('Waiting on first Intune sync','Post-PPKG settle (~120s). Triggering Schedule #3 sync repeatedly.'), - 7: ('Awaiting Intune lockdown', - 'Device ID captured. If Device Category is NOT yet set in Intune, click "set category" first. Bay then waits for the Intune-driven LAPS-prompt reboot to apply the lockdown configuration.'), + 7: ('Registered - assign category', + 'Phase 1 (Intune Registration) complete. Click "set category" in the Intune portal to drop the bay into the right config-profile group.'), 8: ('Imaging complete', 'Lockdown applied. Bay rebooted into ShopFloor session. Ready for production.') } %} @@ -63,6 +63,22 @@ window.addEventListener('DOMContentLoaded', scheduleImagingReload); {% set is_done = s.status == 'succeeded' %} {% set border = 'danger' if is_failed else ('success' if is_done else 'primary') %} {% set friendly = stage_labels.get(stage_idx, ('Stage ' ~ stage_idx, '')) %} + {# Stage 7 fans out by sub-phase. Monitor pushes different stage #} + {# strings as it crosses each Phase 1-4 boundary. Swap friendly #} + {# label based on which keyword shows up. #} + {% if stage_idx == 7 and s.current_stage %} + {% set _cs = s.current_stage|lower %} + {% if 'ready for lockdown' in _cs or 'request lockdown' in _cs %} + {% set friendly = ('Ready - request lockdown', + 'Phase 1-4 all complete (Registration, Device Config, Software Deploy, Credentials). Click "ARTS request" to initiate the lockdown workflow.') %} + {% elif 'credentials' in _cs or 'phase 4' in _cs %} + {% set friendly = ('Phase 3 / 4 - DSC + credentials', + 'SFLD policy delivered, DSC pulling device-config.yaml + running per-app wrappers. SFLD share creds populating.') %} + {% elif 'sfld policy' in _cs or 'phase 2' in _cs or 'device configuration' in _cs %} + {% set friendly = ('Phase 2 - device configuration', + 'Category was assigned in Intune. SFLD ConfigurationProfile (Function + SasToken) has delivered. DSC kicking off next.') %} + {% endif %} + {% endif %}