FlatUnattendW10-shopfloor.xml was rejected by Windows OOBE with
"the answer file is invalid" after the earlier tower-no-WiFi fix.
Root cause: the inline PowerShell in <CommandLine> for Orders 4 and
5 exceeded the SynchronousCommand CommandLine length limit (~1024
chars) and/or contained characters the unattend schema validator
dislikes.
Fix: move the logic to two external PS1 scripts and shrink both
CommandLine entries to ~85 chars each that just invoke the scripts.
- playbook/wait-for-internet.ps1: 60s interactive prompt ("connect
production network now"), then poll TCP 443 to login.microsoft-
online.us for up to 10 min with a hard timeout so the loop always
exits. Uses Test-NetConnection -Port 443 (not Test-Connection /
ICMP) because Microsoft 365 edges do not reliably respond to ping.
- playbook/migrate-to-wifi.ps1: Gates the entire wired-disable
migration on "does a WiFi adapter exist?" If not (tower), the
script is a no-op. If yes, disable wired / wait for WiFi internet
with a 5 min timeout / re-enable wired on timeout fallback.
- startnet.cmd stages both new scripts to W:\Enrollment\ next to
run-enrollment.ps1 during the WinPE phase.
- FlatUnattendW10-shopfloor.xml Orders 4 and 5 shrunk to short
invocations of C:\Enrollment\wait-for-internet.ps1 and
C:\Enrollment\migrate-to-wifi.ps1.
- startnet-template.cmd kept in sync.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CMM imaging pipeline: WinPE-staged bootstrap + on-logon enforcer
against tsgwp00525 share, manifest-driven installer runner shared via
Install-FromManifest.ps1. Installs PC-DMIS 2016/2019 R2, CLM 1.8,
goCMM; enables .NET 3.5 prereq; registers GE CMM Enforce logon task
for ongoing version enforcement.
- Shopfloor serial drivers: StarTech PCIe serial + Prolific PL2303
USB-to-serial via Install-Drivers.cmd wrapper calling pnputil
/add-driver /subdirs /install. Scoped to Standard PCs.
- OpenText extended to CMM/Keyence/Genspect/WaxAndTrace via
preinstall.json PCTypes; Defect Tracker added to CMM profile
desktopApps + taskbarPins.
- Configure-PC startup-item toggle now persists across the logon
sweep via C:\\ProgramData\\GE\\Shopfloor\\startup-overrides.json;
06-OrganizeDesktop Phase 3 respects suppressed items.
- Get-ProfileValue helper added to Shopfloor/lib/Get-PCProfile.ps1;
distinguishes explicit empty array from missing key (fixes Lab
getting Plant Apps in startup because empty array was falsy).
- 06-OrganizeDesktop gains transcript logging at C:\\Logs\\SFLD\\
06-OrganizeDesktop.log and now deletes the stale Shopfloor Intune
Sync task when C:\\Enrollment\\sync-complete.txt is present (task
was registered with Limited principal and couldn't self-unregister).
- startnet.cmd CMM xcopy block (gated on pc-type=CMM) stages the
bundle to W:\\CMM-Install during WinPE.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a machine number prompt to startnet.cmd after the Standard sub-type
selection. Tech enters the number during the PXE boot process. Defaults
to 9999 if Enter is pressed (existing placeholder behavior).
Written to C:\Enrollment\machine-number.txt alongside pc-type.txt.
Consumers:
00-PreInstall-MachineApps.ps1 - replaces 9999 in UDC InstallArgs with
the entered number, so UDC installs with the correct machine number
from the start (no post-setup Set-MachineNumber needed).
01-eDNC.ps1 - writes the machine number to the DNC\General\MachineNo
registry value during eDNC install.
Configure-PC.ps1 - existing $needsMachineNumber check already skips
the prompt when UDC/eDNC aren't at 9999, so no change needed.
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>
Adds a pcProfiles section to site-config.json that lets each PC type (and
optional sub-type) override startupItems, taskbarPins, and desktopApps.
Scripts resolve: pcProfile > site-wide default > hardcoded fallback.
New shared helper: Shopfloor/lib/Get-PCProfile.ps1
Dot-sourced by consuming scripts. Reads pc-type.txt + pc-subtype.txt,
builds a profile key (e.g. "Standard-Machine"), and looks it up in
site-config.json pcProfiles. Exports $siteConfig, $pcType, $pcSubtype,
$profileKey, $pcProfile for the caller to use.
Replaces the inline Get-SiteConfig function that was copy-pasted into
each script. Scripts now do:
. "$PSScriptRoot\lib\Get-PCProfile.ps1"
instead of duplicating the loader.
startnet.cmd changes:
- Added Lab as PC type option (7)
- Standard now has a sub-type menu: Timeclock / Machine
- Display sub-type menu also writes PCSUBTYPE for consistency
- pc-subtype.txt written alongside pc-type.txt when sub-type selected
- site-config.json copied from enrollment share to W:\Enrollment\
site-config.json v2.0:
- New pcProfiles section with profiles for:
Standard-Timeclock, Standard-Machine, CMM, Genspect, Keyence,
WaxAndTrace, Lab, Display-Lobby, Display-Dashboard
- CMM/Genspect/Keyence/WaxAndTrace profiles have TODO comments for
type-specific apps (placeholder with WJ Shopfloor baseline only)
- Lab/Display profiles have empty startupItems and desktopApps
- Top-level startupItems/taskbarPins/desktopApps remain as site-wide
defaults (used when no profile matches)
Updated scripts:
06-OrganizeDesktop.ps1 - desktopApps from profile > site > hardcoded
07-TaskbarLayout.ps1 - taskbarPins from profile > site > hardcoded
08-EdgeDefaultBrowser.ps1 - uses shared profile loader
Configure-PC.ps1 - startupItems from profile > site > hardcoded
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New site-config.json file at C:\Enrollment\ (staged by startnet.cmd from
the enrollment share) contains all West Jefferson-specific values that were
previously hardcoded across 7 scripts. To deploy at a different GE site,
clone site-config.json and change the values - scripts need zero changes.
Config schema (v1.0):
siteName / siteNameCompact - UDC/eDNC site args
urls{} - Edge startup tab fallback URLs
edgeStartupTabs[] - ordered tab list with .url file basenames
opentext{} - excluded .hep profiles and .lnk shortcuts
startupItems[] - Configure-PC toggle list (exe/existing/url)
taskbarPins[] - 07-TaskbarLayout pin order with lnk paths
desktopApps[] - 06-OrganizeDesktop Phase 2 app list
Every script uses the same inline Get-SiteConfig helper that reads the
JSON and returns $null if missing/corrupt. All consumers fall back to the
current hardcoded West Jefferson defaults when $siteConfig is null, so
PXE servers without a site-config.json continue working identically.
Scripts updated:
06-OrganizeDesktop.ps1 - desktopApps array from config
07-TaskbarLayout.ps1 - pinSpec array from config
08-EdgeDefaultBrowser.ps1 - startup tab loop from config
Configure-PC.ps1 - startup items + site name from config
Check-MachineNumber.ps1 - site name from config
Set-MachineNumber.ps1 - site name from config
01-eDNC.ps1 - siteName + siteNameCompact from config
startnet.cmd - copies site-config.json from enrollment share
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces the single-session "cancel PPKG reboot and cram everything into
one autologon" flow with a staged chain where each reboot advances to the
next step automatically. The technician touches the keyboard 3 times total
(UNPLUG prompt, Y to reboot, Configure-PC selections).
New Stage-Dispatcher.ps1:
Reads C:\Enrollment\setup-stage.txt and chains through:
shopfloor-setup -> sync-intune -> configure-pc
Each stage re-registers HKLM RunOnce so the dispatcher fires again on
the next logon. Stage file is deleted when the chain completes.
Transcript logged to C:\Logs\SFLD\stage-dispatcher.log.
Stage "shopfloor-setup": runs Run-ShopfloorSetup.ps1 (which reboots via
shutdown /r /t 10). Dispatcher advances stage to sync-intune in the
~10 second window before the machine goes down, re-registers RunOnce.
Stage "sync-intune": launches Monitor-IntuneProgress.ps1 -Unattended.
Exit 2 (pre-reboot done, user confirmed): dispatcher re-registers
RunOnce and initiates shutdown /r /t 5. Stage stays at sync-intune so
the monitor picks up post-reboot state on next boot.
Exit 0 (post-reboot install complete): dispatcher chains directly to
Configure-PC.ps1 in the same session, then deletes the stage file.
Stage "configure-pc": runs Configure-PC.ps1 and deletes the stage file.
Fallback entry point if the post-reboot chain was interrupted.
Modified run-enrollment.ps1:
Removed the shutdown /a that canceled the PPKG reboot. Instead writes
setup-stage.txt = "shopfloor-setup" and registers RunOnce for the
dispatcher. PPKG reboot fires naturally (handles PendingFileRename
operations like Zscaler rename and PPKG self-cleanup). Now tracked in
the git repo at playbook/shopfloor-setup/run-enrollment.ps1.
Modified Monitor-IntuneProgress.ps1:
New -Unattended switch. When set:
Invoke-SetupComplete exits 0 without waiting for keypress.
Invoke-RebootPrompt exits 2 without prompting or rebooting (dispatcher
handles both). Manual sync_intune.bat usage (no flag) unchanged.
RetriggerMinutes bumped from 3 to 5 (user request).
Modified startnet.cmd:
Now also copies Stage-Dispatcher.ps1 from the PXE server to
W:\Enrollment\Stage-Dispatcher.ps1 alongside run-enrollment.ps1.
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>
- 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>
- Add /user:pxe-upload pxe credentials to all net use commands (share requires auth)
- Replace timeout with ping delays (timeout.exe not available in WinPE)
- Restore size: largest disk match in autoinstall (root cause was BIOS RST mode)
- Simplify autoinstall late-commands structure
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
startnet.cmd now polls for PESetup.exe completion and reboots with a
15-second countdown. Build scripts (USB + Proxmox) auto-download pip
wheels if the pip-wheels/ directory is missing. Added mok-keys/ to
gitignore.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add startnet.cmd: FlatSetupLoader.exe + Boot.tag/Media.tag eliminates
physical USB requirement for WinPE PXE deployment
- Add Upload-Image.ps1: PowerShell script to robocopy MCL cached images
to PXE server via SMB (Deploy, Tools, Sources)
- Add gea-shopfloor-mce image type across playbook, webapp, startnet
- Change webapp import to move (not copy) for upload sources to save disk
- Add Samba symlink following config for shared image directories
- Add Media.tag creation task in playbook for drive detection
- Update prepare-boot-tools.sh with Blancco config/initramfs patching
- Add grub-efi-amd64-bin to download-packages.sh
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>