The imaging chain needs exactly 2 autologons after the first boot
(sync_intune pre-reboot + post-reboot). Setting to 4 adds 2 extra
reboots of margin for unexpected restarts from Windows Update, PPKG
file operations, or script crashes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Em dashes (U+2014) and arrows (U+2192) break PowerShell 5.1 on
Windows when the file has no UTF-8 BOM -- byte 0x94 gets read as
a right double quote in Windows-1252, silently closing strings
mid-parse. This caused run-enrollment.ps1 to fail on PXE-imaged
machines with "string is missing the terminator" at line 113.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two related fixes from the pipeline audit:
1. Stage-Dispatcher race condition (critical):
Run-ShopfloorSetup.ps1 called shutdown /r /t 10 and the dispatcher
had to write the next stage + register RunOnce within that 10-second
window. If disk I/O was slow, the reboot fired before RunOnce was
registered, and the chain broke.
Fix: dispatcher now cancels Run-ShopfloorSetup's pending reboot
(shutdown /a) immediately after it returns, then advances the stage
and registers RunOnce with no time pressure, then initiates its own
shutdown /r /t 5.
2. Dispatcher owns all reboots:
Run-ShopfloorSetup.ps1 now checks the -FromDispatcher flag at the
end. When called by the dispatcher, it schedules shutdown /r /t 30
as a safety net (the dispatcher cancels it immediately). When called
standalone (manual run or legacy FirstLogonCommands), it reboots
directly with /t 10 as before.
This means the dispatcher has full control over the reboot lifecycle:
cancel -> advance stage -> register RunOnce -> reboot. No racing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The stage-file gate in Run-ShopfloorSetup.ps1 would fire even when
called by Stage-Dispatcher.ps1 (because the stage file still contains
"shopfloor-setup"), causing an infinite exit loop.
Fix: Run-ShopfloorSetup now accepts -FromDispatcher switch. The gate
only fires when the switch is absent (i.e. when called by the unattend's
FirstLogonCommands). Stage-Dispatcher passes -FromDispatcher when
invoking Run-ShopfloorSetup, bypassing the gate.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Run-ShopfloorSetup.ps1 now checks for C:\Enrollment\setup-stage.txt at
the very top. If the stage file exists (written by run-enrollment.ps1),
the script exits immediately with "deferring to Stage-Dispatcher.ps1
on next logon". This prevents the unattend's FirstLogonCommands chain
from running Run-ShopfloorSetup in the same session as run-enrollment,
which was bypassing the entire staged reboot chain.
Without this gate:
FirstLogonCommand #1: run-enrollment.ps1 (sets stage file + RunOnce)
FirstLogonCommand #2: Run-ShopfloorSetup.ps1 (runs immediately, ignoring stage)
PPKG reboot fires after both complete
Next boot: dispatcher has nothing to do (Run-ShopfloorSetup already ran)
With the gate:
FirstLogonCommand #1: run-enrollment.ps1 (sets stage file + RunOnce)
FirstLogonCommand #2: Run-ShopfloorSetup.ps1 (sees stage file, exits)
PPKG reboot fires
Next boot: RunOnce fires dispatcher, reads "shopfloor-setup", runs
Run-ShopfloorSetup properly (stage file deleted by gate on re-entry)
Also: Monitor-IntuneProgress.ps1 RetriggerMinutes bumped from 3 to 5.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. UDC JSON ACL: set on directory C:\ProgramData\UDC\ with
ContainerInherit+ObjectInherit instead of the file. UDC_Setup.exe
gets killed by KillAfterDetection before UDC.exe creates
udc_settings.json, so the file doesn't exist at ACL-grant time.
Directory-level ACL with inheritance covers any file created later.
2. Set-MachineNumber.ps1 auto-running: the type-specific loop's
Get-ChildItem -Filter "*.ps1" picked up the desktop tool alongside
the numbered installer scripts. Added Where-Object { $_.Name -match
'^\d' } so only numbered-prefix scripts (01-eDNC, 02-ACLs) run.
3. WJ Shopfloor copy-to-self: Phase 1 sweep moved WJ Shopfloor.lnk
into Shopfloor Tools\, then Phase 2's Find-ExistingLnk found it
there and tried to Copy-Item to the same path. Now checks if
resolved source path == destination and prints "exists: (already
in Shopfloor Tools)" instead of erroring.
4. NTLARS missing from taskbar pins: the $pinSpec entry was never
added to 07-TaskbarLayout.ps1 despite the comment update. Added
between eDNC and Defect_Tracker in pin order.
5. shutdown /a stderr noise: 15+ red "Unable to abort system shutdown"
lines in the transcript from shutdown.exe writing to stderr when no
shutdown is pending. Changed all occurrences in Run-ShopfloorSetup,
00-PreInstall-MachineApps to: cmd /c "shutdown /a 2>nul" *>$null
which suppresses both native stderr and PS error stream.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New tools:
Configure-PC.bat/.ps1 - Interactive desktop tool for SupportUser to
configure a shopfloor PC after imaging. Two sections:
1. Machine number: if UDC/eDNC are still at placeholder 9999, prompt
to set the real number right now (updates UDC JSON + eDNC registry,
restarts UDC.exe with new args).
2. Auto-startup toggle: pick which apps start at user logon from a
numbered list (UDC, eDNC, Defect Tracker, WJ Shopfloor, Plant Apps).
Creates/removes .lnk files in AllUsers Startup folder. Toggle UI
shows [ON]/[ ] state, safe to re-run anytime. Plant Apps URL
resolved from .url file at runtime with hardcoded fallback to
https://mes-wjefferson.apps.lr.geaerospace.net/run/...
3. Item 6 in the toggle list: register/unregister a "Check Machine
Number" logon task for standard (non-admin) users. When enabled,
the task fires at every logon, checks for 9999, pops an InputBox
if found, updates both apps, then unregisters itself on success.
Check-MachineNumber.ps1 - The logon task script. Runs as the logged-in
user (needs GUI for InputBox), not SYSTEM. Writing to ProgramData + HKLM
is possible because 02-MachineNumberACLs.ps1 pre-grants BUILTIN\Users
write access on the two specific targets during imaging.
02-MachineNumberACLs.ps1 - Standard type-specific script (runs after
01-eDNC.ps1). Opens C:\ProgramData\UDC\udc_settings.json for Users:Modify
and HKLM:\...\GE Aircraft Engines\DNC\General for Users:SetValue. Narrow
scope, not blanket admin.
Execution order fixes in Run-ShopfloorSetup.ps1:
The dispatcher now has two lists: $skipInBaseline (scripts NOT run in the
alphabetical baseline loop) and $runAfterTypeSpecific (scripts run
explicitly after type-specific scripts complete). This fixes the bug where
06/07 ran before 01-eDNC.ps1 installed DnC, so eDNC/NTLARS shortcuts were
silently skipped.
New execution order:
Baseline: 00-PreInstall, 04-NetworkAndWinRM (skipping 05-08 + tools)
Type-specific: 01-eDNC, 02-MachineNumberACLs
Finalization: 06-OrganizeDesktop, 07-TaskbarLayout
06 internally calls 05 (Office shortcuts, Phase 0) and 08 (Edge config,
Phase 4) as sub-phases, so they also benefit from running late. Office
isn't installed until after the first reboot (ppkg streams C2R), so 05
no-ops at imaging time but succeeds when 06's SYSTEM logon task re-runs
it on the second boot. 08 resolves startup-tab URLs from .url files
delivered by DSC (even later); same self-heal via the logon task.
Other fixes in this commit:
- OpenText Setup-OpenText.ps1 Step 4: exclude WJ_Office.lnk, IBM_qks.lnk,
mmcs.lnk desktop shortcuts (matching the Step 3 .hep profile exclusion
from the previous commit). Removes stale copies from prior installs.
- 05-OfficeShortcuts.ps1: widened Office detection to 6 path variants
covering C2R + MSI + Office15/16, with diagnostic output on miss.
- 06-OrganizeDesktop.ps1: removed Phase 3 (desktop-root pin copies for
eDNC/NTLARS) so shortcuts live in Shopfloor Tools only, not duplicated
at root. Emptied $keepAtRoot. Added Phase 0 (call 05) and Phase 4
(call 08). Lazy folder creation + empty-folder cleanup. Scheduled task
now runs as SYSTEM (was BUILTIN\Users with Limited which failed the
admin check). Added NTLARS to 07's taskbar pin list.
- 08-EdgeDefaultBrowser.ps1: Plant Apps URL fallback hardcoded from
device-config.yaml.
- All new scripts have Start-Transcript logging to C:\Logs\SFLD\ with
timestamps and running-as identity.
- Run-ShopfloorSetup.ps1: Start-Transcript + Stop-Transcript wrapping
entire dispatcher run, writes to C:\Logs\SFLD\shopfloor-setup.log.
Configure-PC.bat added to SupportUser desktop copy list.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace em-dash characters with plain hyphens across the 5 shopfloor
setup scripts (avoids cp1252 mojibake in .bat files and keeps the
PowerShell sources consistent). Also adds [Parameter(Position=1)] to
Write-PreInstallLog so the Level argument can be passed positionally.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a local-install pipeline so Standard shopfloor PCs get Oracle, the
VC++ redists (2008-2022), and UDC installed during PXE imaging via Samba
instead of pulling ~215 MB per device from Azure blob over the corporate
WAN. Intune DSC then verifies (already-installed apps are skipped) and
the only Azure traffic on the happy path is ~11 KB of CustomScripts
wrapper polling.
New files:
- playbook/preinstall/preinstall.json — curated app list with PCTypes
filter and per-app detection rules. Install order puts VC++ 2008
LAST so its (formerly) reboot-triggering bootstrapper doesn't kill
the runner mid-loop. (2008 itself now uses extracted vc_red.msi with
REBOOT=ReallySuppress; the reorder is defense in depth.)
- playbook/shopfloor-setup/Shopfloor/00-PreInstall-MachineApps.ps1 —
the runner. Numbered 00- so it runs first in the baseline sequence.
Reads preinstall.json, filters by PCTYPE, polls for completion via
detection check (handles UDC's hung WPF process by killing it once
detection passes), uses synchronous WriteThrough logging that
survives hard reboots, preserves log history across runs.
- playbook/shopfloor-setup/Standard/Set-MachineNumber.{ps1,bat} — desktop
helper for SupportUser. Reads current UDC + eDNC machine numbers,
prompts via VB InputBox, validates digits-only, kills running UDC,
edits both C:\ProgramData\UDC\udc_settings.json and HKLM\…\GE Aircraft
Engines\DNC\General\MachineNo, relaunches UDC. Lets a tech assign a
real machine number to a mass-produced PC without admin/LAPS.
- playbook/sync-preinstall.sh — workstation helper to push installer
binaries from /home/camp/pxe-images/main/ to the live PXE Samba.
Changes:
- playbook/startnet.cmd + startnet-template.cmd — add xcopy to stage
preinstall bundle from Y:\preinstall\ to W:\PreInstall\ during the
WinPE imaging phase, gated on PCTYPE being set.
- playbook/pxe_server_setup.yml — create /srv/samba/enrollment/preinstall
+ installers/ directories and deploy preinstall.json there.
- playbook/shopfloor-setup/Run-ShopfloorSetup.ps1 — bump AutoLogonCount
to 99 at start (defense against any installer triggering an immediate
reboot mid-dispatcher; final line still resets to 2 on successful
completion). Copy Set-MachineNumber.{ps1,bat} to SupportUser desktop
on Standard PCs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- AutoLogonCount reduced from 2 to 1 in Run-ShopfloorSetup.ps1
- Remove default pinned Start Menu tiles and set blank layout for future users
- Add sync_intune.bat: triggers MDM sync and polls for SFLD group policies
- Update README.md and SETUP.md with current project state (boot chain, new
scripts, samba shares, webapp pages, commit history)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix check-bios.cmd: replace parenthesized if blocks with goto labels
(cmd.exe fails silently with if/else on network-mapped drives)
- Move BIOS check files to winpeapps/_shared/BIOS for reliable SMB access
- Add network wait loop before BIOS check in startnet.cmd
- Show firmware status in WinPE menu header (BIOS_STATUS variable)
- Add BypassNRO registry key to skip OOBE network requirement
- Refactor download-drivers.py with --parallel N flag (ThreadPoolExecutor)
- Set SupportUser AutoLogonCount to 3 in shopfloor unattend
- Add shutdown -a at start + shutdown /r /t 10 at end of Run-ShopfloorSetup.ps1
- Switch download-drivers.py from wget to curl for reliable stall detection
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>