Tech catches a PC imaged with a wrong machine number. Previously the
share restore (NTLARS .reg + UDC settings + UDC live data) only fired
on the placeholder->real transition, so a real->real change rewrote
only UDC JSON, eDNC reg, and MTConnect Devices.xml - leaving the wrong
NTLARS config in place.
Update-MachineNumber.ps1: replace the placeholder-only guard with an
any-change guard so the share restore block fires on reassign too.
The existing one-shot migrated/ consumption keeps live-data restore
idempotent. Also writes C:\Enrollment\machine-number.txt to keep
imaging-time scripts in sync.
Set-MachineNumber.ps1 (both collections + nocollections): show a
confirmation dialog when reassigning between two real numbers, naming
old/new and listing what gets pulled. Audit each call to
C:\Logs\Shopfloor\reassign.log.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bundles drift left uncommitted from prior sessions and the UDC matrix
verify entry added today.
Drift items (all per session-progress.md, completed in earlier sessions
but never staged):
- playbook/check-bios.cmd (deleted, moved to BIOS/check-bios.cmd)
- playbook/migrate-to-wifi.ps1 (made no-op 2026-04-24 after the dnsmasq
no-gateway fix removed the wired-NIC race that motivated it)
- playbook/preinstall/oracle/Install-Oracle11r2.cmd (post-OUI .ora copy
added 2026-04-24)
- playbook/preinstall/oracle/tnsnames.ora (live tnsnames, 469 KB,
deployed alongside the wrapper 2026-04-24)
- playbook/pxe_server_setup.yml (dnsmasq dhcp-option=3,6 commented,
Oracle .ora deploy task added 2026-04-24)
- playbook/shopfloor-setup/BIOS/{check-bios.cmd, models.txt} (BIOS
detection refinements)
- playbook/shopfloor-setup/Shopfloor/Force-Lockdown.bat
- playbook/shopfloor-setup/Shopfloor/Monitor-IntuneProgress.ps1
- playbook/shopfloor-setup/Shopfloor/SetShopfloorAutoLogon.bat (new)
- playbook/shopfloor-setup/Shopfloor/09-Install-PrinterInstallerMap.ps1
(new, places PrinterInstallerMap.exe + Public Desktop shortcut at
imaging time; manifest entry self-heals on tamper)
- playbook/shopfloor-setup/Shopfloor/lib/Show-IntuneDeviceQR.ps1 (new,
standalone QR rendering for site that wanted just that piece)
- playbook/shopfloor-setup/gea-shopfloor-collections/{Install-eMxInfo.cmd.template,
Restore-UDCData.ps1} (these were uncommitted in pre-rename Standard/;
git mv didn't catch them because they were untracked at the time)
- docs/shopfloor-machine-imaging-guide.md (operator-facing how-to)
Matrix:
- common.test/matrix.json: add UDC verify entry to gea-shopfloor-collections
row. Surfaces UDC silent-install issue (item H pending) instead of
letting it pass silently.
.gitignore:
- PrinterInstallerMap.exe (142 MB) excluded. Track via LFS or stage on
PXE server only - too big for regular git history. Untouched on disk
so existing local copy still works.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pairs with Phase 1+2 from earlier (alias maps in Install-FromManifest,
GE-Enforce, Get-PCProfile, verify-state). See project-shopfloor-rename-reorg
memory for the plan.
Phase 3 (repo + paths):
- git mv per-PC-type dirs to gea-shopfloor-* names:
Standard -> gea-shopfloor-collections
CMM -> gea-shopfloor-cmm
Keyence -> gea-shopfloor-keyence
Genspect -> gea-shopfloor-genspect
WaxAndTrace -> gea-shopfloor-waxtrace
Display -> gea-shopfloor-display
Lab -> gea-shopfloor-common (folded; Timeclock+Lab merge)
- New gea-shopfloor-nocollections/ (clone of collections sans UDC scripts).
- New gea-shopfloor-heattreat/ (placeholder, README only).
- Move Standard/ntlars-backups/ -> _ntlars-backups/ (per-MN, not per-type).
- Run-ShopfloorSetup.ps1: Resolve-PCTypeDir helper walks alias group when
the on-disk dir for the current pcType is missing. Set-MachineNumber
helper-copy gated on collections|nocollections|legacy Standard-Machine.
- Update-MachineNumber.ps1: pcProfiles lookups try gea-shopfloor-collections
first, fall back to legacy Standard-Machine. PowerShell 5.1 compatible
(no null-coalesce).
Phase 4 (startnet.cmd menu):
- Choice 3 "GEA Shopfloor" now drills into a 9-item sub-menu instead of
going straight to enrollment. Sub-cats:
1. Machine with Collections -> gea-shopfloor-collections
2. Machine without Collections -> gea-shopfloor-nocollections
3. Common (Timeclock, Lab) -> gea-shopfloor-common
4. Keyence -> gea-shopfloor-keyence
5. CMM -> gea-shopfloor-cmm
6. Genspect -> gea-shopfloor-genspect
7. Heattreat -> gea-shopfloor-heattreat
8. Wax and Trace -> gea-shopfloor-waxtrace
9. Display -> gea-shopfloor-display
- Office menu (existing 6-option) follows for every sub-cat.
- Machine number prompt only for collections + nocollections.
- pc-subtype.txt + display-type.txt no longer written. PCTYPE is a
single full string (gea-shopfloor-*); subtype-aware code paths fall
back to empty and resolve via the alias map.
- CMM bootstrap stage gate switched from "%PCTYPE%"=="CMM" to
"%PCTYPE%"=="gea-shopfloor-cmm".
Test harness:
- B-enforce/run.sh PCSUBTYPE default changed from "Machine" to "" so
single-arg invocation matches the new single-string scheme. Two-arg
legacy form ("Standard Machine") still works via aliasing.
- B-enforce/tamper.ps1 alias-aware Test-MatrixEntryMatches mirroring
verify-state.ps1.
Smoke-tested on win11 VM as SYSTEM via qga: B-enforce harness 5-phase
cycle (stage / baseline / tamper / heal / idempotent) passes 10/10
with PCType=gea-shopfloor-collections AND with legacy "Standard Machine"
two-arg form.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 5 + 6 of the gea-shopfloor-* rename.
Get-PCProfile.ps1: when the legacy profileKey ("Standard-Machine",
"CMM", etc.) is missing from siteConfig.pcProfiles, walks the alias
group and returns the first matching new key ("gea-shopfloor-collections",
"gea-shopfloor-cmm", etc.). Vice versa: a fleet PC writing the new
string finds its profile under the old key. Same alias map shape as
GE-Enforce + Install-FromManifest, kept in sync manually for now -
extract to shared file later if drift becomes a problem.
matrix.json: adds 3 new rows for gea-shopfloor-nocollections,
gea-shopfloor-common (Timeclock+Lab merge), gea-shopfloor-heattreat
(placeholder). Existing rows for legacy names retained; the new
verify-state alias resolution lets either be requested.
verify-state.ps1: Test-MatrixEntryMatches walks the alias map so
harness invocation with "Standard Machine" or "gea-shopfloor-collections"
both resolve to the same matrix row.
Smoke-tested via qga-as-SYSTEM on win11: legacy Standard/Machine,
new gea-shopfloor-collections, and new gea-shopfloor-nocollections
all return 10/10 pass against current VM state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the tech transitions a 9999-placeholder PC to its real machine number,
also restore the per-bay udc_settings_<num>.json from
\\tsgwp00525\shared\spc\udc\settings_backups\. PXE-time preinstall can't reach
this share (no SFLD creds yet), so 00-PreInstall uses the local C:\Enrollment
mirror; post-config the share is reachable, so the renumber path goes direct
to the canonical source.
Adds udcSettingsSharePath to site-config.json under Standard-Machine.
Bundles in prior uncommitted work in the same file: ntlars reg restore,
UDC data restore (CurrentData.json + ArchivedData/), MTConnect Devices.xml
inline rewrite + service restart, and one-shot consume of per-bay UDC
backup -> migrated/<timestamp>/.
- Register-MapSfldShare.ps1: swap scheduled task for HKLM\Run entry. Task with -GroupId runs in session 0 with no HKCU, so /persistent:yes fails and the drive mapping isn't visible to Explorer. Run key fires at Explorer startup in the interactive user's session with full token + HKCU. Unregisters legacy 'GE Shopfloor Map S: Drive' task for PCs already imaged.
- Run-ShopfloorSetup.ps1: stop bumping AutoLogonCount (99 at start, 4 at end). Windows decrements per-logon and at 0 clears AutoAdminLogon + DefaultPassword, which nukes the lockdown-configured ShopFloor autologon. Re-enable-wired-NICs task now gates on Autologon_Remediation.log 'Autologon set for ShopFloor' instead of SFLD creds, so wired stays off through the whole Intune+DSC+lockdown chain.
- Monitor-IntuneProgress.ps1: Phase 4 treats 'no custom scripts' as COMPLETE when DSC install is done (was WAITING, which stalled the state machine on PC types without scripts). Push retrigger out to 15min when entering lockdown-wait so a stale 5min retrigger doesn't fire mid-Remediation. Removed the AutoLogonCount delete in Invoke-SetupComplete since we no longer set it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AutoLogonCount depletion:
Run-ShopfloorSetup set AutoLogonCount=4 for SupportUser. Windows
decrements per-logon; at 0 it clears AutoAdminLogon + DefaultPassword,
nuking the lockdown-configured ShopFloor autologon. Fix: delete
AutoLogonCount in Invoke-SetupComplete before the lockdown reboot.
ShopFloor's Autologon.exe-set config persists indefinitely.
Sync_intune window on ShopFloor:
The marker-check path used 'exit 0' but the task runs with -NoExit,
leaving a dangling PowerShell window on every ShopFloor logon. Fix:
[Environment]::Exit(0) kills the host outright, defeating -NoExit.
S: drive mapping:
Vendor ConsumeCredentials.ps1 calls New-StoredCredential -Persist
LocalMachine (needs admin) before net use. ShopFloor is non-admin so
cred-store fails silently and net use has no auth. Fix: new
Map-SfldShare.ps1 reads HKLM creds and passes them inline to
net use /user: -- no Credential Manager needed, works as Limited.
Register-MapSfldShare updated to stage + reference our script.
Wired NIC re-enable:
SYSTEM task polls for SFLD creds (Phase 5), re-enables wired NICs,
self-deletes. Replaces the broken Enable-NetAdapter in Monitor
(Limited principal can't enable NICs). No-WiFi devices unaffected
(migrate-to-wifi never disables, re-enable is a no-op).
Sync throttle:
15 min retrigger when only waiting for lockdown (was 5 min for all
phases). Avoids interrupting the Intune Remediation script.
Defect Tracker path:
All references corrected to C:\Program Files (x86)\WJF_Defect_Tracker.
QR code retry:
Build-QRCodeText retried every poll cycle until DeviceId appears
(was single-shot that could miss the dsregcmd timing window).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause found via decompiling UDC_Setup.exe: it never writes
udc_settings.json from CLI args. Instead it pulls
Settings_Backups\udc_settings_<num>.json from \\tsgwp00525\shared\SPC\UDC
-- which is unreachable at imaging time (no SFLD creds yet). Silent
File.Exists() false, settings never copy, UDC lands on Evendale defaults.
Fix: stage 80 udc_settings_*.json backups under
shopfloor-setup/Standard/udc-backups/ (same tree as ntlars-backups,
xcopy'd to C:\Enrollment\ by existing startnet.cmd). 00-PreInstall
pre-creates C:\ProgramData\UDC\udc_settings.json from the matching
backup BEFORE UDC_Setup.exe runs. Installer's server-side copy silently
fails (unreachable), our pre-staged file survives.
Also:
- preinstall.json UDC InstallArgs corrected: "West Jefferson" -9999
(quoted spaced site + dash-prefixed number, confirmed via decompile)
- Update-MachineNumber.ps1 UDC.exe relaunch: quoted site + dash number
- Monitor-IntuneProgress: action prompts (Select Device Category after
Phase 1; Initiate ARTS Lockdown after Phase 5/creds), Display flow
(3-phase: Registration -> Config -> Lockdown), Phase 6 IME-based
lockdown detection
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
UI overhaul:
Replaced the 30+ line checkbox-per-sub-item view with a clean
6-line phase summary styled for GE Aerospace branding. Each phase
shows one colored status tag: [COMPLETE] green, [IN PROGRESS] cyan,
[WAITING] gray, [FAILED] red. Action hint for Phase 2 (device
category assignment) in yellow. QR code + Device ID below.
Phase 6 lockdown detection:
Replaced DefaultUserName + admin-rename checks (which pass at PPKG
time, way too early) with Intune Remediation log artifacts:
- Autologon_Remediation.log: "Autologon set for ShopFloor"
- Autologon_Detection.log: "matches the expected value: 1"
These only exist after the Intune Remediation cycle actually fires
post-enrollment, making Phase 6 a true end-of-chain signal.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cosmetic + accuracy fixes spotted on the live test PC:
- Phase 3 deploy/install lines had a stray double-space after the
checkbox; Phase 5 'Share creds present in HKLM' and Phase 6
'Administrator renamed' had wider misalignment. All four lines
collapsed to single-space-after-checkbox so the column lines up
with the rest of the table.
- Phase 4 status detector was greping the last 30 lines of each
Install-*.log for /(?i)\b(ERROR|Failed|exception)\b/. That hit
benign summary lines like 'Failed: 0' or 'Errors: 0' and
marked successful runs as failed (Install-VCRedists.ps1 was the
trigger -- 8/8 'Already installed - skipping' but the summary
contained 'Failed: 0' and Phase 4 said FAILED). Tightened the
regex to also exclude /\b(ERROR|Failed|Failures|Errors|Exceptions?)\s*[:=]\s*0\b/
so the keyword has to be next to a non-zero value (or the
vocabulary 'Exit code 1603 - FAILED' style still trips correctly).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
UDC_Setup.exe and UDC.exe expect:
UDC_Setup.exe WestJefferson -7605
Not the spaced-quoted positional pair we'd been passing:
UDC_Setup.exe "West Jefferson" 7605
The wrong format meant UDC ignored both args, fell back to defaults
(Site=Evendale, MachineNumber=blank). Combined with the kill-after-detect
window, neither value got persisted to udc_settings.json regardless of
whether UDC.exe was given time to write.
Changes:
- preinstall.json: UDC InstallArgs now "WestJefferson -9999"
- 00-PreInstall-MachineApps.ps1: site override now matches/replaces
the compact 'WestJefferson' token (not 'West Jefferson') and uses
siteNameCompact from site-config; targetNum extraction regex updated
to '-(\d+)$' for the new dash-prefix format
- Update-MachineNumber.ps1: UDC.exe relaunch now passes positional
compact-site + dash-prefixed number instead of -site/-machine flags
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Phase 6 (Lockdown) and tighten Phase 5 so the 5-min Intune sync loop
doesn't declare success until the device is genuinely operator-ready.
- Phase 6 watches two HKLM-level signals confirmed in the 2026-04-15
pre/post lockdown state diff: Winlogon\DefaultUserName flipped to
'ShopFloor', and local Administrator renamed to 'SFLDAdmin'. Both land
via MDM PolicyCSP after DSCInstall.log finishes.
- Phase 5 was just checking that the Consume Credentials scheduled task
existed; that only proves DSC scheduled it. Now also verifies creds
actually landed under HKLM:\SOFTWARE\GE\SFLD\Credentials\* with
TargetHost+Username+Password populated -- which is what Machine/Acrobat/
CMM-Enforce actually consume.
- Final completion gate: DscInstallComplete && CredsPopulated &&
LockdownComplete (was just DscInstallComplete). Display PCs unchanged --
they exit early via the no-DSC Phase 1 path.
- Invoke-SetupComplete now issues shutdown /r /t 10 in AsTask mode after
writing the sync-complete marker and running the Configure-PC machine#
prompt. Next boot triggers ShopFloor autologon, which materializes the
ShopFloor profile from C:\Users\Default (where 03-ShellDefaults already
baked in TaskbarAl=0, etc.).
- Phase 1->2 gap (waiting for tech to assign device category in Intune
portal) now shows an explicit ACTION hint instead of empty checkboxes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 03-ShellDefaults.ps1: Default-User TaskbarAl=0 (left), HKLM policies to
hide Start Recommended section, kill Bing web search + suggestions,
disable Cortana. LTSC-honoured; runs fleet-wide via baseline loop.
- ntlars-backups/: 147 per-machine eDNC registry backups renamed to
flat <MachineNumber>.reg scheme. Historical off-by-one entries from
the original dump rewritten to match CSV-target MachineNo.
- Standard/03-RestoreEDncConfig.ps1: at imaging time, if tech typed a
real machine number at PXE (not 9999), import <num>.reg from the local
staged copy. Restores eFocas IP, PPDCS serial, Hssb relays -- not just
the bare MachineNo. Skipped on Timeclock / 9999 / missing backup.
- Update-MachineNumber.ps1: when tech later sets a real number from 9999,
pull <num>.reg from tsgwp00525 SFLD share (ntlarsBackupSharePath in
site-config) and reg-import it before writing the new MachineNo.
- Restore-EDncReg.ps1: shared helper (Mount-SFLDShare + Import-EDncRegBackup)
used by both callers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Monitor-IntuneProgress: Display PCs skip DSC phases entirely (no SAS
token, no DSCInstall.log), complete after Phase 1 identity. Renderer
hides Phase 2-5 for Display type.
- Playbook: deploy PPKG files and run-enrollment.ps1 from USB to
enrollment share. Bump dnsmasq restart cron from 15s to 30s.
- build-usb.sh: copy enrollment/ directory (PPKGs) onto USB if present.
- user-data: add dpkg --configure -a after offline .deb install to fix
packages left in unconfigured state (cron, systemd-timesyncd).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rolls up everything from the CMM imaging test iteration tonight. No
single concern - several small, related polish items on the option-3
patched-MSI pipeline and the shopfloor-setup / sync_intune handoff.
- Rename all type-specific "01-Setup-<Type>.ps1" scripts to
"09-Setup-<Type>.ps1" across CMM, Display, Genspect, Keyence, Lab,
and WaxAndTrace. The "01-" prefix implied the script runs first in
the overall sequence when it actually runs between baseline (00, 04)
and finalization (06, 07). Logs now read "Running CMM setup:
09-Setup-CMM.ps1" which matches the real position. Standard/
01-eDNC.ps1 + 02-MachineNumberACLs.ps1 left alone - those digits
represent real within-type ordering.
- playbook/shopfloor-setup/site-config.json CMM profile updates:
- startupItems = [] (empty). Previously had WJ Shopfloor auto-launch
which the user does not want on CMM workstations. Now relies on
the Get-ProfileValue empty-array fix to not fall through to site
defaults.
- desktopApps + taskbarPins gain entries for PC-DMIS 2016, PC-DMIS
2019 R2, CLM Admin, and goCMM so 06-OrganizeDesktop Phase 2
materializes them into C:\\Users\\Public\\Desktop\\Shopfloor Tools\\
and 07-TaskbarLayout pins them. goCMM is under C:\\Program Files
(x86)\\General Electric\\goCMM\\ (GE product, not Hexagon).
- playbook/shopfloor-setup/Run-ShopfloorSetup.ps1: remove the blocking
"UNPLUG ethernet cable, press any key" prompt + the interactive
wired-NIC re-enable. The whole prompt block was a hard blocker on
the imaging chain that required a human to walk to each PC.
- playbook/shopfloor-setup/Shopfloor/lib/Monitor-IntuneProgress.ps1:
re-enable wired NICs unconditionally at the top of the transcript.
This is the new home for the re-enable that used to live behind the
prompt in Run-ShopfloorSetup. By the time sync_intune fires (after
PPKG reboot + auto-login + Stage-Dispatcher), the tech has had
minutes of wall-clock time to physically rewire from PXE to
production without us blocking on a keypress. Tower case is a
no-op because migrate-to-wifi.ps1 already left wired enabled.
- Internal comment updates in 09-Setup-CMM.ps1, cmm-manifest.json,
Install-FromManifest.ps1, and startnet.cmd (+ startnet-template)
to reflect the new filename.
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>
BUILTIN\Users (Limited RunLevel) can't delete scheduled tasks, so
Unregister-ScheduledTask failed silently and the sync task kept firing
at every logon even after completion.
Fix: write C:\Enrollment\sync-complete.txt on completion. At script
startup in -AsTask mode, check for the marker and exit immediately if
found. The task stays in Task Scheduler but does nothing -- fires at
logon, sees marker, exits in under a second. No visible window.
Manual sync_intune.bat runs (no -AsTask) ignore the marker and always
show the full status display for inventory QR code purposes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three changes to eliminate the redundant startup-item picker during
the imaging chain:
06-OrganizeDesktop.ps1 - new Phase 3: auto-apply startup items
Reads pcProfile.startupItems (or site-wide default) and creates
.lnk files in AllUsers Startup folder. Supports exe, existing, and
url types (same as Configure-PC). Idempotent - skips items that
already exist so manual changes aren't overwritten. Runs during
shopfloor setup finalization, so the tech doesn't need to select
startup items again.
Configure-PC.ps1 - new -MachineNumberOnly switch
When set, skips the entire startup-items section and only shows the
machine number prompt (if UDC/eDNC at 9999). Used by sync_intune
-AsTask after completion. Full startup picker still available when
the tech opens Configure-PC.bat manually from the desktop.
Monitor-IntuneProgress.ps1 - simplified -AsTask completion
After post-reboot DSC complete: unregisters task, launches
Configure-PC -MachineNumberOnly, exits. Tech uses sync_intune.bat
on the desktop to see QR code for inventory purposes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The QR code text was built once at script startup. If the device wasn't
AAD-joined yet, it showed "Device not yet Azure AD joined" forever -
even after Phase 1 checks passed. Now regenerates Build-QRCodeText
when Phase1.AzureAdJoined transitions to true.
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>
Three optimization batches from the pipeline audit:
1. Shared Update-MachineNumber.ps1 helper (lib/)
Extracts duplicated machine-number update logic from Configure-PC.ps1,
Check-MachineNumber.ps1, and Set-MachineNumber.ps1 into a shared
dot-sourceable helper at Shopfloor/lib/Update-MachineNumber.ps1.
Exports:
Get-CurrentMachineNumber → @{ Udc = $string; Ednc = $string }
Update-MachineNumber -NewNumber <n> [-Site <s>] → @{ UdcUpdated; EdncUpdated; Errors }
All three consumers now dot-source the helper instead of duplicating
~50 lines each. Set-MachineNumber.ps1 also migrated from inline
Get-SiteConfig to dot-sourcing Get-PCProfile.ps1 for consistency.
2. Site-config integration for remaining scripts
Setup-OpenText.ps1: exclude lists (profiles + shortcuts) now read from
site-config.json opentext section, falling back to West Jefferson
defaults. Inline Get-SiteConfig since the script runs from
C:\PreInstall\installers\opentext\ (can't dot-source Get-PCProfile).
00-PreInstall-MachineApps.ps1: after parsing preinstall.json, scans
InstallArgs for "West Jefferson" and replaces with site-config
siteName if different. Inline Get-SiteConfig for same reason.
3. Placeholder type-specific directories
Created skeleton 01-Setup-*.ps1 scripts for all PC types so the
directory structure is in place and Run-ShopfloorSetup's type-specific
loop has something to iterate over:
Genspect/01-Setup-Genspect.ps1
Keyence/01-Setup-Keyence.ps1
WaxAndTrace/01-Setup-WaxAndTrace.ps1
Lab/01-Setup-Lab.ps1
Each logs a "no type-specific apps configured yet" banner and exits.
Fill in app installs when details are finalized; for share-based
installs, copy the CMM/01-Setup-CMM.ps1 pattern.
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>
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>
Don't prompt the user to reboot until the enrollment pipeline has
finished its pre-reboot work. Previously Test-RebootState fired as
soon as DSCDeployment.log showed "completed", even if Phase 1 (Identity)
or Phase 2 (SFLD config) checks were still in progress.
Now the reboot prompt requires ALL of these to be green in the snapshot:
Phase 1: AzureAdJoined, IntuneEnrolled, EmTaskExists, PoliciesArriving
Phase 2: SfldRoot, FunctionOk, SasTokenOk
Phase 3: DeployLogExists, DeployComplete
This prevents the edge case where DSCDeployment.log completes but the
user reboots before Intune policies have fully landed, which could leave
the post-reboot DSC install phase without the SAS token or function
assignment it needs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Run-ShopfloorSetup.ps1 line 46-47 does:
Get-ChildItem -Path $baselineDir -Filter "*.ps1" -File | Sort-Object Name
foreach ($script in $scripts) { & $script.FullName }
This picks up EVERY *.ps1 in Shopfloor\ and runs it as a baseline
script. Last commit (66d13d8) put Monitor-IntuneProgress.ps1 in that
same directory, which means the dispatcher was running it as the LAST
baseline script (M sorts after 00/04/05). The monitor is an infinite
poll loop that never returns until the SFLD lifecycle is complete -
so the dispatcher hung there forever, and Standard\01-eDNC.ps1 and
Standard\Set-MachineNumber.ps1 never ran.
Symptoms in the test run:
- 00-PreInstall-MachineApps.ps1 ran (10 installed, 1 OpenText fail)
- 04-NetworkAndWinRM.ps1 ran silently
- 05-OfficeShortcuts.ps1 ran silently
- Monitor-IntuneProgress.ps1 started (Clear-Host + status table) and
hung in its main loop
- eDNC + Set-MachineNumber never ran
Fix: move Monitor-IntuneProgress.ps1 into Shopfloor\lib\ so the
dispatcher's non-recursive Get-ChildItem doesn't see it. Update
sync_intune.bat's MONITOR path to the new location, and add a
comment explaining WHY the monitor lives under lib\ to prevent this
mistake from being repeated.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>