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:
@@ -54,6 +54,52 @@ param(
|
|||||||
[int]$RetriggerMinutes = 3
|
[int]$RetriggerMinutes = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Transcript logging - writes EVERYTHING the script sees and writes to a log
|
||||||
|
# file so we can diagnose auto-close / crash issues after the fact. Stored
|
||||||
|
# under C:\Logs\SFLD\ alongside the DSC logs. If the dir doesn't exist yet
|
||||||
|
# we fall back to %TEMP% so logging never itself fails the run.
|
||||||
|
# ============================================================================
|
||||||
|
$logDir = 'C:\Logs\SFLD'
|
||||||
|
if (-not (Test-Path $logDir)) {
|
||||||
|
try { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } catch { $logDir = $env:TEMP }
|
||||||
|
}
|
||||||
|
$transcriptPath = Join-Path $logDir 'sync_intune_transcript.txt'
|
||||||
|
try { Start-Transcript -Path $transcriptPath -Append -Force | Out-Null } catch {}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "=== Monitor-IntuneProgress.ps1 starting $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') ==="
|
||||||
|
Write-Host "Transcript: $transcriptPath"
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Console resize - the QR code is ~30 lines and the 5-phase status table is
|
||||||
|
# ~25 lines. Default cmd window is 25 rows, so the QR scrolls off the top.
|
||||||
|
# Bump the window (and buffer) to ~58 rows so the whole frame fits at once.
|
||||||
|
# Wrapped in try/catch because some hosts (Windows Terminal, ISE) don't honor
|
||||||
|
# WindowSize/BufferSize changes - in that case we just live with the default.
|
||||||
|
# ============================================================================
|
||||||
|
try {
|
||||||
|
$rui = $Host.UI.RawUI
|
||||||
|
$maxH = $rui.MaxPhysicalWindowSize.Height
|
||||||
|
$targetWindow = [Math]::Min(58, [int]$maxH)
|
||||||
|
$targetBuffer = [Math]::Max($targetWindow, 200)
|
||||||
|
|
||||||
|
# Buffer must be set FIRST and must be >= window. Width must not shrink
|
||||||
|
# below current window width or SetBufferSize throws.
|
||||||
|
$bs = $rui.BufferSize
|
||||||
|
if ($bs.Height -lt $targetBuffer) {
|
||||||
|
$bs.Height = $targetBuffer
|
||||||
|
$rui.BufferSize = $bs
|
||||||
|
}
|
||||||
|
|
||||||
|
$ws = $rui.WindowSize
|
||||||
|
if ($ws.Height -lt $targetWindow) {
|
||||||
|
$ws.Height = $targetWindow
|
||||||
|
$rui.WindowSize = $ws
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Helpers
|
# Helpers
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -306,6 +352,16 @@ function Invoke-IntuneSync {
|
|||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# QR code (cached as text - generate once, re-print on every redraw)
|
# QR code (cached as text - generate once, re-print on every redraw)
|
||||||
|
#
|
||||||
|
# Half-block renderer: each output character represents 1 QR module wide and
|
||||||
|
# 2 QR modules tall, using Unicode half-block characters:
|
||||||
|
# U+2588 ([block]) = both top and bottom modules set
|
||||||
|
# U+2580 ([upper]) = only top module set
|
||||||
|
# U+2584 ([lower]) = only bottom module set
|
||||||
|
# space = neither set
|
||||||
|
# Cuts QR height roughly in half vs AsciiQRCode full-block rendering. May
|
||||||
|
# render slightly non-square on fonts where char cell aspect isn't exactly
|
||||||
|
# 1:2, but it's half the real estate and the user accepted that trade-off.
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
function Build-QRCodeText {
|
function Build-QRCodeText {
|
||||||
$lines = @()
|
$lines = @()
|
||||||
@@ -324,20 +380,51 @@ function Build-QRCodeText {
|
|||||||
$lines += ""
|
$lines += ""
|
||||||
|
|
||||||
$dllPath = 'C:\Enrollment\shopfloor-setup\Shopfloor\QRCoder.dll'
|
$dllPath = 'C:\Enrollment\shopfloor-setup\Shopfloor\QRCoder.dll'
|
||||||
if (Test-Path $dllPath) {
|
if (-not (Test-Path $dllPath)) {
|
||||||
|
$lines += "(QRCoder.dll not found at $dllPath - skipping QR code)"
|
||||||
|
return ($lines -join "`n")
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Add-Type -Path $dllPath
|
Add-Type -Path $dllPath
|
||||||
$gen = New-Object QRCoder.QRCodeGenerator
|
$gen = New-Object QRCoder.QRCodeGenerator
|
||||||
$data = $gen.CreateQrCode($deviceId, [QRCoder.QRCodeGenerator+ECCLevel]::L)
|
$data = $gen.CreateQrCode($deviceId, [QRCoder.QRCodeGenerator+ECCLevel]::L)
|
||||||
$ascii = New-Object QRCoder.AsciiQRCode($data)
|
|
||||||
$qr = $ascii.GetGraphic(1, [char]0x2588 + [char]0x2588, ' ')
|
# Walk the module matrix. QRCoder's ModuleMatrix does NOT include a
|
||||||
$lines += $qr -split "`r?`n"
|
# quiet zone by default (confirmed empirically - for a UUID payload
|
||||||
|
# at ECC L, ModuleMatrix is 29x29 for QR3 data only). We add a
|
||||||
|
# 4-module quiet zone manually via $pad below.
|
||||||
|
$matrix = $data.ModuleMatrix
|
||||||
|
$size = $matrix.Count
|
||||||
|
$pad = 4
|
||||||
|
$total = $size + 2 * $pad
|
||||||
|
|
||||||
|
$upper = [char]0x2580
|
||||||
|
$lower = [char]0x2584
|
||||||
|
$full = [char]0x2588
|
||||||
|
$left = ' ' # 8-space indent matches the old layout
|
||||||
|
|
||||||
|
for ($y = 0; $y -lt $total; $y += 2) {
|
||||||
|
$sb = New-Object System.Text.StringBuilder
|
||||||
|
[void]$sb.Append($left)
|
||||||
|
for ($x = 0; $x -lt $total; $x++) {
|
||||||
|
$mx = $x - $pad
|
||||||
|
$my1 = $y - $pad
|
||||||
|
$my2 = $y + 1 - $pad
|
||||||
|
|
||||||
|
$top = ($my1 -ge 0 -and $my1 -lt $size -and $mx -ge 0 -and $mx -lt $size -and $matrix[$my1].Get($mx))
|
||||||
|
$bot = ($my2 -ge 0 -and $my2 -lt $size -and $mx -ge 0 -and $mx -lt $size -and $matrix[$my2].Get($mx))
|
||||||
|
|
||||||
|
if ($top -and $bot) { [void]$sb.Append($full) }
|
||||||
|
elseif ($top) { [void]$sb.Append($upper) }
|
||||||
|
elseif ($bot) { [void]$sb.Append($lower) }
|
||||||
|
else { [void]$sb.Append(' ') }
|
||||||
|
}
|
||||||
|
$lines += $sb.ToString()
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
$lines += "(QR code generation failed: $($_.Exception.Message))"
|
$lines += "(QR code generation failed: $($_.Exception.Message))"
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
$lines += "(QRCoder.dll not found at $dllPath - skipping QR code)"
|
|
||||||
}
|
|
||||||
return ($lines -join "`n")
|
return ($lines -join "`n")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,8 +495,105 @@ function Format-Snapshot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Main loop
|
# Key buffer drain + single-keypress reader - used before final prompts so
|
||||||
|
# any keystrokes the user tapped during the polling loop don't satisfy the
|
||||||
|
# prompt prematurely. Wrapped in try/catch because [Console]::KeyAvailable
|
||||||
|
# throws in non-interactive hosts (ISE, remoting).
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
function Clear-KeyBuffer {
|
||||||
|
try {
|
||||||
|
while ([Console]::KeyAvailable) { [void][Console]::ReadKey($true) }
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Read-SingleKey {
|
||||||
|
param([string[]]$ValidKeys)
|
||||||
|
Clear-KeyBuffer
|
||||||
|
while ($true) {
|
||||||
|
try {
|
||||||
|
$k = [Console]::ReadKey($true)
|
||||||
|
$char = ([string]$k.KeyChar).ToUpper()
|
||||||
|
if ($ValidKeys -contains $char) { return $char }
|
||||||
|
} catch {
|
||||||
|
# Fallback for hosts that don't expose ReadKey - use Read-Host.
|
||||||
|
$ans = (Read-Host ("Enter one of: " + ($ValidKeys -join '/'))).Trim().ToUpper()
|
||||||
|
if ($ValidKeys -contains $ans) { return $ans }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Wait-ForAnyKey {
|
||||||
|
param([string]$Prompt = 'Press any key to close this window...')
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host $Prompt
|
||||||
|
Clear-KeyBuffer
|
||||||
|
try {
|
||||||
|
[void][Console]::ReadKey($true)
|
||||||
|
} catch {
|
||||||
|
[void](Read-Host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Final-state handlers - called from the main loop when either the
|
||||||
|
# post-reboot install has completed, or the pre-reboot deployment is done
|
||||||
|
# and a reboot is required. Each handler draws its own final frame, prompts
|
||||||
|
# the user, then exits.
|
||||||
|
# ============================================================================
|
||||||
|
function Invoke-SetupComplete {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "========================================" -ForegroundColor Green
|
||||||
|
Write-Host " Setup complete - no reboot needed" -ForegroundColor Green
|
||||||
|
Write-Host "========================================" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "The post-reboot DSC install phase is finished. The device is ready."
|
||||||
|
Wait-ForAnyKey
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-RebootPrompt {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "========================================" -ForegroundColor Yellow
|
||||||
|
Write-Host " REBOOT REQUIRED" -ForegroundColor Yellow
|
||||||
|
Write-Host "========================================" -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "The pre-reboot deployment phase is complete. You must reboot now"
|
||||||
|
Write-Host "to start the post-reboot DSC install phase, which downloads"
|
||||||
|
Write-Host "device-config.yaml and runs the per-app wrappers: Install-eDNC,"
|
||||||
|
Write-Host "Install-UDC, Install-VCRedists, Install-OpenText, and so on."
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Press Y to reboot now, or N to cancel: " -NoNewline -ForegroundColor Cyan
|
||||||
|
$ans = Read-SingleKey -ValidKeys @('Y', 'N')
|
||||||
|
Write-Host $ans
|
||||||
|
if ($ans -eq 'Y') {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Rebooting in 5 seconds..." -ForegroundColor Cyan
|
||||||
|
Start-Sleep -Seconds 5
|
||||||
|
try {
|
||||||
|
Restart-Computer -Force -ErrorAction Stop
|
||||||
|
} catch {
|
||||||
|
Write-Host "Restart-Computer failed: $($_.Exception.Message)" -ForegroundColor Red
|
||||||
|
Write-Host "Falling back to shutdown.exe /r /t 5..."
|
||||||
|
& shutdown.exe /r /t 5
|
||||||
|
}
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Cancelled - reboot manually when ready." -ForegroundColor Yellow
|
||||||
|
Wait-ForAnyKey
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Main loop
|
||||||
|
#
|
||||||
|
# Display order is intentional: status table FIRST, QR LAST. The cursor
|
||||||
|
# ends below the QR, so the viewport follows it and keeps the QR visible
|
||||||
|
# regardless of window size. This works in classic conhost and Windows
|
||||||
|
# Terminal - neither reliably honors programmatic window resize, so we
|
||||||
|
# solve it by controlling cursor position instead.
|
||||||
|
# ============================================================================
|
||||||
|
try {
|
||||||
$qrText = Build-QRCodeText
|
$qrText = Build-QRCodeText
|
||||||
Invoke-IntuneSync
|
Invoke-IntuneSync
|
||||||
$lastSync = Get-Date
|
$lastSync = Get-Date
|
||||||
@@ -419,25 +603,22 @@ while ($true) {
|
|||||||
$snap = Get-Snapshot
|
$snap = Get-Snapshot
|
||||||
|
|
||||||
Clear-Host
|
Clear-Host
|
||||||
Write-Host $qrText
|
Write-Host "=== Monitor running - transcript: $transcriptPath ===" -ForegroundColor DarkGray
|
||||||
Write-Host ""
|
|
||||||
foreach ($l in (Format-Snapshot -Snap $snap -LastSync $lastSync -NextRetrigger $nextRetrigger)) {
|
foreach ($l in (Format-Snapshot -Snap $snap -LastSync $lastSync -NextRetrigger $nextRetrigger)) {
|
||||||
Write-Host $l
|
Write-Host $l
|
||||||
}
|
}
|
||||||
|
|
||||||
# Final state: post-reboot install complete -> exit clean
|
|
||||||
if ($snap.DscInstallComplete) {
|
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host "All milestones reached. Setup complete." -ForegroundColor Green
|
Write-Host $qrText
|
||||||
exit 0
|
|
||||||
|
# Final state: post-reboot install complete
|
||||||
|
if ($snap.DscInstallComplete) {
|
||||||
|
Invoke-SetupComplete
|
||||||
}
|
}
|
||||||
|
|
||||||
# Reboot check (boot-loop-safe)
|
# Reboot check (boot-loop-safe)
|
||||||
$rebootState = Test-RebootState
|
$rebootState = Test-RebootState
|
||||||
if ($rebootState -eq 'needed') {
|
if ($rebootState -eq 'needed') {
|
||||||
Write-Host ""
|
Invoke-RebootPrompt
|
||||||
Write-Host "Pre-reboot deployment phase complete - REBOOT REQUIRED" -ForegroundColor Yellow
|
|
||||||
exit 2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Re-trigger sync periodically
|
# Re-trigger sync periodically
|
||||||
@@ -451,3 +632,28 @@ while ($true) {
|
|||||||
|
|
||||||
Start-Sleep -Seconds $PollSecs
|
Start-Sleep -Seconds $PollSecs
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
# Any unhandled exception in the main loop lands here. Write the error
|
||||||
|
# into the transcript and then block on a keypress so the PS window
|
||||||
|
# doesn't auto-close before the user can read it.
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "=====================================================" -ForegroundColor Red
|
||||||
|
Write-Host " UNHANDLED ERROR in Monitor-IntuneProgress.ps1" -ForegroundColor Red
|
||||||
|
Write-Host "=====================================================" -ForegroundColor Red
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Message : $($_.Exception.Message)" -ForegroundColor Red
|
||||||
|
Write-Host "Type : $($_.Exception.GetType().FullName)" -ForegroundColor Red
|
||||||
|
Write-Host "At : $($_.InvocationInfo.PositionMessage)" -ForegroundColor Red
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Stack trace:" -ForegroundColor Red
|
||||||
|
Write-Host $_.ScriptStackTrace -ForegroundColor Red
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Full transcript: $transcriptPath"
|
||||||
|
Wait-ForAnyKey
|
||||||
|
try { Stop-Transcript | Out-Null } catch {}
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
try { Stop-Transcript | Out-Null } catch {}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,81 +1,58 @@
|
|||||||
@echo off
|
@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
|
||||||
REM All polling, status display, sync triggering, and reboot detection lives in
|
REM This .bat exists only because Windows doesn't give .ps1 files a
|
||||||
REM the PowerShell monitor. This .bat just handles:
|
REM "double-click to run as admin" verb. It finds the monitor script,
|
||||||
REM 1. Self-elevate to admin
|
REM then calls Start-Process -Verb RunAs to open a NEW elevated
|
||||||
REM 2. Invoke the monitor (which renders QR + 5-phase status table)
|
REM PowerShell console hosting it. The cmd window that ran this .bat
|
||||||
REM 3. Branch on the monitor's exit code:
|
REM exits immediately afterwards - all UI (QR code, status table, reboot
|
||||||
REM 0 = post-reboot install complete, "done" message and exit
|
REM prompt) lives in the separate PS window.
|
||||||
REM 2 = pre-reboot deployment done, prompt for reboot
|
|
||||||
REM else = error, pause so the user can read it
|
|
||||||
REM
|
REM
|
||||||
REM The monitor lives at C:\Enrollment\shopfloor-setup\Shopfloor\Monitor-IntuneProgress.ps1.
|
REM Monitor lookup order:
|
||||||
REM This .bat gets copied to the user's desktop by Run-ShopfloorSetup.ps1, so
|
REM 1. %~dp0lib\Monitor-IntuneProgress.ps1
|
||||||
REM %~dp0 doesn't necessarily point at the shopfloor-setup tree - we use the
|
REM - repo layout / canonical on-disk layout (bat in Shopfloor/, .ps1 in Shopfloor/lib/)
|
||||||
REM absolute path to find the monitor instead.
|
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
|
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"
|
set "MONITOR=C:\Enrollment\shopfloor-setup\Shopfloor\lib\Monitor-IntuneProgress.ps1"
|
||||||
|
if exist "%MONITOR%" goto :launch
|
||||||
|
|
||||||
REM Self-elevate to administrator
|
echo ERROR: Monitor-IntuneProgress.ps1 not found in any of:
|
||||||
net session >nul 2>&1
|
echo %~dp0lib\Monitor-IntuneProgress.ps1
|
||||||
if errorlevel 1 (
|
echo %~dp0Monitor-IntuneProgress.ps1
|
||||||
powershell -Command "Start-Process '%~f0' -Verb RunAs"
|
echo C:\Users\SupportUser\Desktop\Monitor-IntuneProgress.ps1
|
||||||
exit /b
|
echo C:\Enrollment\shopfloor-setup\Shopfloor\lib\Monitor-IntuneProgress.ps1
|
||||||
)
|
|
||||||
|
|
||||||
if not exist "%MONITOR%" (
|
|
||||||
echo ERROR: Monitor not found at:
|
|
||||||
echo %MONITOR%
|
|
||||||
echo.
|
echo.
|
||||||
echo Was the shopfloor-setup tree staged correctly?
|
|
||||||
pause
|
pause
|
||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
|
||||||
|
|
||||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%MONITOR%"
|
:launch
|
||||||
set "MONITOR_EXIT=%errorlevel%"
|
echo Launching: %MONITOR%
|
||||||
|
powershell -NoProfile -Command "Start-Process powershell.exe -Verb RunAs -ArgumentList '-NoProfile','-NoExit','-ExecutionPolicy','Bypass','-File','%MONITOR%'"
|
||||||
echo.
|
exit /b
|
||||||
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%
|
|
||||||
|
|||||||
@@ -1,6 +1,38 @@
|
|||||||
@echo off
|
@echo off
|
||||||
REM Set-MachineNumber.bat - Wrapper for Set-MachineNumber.ps1
|
REM Set-MachineNumber.bat - Wrapper for Set-MachineNumber.ps1
|
||||||
REM Runs the PowerShell helper with bypass execution policy so a double-click
|
REM
|
||||||
REM from the desktop just works.
|
REM Looks for the .ps1 in three places, in order:
|
||||||
|
REM 1. %~dp0Set-MachineNumber.ps1
|
||||||
|
REM - .bat and .ps1 side-by-side (normal desktop-copied case, repo layout)
|
||||||
|
REM 2. C:\Users\SupportUser\Desktop\Set-MachineNumber.ps1
|
||||||
|
REM - dispatcher-copied location, if this .bat lives somewhere else
|
||||||
|
REM 3. C:\Enrollment\shopfloor-setup\Standard\Set-MachineNumber.ps1
|
||||||
|
REM - canonical enrollment staging copy
|
||||||
|
REM
|
||||||
|
REM Goto-based dispatch - no nested if blocks, no literal parens in echo lines.
|
||||||
|
REM CMD parses "if (...)" blocks by counting parens and will silently eat any
|
||||||
|
REM "(" or ")" inside an echo, so keeping the flow flat avoids that class of
|
||||||
|
REM syntax bomb entirely.
|
||||||
|
|
||||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0Set-MachineNumber.ps1"
|
setlocal
|
||||||
|
set "PS1=%~dp0Set-MachineNumber.ps1"
|
||||||
|
if exist "%PS1%" goto :run
|
||||||
|
|
||||||
|
set "PS1=C:\Users\SupportUser\Desktop\Set-MachineNumber.ps1"
|
||||||
|
if exist "%PS1%" goto :run
|
||||||
|
|
||||||
|
set "PS1=C:\Enrollment\shopfloor-setup\Standard\Set-MachineNumber.ps1"
|
||||||
|
if exist "%PS1%" goto :run
|
||||||
|
|
||||||
|
echo ERROR: Set-MachineNumber.ps1 not found in any of:
|
||||||
|
echo %~dp0Set-MachineNumber.ps1
|
||||||
|
echo C:\Users\SupportUser\Desktop\Set-MachineNumber.ps1
|
||||||
|
echo C:\Enrollment\shopfloor-setup\Standard\Set-MachineNumber.ps1
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:run
|
||||||
|
echo Launching: %PS1%
|
||||||
|
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%PS1%"
|
||||||
|
exit /b %errorlevel%
|
||||||
|
|||||||
Reference in New Issue
Block a user