Shopfloor sync_intune + Set-MachineNumber hardening
Long debugging round on the shopfloor test PC with several overlapping
bugs. This commit folds all the fixes together.
sync_intune.bat
- Slim down to an elevation thunk that launches a NEW elevated PS
window via Start-Process -Verb RunAs (with -NoExit so the window
doesn't vanish on error). All UI now lives in the PS monitor, not
mixed into the cmd launcher.
- Goto-based control flow. Earlier version had nested if (...) blocks
with literal parens inside echo lines (e.g. "wrappers (Install-eDNC,
...etc)."); cmd parses if-blocks by counting parens character-by-
character, so the ")" in "etc)." closed the outer block early and
the leftover "." threw ". was unexpected at this time.", crashing
the elevated cmd /c window before pause ran.
- Multi-location Monitor-IntuneProgress.ps1 lookup so the user's
quick-test workflow (drop both files on the desktop) works without
manually editing the hardcoded path. Lookup order:
1. %~dp0lib\Monitor-IntuneProgress.ps1
2. %~dp0Monitor-IntuneProgress.ps1
3. C:\Users\SupportUser\Desktop\Monitor-IntuneProgress.ps1
4. C:\Enrollment\shopfloor-setup\Shopfloor\lib\Monitor-IntuneProgress.ps1
- Prints "Launching: <path>" as its first line so you can see which
copy it actually loaded. This caught a bug where a stale desktop
copy was shadowing the canonical file via fallback #2.
Set-MachineNumber.bat
- Same multi-location lookup pattern. Old version used
%~dp0Set-MachineNumber.ps1 and bombed when the bat was copied to
the desktop without its .ps1 sibling.
- Goto-based dispatch, no nested parens, for the same parser reason.
Monitor-IntuneProgress.ps1
- Start-Transcript at the top, writing to C:\Logs\SFLD\ (falls back
to %TEMP% if C:\Logs\SFLD isn't writable yet) with a startup banner
including a timestamp. Every run leaves a captured trace.
- Main polling loop wrapped in try/catch/finally. Unhandled exceptions
print a red report with type, message, position, and stack trace,
then block on Wait-ForAnyKey so the window can't auto-close on a
silent crash.
- Console window resize at startup via $Host.UI.RawUI.WindowSize /
BufferSize, wrapped in try/catch (Windows Terminal ignores it, but
classic conhost honors it).
- Clear-KeyBuffer / Read-SingleKey / Wait-ForAnyKey helpers. Drain any
buffered keystrokes from the polling loop before each prompt so an
accidental keypress can't satisfy a pause prematurely.
- Invoke-SetupComplete / Invoke-RebootPrompt final-state handlers.
The REBOOT REQUIRED branch now shows a yellow 3-line header, a
four-line explanation, and a cyan "Press Y to reboot now, or N to
cancel:" prompt via Read-SingleKey @('Y','N'). Y triggers
Restart-Computer -Force (with shutdown.exe fallback), N falls
through to Wait-ForAnyKey.
- Display order: status table FIRST, QR LAST. The cursor ends below
the QR so the viewport always follows it - keeps the QR on screen
regardless of window height. Works on both classic conhost and
Windows Terminal (neither reliably honors programmatic resize).
- Half-block QR renderer: walks QRCoder's ModuleMatrix directly and
emits U+2580 / U+2584 / U+2588 / space, one output line per two
matrix rows. Halves the rendered height vs AsciiQRCode full-block.
Quiet zone added manually via $pad=4 since QRCoder's ModuleMatrix
doesn't include one. Trade-off: may not be perfectly square on all
fonts, but the user accepted that for the smaller footprint after
multiple iterations comparing full-block vs half-block vs PNG popup.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,81 +1,58 @@
|
||||
@echo off
|
||||
REM sync_intune.bat - Thin launcher for Monitor-IntuneProgress.ps1
|
||||
REM sync_intune.bat - Launches Monitor-IntuneProgress.ps1 in an elevated
|
||||
REM PowerShell window.
|
||||
REM
|
||||
REM All polling, status display, sync triggering, and reboot detection lives in
|
||||
REM the PowerShell monitor. This .bat just handles:
|
||||
REM 1. Self-elevate to admin
|
||||
REM 2. Invoke the monitor (which renders QR + 5-phase status table)
|
||||
REM 3. Branch on the monitor's exit code:
|
||||
REM 0 = post-reboot install complete, "done" message and exit
|
||||
REM 2 = pre-reboot deployment done, prompt for reboot
|
||||
REM else = error, pause so the user can read it
|
||||
REM This .bat exists only because Windows doesn't give .ps1 files a
|
||||
REM "double-click to run as admin" verb. It finds the monitor script,
|
||||
REM then calls Start-Process -Verb RunAs to open a NEW elevated
|
||||
REM PowerShell console hosting it. The cmd window that ran this .bat
|
||||
REM exits immediately afterwards - all UI (QR code, status table, reboot
|
||||
REM prompt) lives in the separate PS window.
|
||||
REM
|
||||
REM The monitor lives at C:\Enrollment\shopfloor-setup\Shopfloor\Monitor-IntuneProgress.ps1.
|
||||
REM This .bat gets copied to the user's desktop by Run-ShopfloorSetup.ps1, so
|
||||
REM %~dp0 doesn't necessarily point at the shopfloor-setup tree - we use the
|
||||
REM absolute path to find the monitor instead.
|
||||
REM Monitor lookup order:
|
||||
REM 1. %~dp0lib\Monitor-IntuneProgress.ps1
|
||||
REM - repo layout / canonical on-disk layout (bat in Shopfloor/, .ps1 in Shopfloor/lib/)
|
||||
REM 2. %~dp0Monitor-IntuneProgress.ps1
|
||||
REM - both files dropped in same dir (quick-test on desktop)
|
||||
REM 3. C:\Users\SupportUser\Desktop\Monitor-IntuneProgress.ps1
|
||||
REM - dispatcher-dropped, if this .bat is being invoked from elsewhere
|
||||
REM 4. C:\Enrollment\shopfloor-setup\Shopfloor\lib\Monitor-IntuneProgress.ps1
|
||||
REM - canonical enrollment staging copy
|
||||
REM
|
||||
REM -NoExit on the inner PowerShell keeps the new window open after the
|
||||
REM script finishes so the user can read any final output or error before
|
||||
REM the window closes.
|
||||
REM
|
||||
REM Goto-based dispatch - no nested if blocks, no literal parens in echo
|
||||
REM lines. CMD parses "if (...)" blocks by counting parens and will silently
|
||||
REM eat any "(" or ")" inside an echo, so keeping the flow flat avoids that
|
||||
REM class of syntax bomb entirely.
|
||||
|
||||
setlocal
|
||||
title Intune Policy Sync
|
||||
title Intune Policy Sync Launcher
|
||||
|
||||
set "MONITOR=%~dp0lib\Monitor-IntuneProgress.ps1"
|
||||
if exist "%MONITOR%" goto :launch
|
||||
|
||||
set "MONITOR=%~dp0Monitor-IntuneProgress.ps1"
|
||||
if exist "%MONITOR%" goto :launch
|
||||
|
||||
set "MONITOR=C:\Users\SupportUser\Desktop\Monitor-IntuneProgress.ps1"
|
||||
if exist "%MONITOR%" goto :launch
|
||||
|
||||
REM Monitor lives under lib\ to keep it OUT of the dispatcher's baseline scan.
|
||||
REM Run-ShopfloorSetup.ps1 does Get-ChildItem -Filter "*.ps1" on the Shopfloor\
|
||||
REM dir (non-recursive) and runs every script it finds - if Monitor-IntuneProgress
|
||||
REM lived there, the dispatcher would invoke it as a baseline script and hang the
|
||||
REM whole shopfloor setup forever (it's an infinite poll loop, never returns).
|
||||
set "MONITOR=C:\Enrollment\shopfloor-setup\Shopfloor\lib\Monitor-IntuneProgress.ps1"
|
||||
if exist "%MONITOR%" goto :launch
|
||||
|
||||
REM Self-elevate to administrator
|
||||
net session >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
powershell -Command "Start-Process '%~f0' -Verb RunAs"
|
||||
exit /b
|
||||
)
|
||||
|
||||
if not exist "%MONITOR%" (
|
||||
echo ERROR: Monitor not found at:
|
||||
echo %MONITOR%
|
||||
echo.
|
||||
echo Was the shopfloor-setup tree staged correctly?
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%MONITOR%"
|
||||
set "MONITOR_EXIT=%errorlevel%"
|
||||
|
||||
echo ERROR: Monitor-IntuneProgress.ps1 not found in any of:
|
||||
echo %~dp0lib\Monitor-IntuneProgress.ps1
|
||||
echo %~dp0Monitor-IntuneProgress.ps1
|
||||
echo C:\Users\SupportUser\Desktop\Monitor-IntuneProgress.ps1
|
||||
echo C:\Enrollment\shopfloor-setup\Shopfloor\lib\Monitor-IntuneProgress.ps1
|
||||
echo.
|
||||
if "%MONITOR_EXIT%"=="0" (
|
||||
echo ========================================
|
||||
echo Setup complete - no reboot needed
|
||||
echo ========================================
|
||||
echo.
|
||||
echo The post-reboot DSC install phase is finished. The device is ready.
|
||||
echo.
|
||||
pause
|
||||
exit /b 0
|
||||
)
|
||||
|
||||
if "%MONITOR_EXIT%"=="2" (
|
||||
echo ========================================
|
||||
echo REBOOT REQUIRED
|
||||
echo ========================================
|
||||
echo.
|
||||
echo The pre-reboot deployment phase is complete. You must reboot now to
|
||||
echo start the post-reboot DSC install phase, which downloads device-config.yaml
|
||||
echo and runs the per-app wrappers (Install-eDNC, Install-UDC, Install-VCRedists,
|
||||
echo Install-OpenText, etc).
|
||||
echo.
|
||||
choice /c YN /m "Reboot now"
|
||||
if errorlevel 2 (
|
||||
echo Cancelled - reboot manually when ready.
|
||||
pause
|
||||
exit /b 0
|
||||
)
|
||||
shutdown /r /t 5
|
||||
exit /b 0
|
||||
)
|
||||
|
||||
echo ERROR: Monitor exited with code %MONITOR_EXIT%
|
||||
pause
|
||||
exit /b %MONITOR_EXIT%
|
||||
exit /b 1
|
||||
|
||||
:launch
|
||||
echo Launching: %MONITOR%
|
||||
powershell -NoProfile -Command "Start-Process powershell.exe -Verb RunAs -ArgumentList '-NoProfile','-NoExit','-ExecutionPolicy','Bypass','-File','%MONITOR%'"
|
||||
exit /b
|
||||
|
||||
Reference in New Issue
Block a user