Commit Graph

30 Commits

Author SHA1 Message Date
cproudlock
45f39fd431 startnet.cmd: suppress 'System error 85' on duplicate Y: mount
The :prompt_waxtrace_asset picker block maps Y: to the enrollment share
so select-waxtrace-asset.ps1 can read INDEX.csv. The later :skip_machinenum
block also runs `net use Y: \\172.16.9.1\enrollment ...` to keep the share
mounted through the rest of imaging, but Y: is already mapped at that
point so the second net use throws "System error 85: The local device
name is already in use". Harmless (script proceeds and "the command
completed successfully" prints right after) but visible noise to the
operator during PXE imaging.

Gate the second net use behind `if exist Y:\` so we only map when Y: is
not already mounted. Same end state, no error message.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 09:38:34 -04:00
cproudlock
5891a1966f Wax/Trace: heal 218-378-13 cal disc filename bug + VC++ 2017 + picker
09-Setup-WaxAndTrace.ps1 Step 3:
- Detect Mitutoyo's burn-time typo on 218-378-13 series cal discs
  (filenames carry a trailing space inside the probe ID component,
  e.g. "Linear_X_218-378-13 _100072210.txt"). Their own .NET Setup.exe
  calls FileSystemInfo.set_Attributes on the source path and throws
  System.ArgumentException because the path contains an embedded space
  component, crashing every cal apply on 218-378-13 bays (exit
  -532462766 = 0xE0434352, .NET unhandled exception). Confirmed via
  WER Event 1026 captured during today's WJF00159 imaging.
- When the buggy filenames are detected, bypass the broken vendor
  Setup.exe and direct-copy data\*.* into
  C:\Program Files (x86)\MitutoyoApp\Formtracepak\data\, renaming
  each file to strip ' _' (space-underscore) -> '_'. Clear read-only
  attr on each landed file. Older 218-458A discs have clean filenames
  and still use the vendor Setup.exe path.

waxtrace-manifest.json:
- Drop DetectionValue=v14.15.26706 from both VC++ 2017 redist entries.
  Windows Update routinely bumps the VS14 runtime to 14.16+ / 14.3x+,
  the older Mitutoyo redist refuses to install over the newer (exit
  1638 'Another version already installed') and the manifest engine
  marked it as failed even though the runtime was fine. Detection is
  now by registry-key+name presence, which any VC++ 2015-2022 redist
  satisfies (they are backward-compatible).

startnet.cmd:prompt_waxtrace_asset:
- Replace free-text input with select-waxtrace-asset.ps1 arrow-key
  picker driven from installers-post/waxtrace/calibrations/INDEX.csv.
- Map Y: enrollment share early so the picker can read INDEX.csv.
- Replace parens-in-parens block (echo of '(e.g. WJRP2335)' inside
  the if-paren caused 'to was unexpected at this time' parse error
  observed by tech mid-imaging) with goto-flow.
- Fall back to free-text prompt if picker unavailable or operator
  presses Esc.

select-waxtrace-asset.ps1:
- Sort bays descending by asset tag so WJRP* lands at top of menu.
- Also staged as gea-shopfloor-waxtrace/select-waxtrace-asset.ps1 so
  sync-waxtrace.sh ships it to installers-post/waxtrace/ on the share.

sync-waxtrace.sh:
- Push select-waxtrace-asset.ps1 next to INDEX.csv on the share.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 19:38:19 -04:00
cproudlock
37357eee43 Shopfloor images: add Wax/Trace + Keyence per-model variants
Wax/Trace (gea-shopfloor-waxtrace):
- captured/ holds master FormTracePak v6.0 state (Program Files reg dump
  gzipped, ARP entries) taken from a win11 VM where the CD-ROM-bound VB6
  wrapper was driven to completion. xcopy + reg-import replays the install
  on real bays without running the wrapper itself.
- 09-Setup-WaxAndTrace.ps1 rewrites the stub: installs prereqs via manifest
  (VC++ 2008/2017 x86+x64, Sentinel HASP), expands the captured zips into
  C:\Program Files (x86)\MitutoyoApp + C:\MitutoyoApp, imports the reg
  hive, then mounts the bay's per-machine cal ISO (matched by asset tag
  in machine-number.txt) and runs its Setup.exe.
- waxtrace-manifest.json lists the 5 prereqs with InstallShield-style
  silent flags verified on the win11 VM.
- sync-waxtrace.sh ships captured-binary/ + prereqs + cal ISOs from
  /home/camp/pxe-images/iso/mitutoyo-cal/ to
  /srv/samba/enrollment/installers-post/waxtrace/ on the PXE box.
- select-waxtrace-asset.ps1 arrow-key bay picker for WinPE (parses
  INDEX.csv from the cal share, offers "Other (new bay)" fallback).
- startnet.cmd: prompt_waxtrace_asset prompt, skip_waxtrace_stage xcopy
  block (mirrors :skip_cmm_stage), machine-number.txt write covers bay
  asset tag (WJRP*).

Keyence (gea-shopfloor-keyence) - now multi-model:
- vr3000/manifest.json + vr5000/manifest.json + vr6000/manifest.json
  (current single-model VR-6000 moved into vr6000/ subdir). Each ships
  the model's MSI silent-install + DetectionPath via ProductCode.
  Big payloads (Data1.cab, Data11.cab) gitignored, staged via
  sync-keyence.sh from /home/camp/pxe-images/iso/keyence/.
- 09-Setup-Keyence.ps1 dispatches by C:\Enrollment\keyence-model.txt
  (written by startnet.cmd in :keyence_submenu) and points
  InstallerRoot at C:\KeyenceInstall\<model>. DXSETUP probe widened
  to all three Program Files paths (VR-3000 G2, VR-5000, VR-6000).
- startnet.cmd: :keyence_submenu picks vr3000/vr5000/vr6000,
  :skip_keyence_stage xcopy block selectively stages chosen model bundle,
  pc-subtype.txt also written = drops directly into existing GE-Enforce
  PCSubType wiring (looks for gea-shopfloor-keyence-<model>\manifest.json
  on the tsgwp00525 share for ongoing enforcement, no dispatcher change
  needed).
- sync-keyence.sh mirrors sync-waxtrace.sh pattern.

Verified silent MSI install for VR-3000 G2 v2.5.0 and VR-5000 v3.3.1 on
the win11 VM 2026-05-18 with /qn /norestart ALLUSERS=1 REBOOT=ReallySuppress
TRANSFORMS=1033.mst. boot.wim on 172.16.9.1 wimupdate'd with the new
startnet.cmd.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 16:34:20 -04:00
cproudlock
7f097013fc BIOS sub-stage: switch to flag-file signaling, move push after W: copies
check-bios.cmd: drop literal `^` from BIOS_STATUS (caret survives quoted
SET so substring search for `->` never matched). Write X:\bios-fired.flag
on flash_done + staged paths so startnet.cmd can detect via if-exist.

startnet.cmd: replace `call set` substring-replace with `if exist
X:\bios-fired.flag`. Move push to after W:\Enrollment xcopy completes
(before Y: cleanup) so dashboard reflects "BIOS firmware update" stage
once file staging is done, matching user mental model of imaging order.

Tested flag-file logic in win11 VM cmd.exe: missing -> SKIPS, present
-> FIRES, removed -> SKIPS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 15:58:25 -04:00
cproudlock
9108b495c9 startnet.cmd: fix BIOS sub-stage detection (substring-replace trick)
Original used 'echo %BIOS_STATUS% | findstr /C:"->"' to detect whether
check-bios.cmd actually applied a firmware update. The '>' inside the
value (from check-bios writing e.g. 'updated 1.5.0 -> 1.6.0') gets
parsed by cmd.exe as a redirect operator BEFORE the pipe is set up.
Result: echo wrote a file named '1.6.0' in cwd instead of piping to
findstr. findstr saw no input, returned errorlevel=1, block never
fired. Plus a stray file got created in X:\Windows\System32.

Confirmed empirically in the win11 VM with test bat:
 - echo|findstr approach: SKIPS even when '->' present  (bug)
 - substring-replace: FIRES iff '->' present  (correct)

Fix: replace echo/pipe/findstr with a substring-replace test:

  call set "BIOS_STATUS_STRIPPED=%%BIOS_STATUS:->=%%"
  if not "%BIOS_STATUS%"=="%BIOS_STATUS_STRIPPED%" ( ... )

The '>' inside %VAR:->=...% is parsed as part of the substring
substitution token, not as a redirect. Yields true diff only when
the arrow was actually in BIOS_STATUS.

xcopy region was never impacted by the bug because subsequent
if-exist blocks didn't depend on the previous errorlevel state.
But the BIOS sub-stage push to the dashboard was silently broken.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 11:50:12 -04:00
cproudlock
d8c64bef2b Add conditional BIOS-update sub-stage on idx=1
winpe-status-push.ps1 now accepts -CurrentStage / -StageIndex
params so callers can override the default "WinPE: PESetup / WIM
apply" string. Backwards compatible.

startnet.cmd: after the existing initial WinPE status push,
inspect $BIOS_STATUS for the "->" marker that check-bios.cmd
writes when an update was actually applied or staged. If present,
fire a second idx=1 push with stage="WinPE: BIOS firmware update -
<status>". No-op for clean "up to date" / "no update in catalog"
runs.

imaging.html: at stage_idx=1 with "bios" in current_stage, swap
friendly label to "Updating BIOS firmware" with a do-NOT-power-off
hint. Bays without firmware updates show the default "Booting from
PXE" label as before.

boot.wim startnet.cmd updated via wimupdate so live PXE clients
pick it up at next boot.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 10:26:31 -04:00
cproudlock
ce604adcda Renumber PXE LAN from 10.9.100.0/24 to 172.16.9.0/24
Single-site bay-stuck issue at WJ: GE Intune Report IP script filters
Get-NetIPAddress on StartsWith("10.") and posts everything matching
to the GE Tines webhook. Bays at WJ get the PXE LAN 10.9.100.x IP
captured and reported -> GE backend tags bays as on a non-corp 10.x
subnet -> dynamic group eligibility for SFLD policy never matches.
Other GE sites work because their PXE LANs aren't on 10.x at all.

Renumber PXE LAN to RFC1918 172.16.9.0/24 so the GE filter naturally
skips wired PXE addresses without any disable-NIC dance.

Server-side already in flight (netplan dual-bound, dnsmasq scope +
boot URL repointed, blancco preferences + grub.cfg + iPXE GetPxeScript
all sed'd to 172.16.9.1). This commit is the playbook / scripts /
docs side: 109 hits across 35 files sed'd in one shot.

After this lands + boot.wim is rebuilt + bays renumber off DHCP,
the 10.9.100.1 binding will be dropped from netplan as the final
cleanup step.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 16:30:32 -04:00
cproudlock
a57ed5fd96 winpe: externalize WinPE-phase status push to scripts/winpe-status-push.ps1
The inline one-liner in startnet.cmd called Get-NetAdapter, which is
not available in WinPE's stripped PowerShell (no NetTCPIP module).
Errors silently swallowed by the surrounding try/catch - POST never
fired, dashboard never showed bays during the WIM-apply phase.

Externalize to a standalone .ps1 on the enrollment share:

  * Uses wmic (always present in WinPE 10+) for both serial AND mac
    instead of Get-CimInstance / Get-NetAdapter.
  * Logs every step to X:\Windows\Temp\winpe-status-push.log so a
    future "POST didn't fire" debug is one file read away.
  * startnet.cmd now just runs powershell -File Y:\scripts\winpe-status-
    push.ps1. Future edits to the push logic do NOT require a boot.wim
    rebuild; just edit the .ps1 on the share.

Mirror the existing pattern for run-enrollment.ps1 / wait-for-internet.ps1
/ migrate-to-wifi.ps1 (all already at /srv/samba/enrollment/scripts/).
Add the new file to the playbook's enrollment-scripts copy loop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:05:50 -04:00
cproudlock
8cd0c147d8 imaging: renumber stages to be time-monotonic (1=WinPE, 7=Intune ID)
Previously the stage indices reflected logical milestones but not the
order they fire in. Run-ShopfloorSetup posted idx=1 (start) and idx=4
(PPKG) - but 09-Setup-Keyence (inside per-type loop) ran BETWEEN them
and posted idx=5/6. The dashboard then "regressed" from 6 back to 4
when PPKG fired, making it look stuck at the per-type-complete card.

New numbering matches actual execution order:

  1 - WinPE: PESetup / WIM apply              (startnet.cmd)
  2 - Run-ShopfloorSetup: starting            (Run-ShopfloorSetup.ps1)
  3 - 09-Setup-<Type>: starting               (per-type)
  4 - 09-Setup-<Type>: complete               (per-type)
  5 - Run-ShopfloorSetup: PPKG enrollment     (Run-ShopfloorSetup.ps1)
  6 - Run-ShopfloorSetup: handoff to Monitor  (Run-ShopfloorSetup.ps1)
  7 - Monitor-IntuneProgress: Intune Device ID captured

services/imaging_status.py rewind threshold reverts to stage_index <= 1
now that WinPE startnet posts idx=1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 11:34:01 -04:00
cproudlock
4e018feaa0 webapp/imaging: rewind detection + WinPE-phase status push
services/imaging_status.py - if a new POST arrives with stage_index <= 1
that is lower than the cached stage_index, OR the previous run already
finished (status=succeeded|failed), reset the session: clear log_tail,
mint a fresh started_at, drop the status field so the in_progress
default re-applies. Preserves serial + records the previous run's
last_updated under previous_run_at for audit. Without this, a reimage
on the same bay would leave a stale 6/8 "succeeded" card visible until
the new run progressed past that index.

playbook/startnet.cmd - one-line PowerShell POST after the PXE menu
choice + enrollment-share mount, before PESetup.exe waits to start.
Captures BIOS serial via wmic, MAC via Get-NetAdapter, and posts:
  stage_index=2, current_stage="WinPE: PESetup / WIM apply".
Best-effort; try/catch swallows any network failure so a missing
webapp never blocks imaging. PXE clients will now appear on the
/imaging dashboard during WinPE phase instead of only post-PPKG.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 11:11:03 -04:00
cproudlock
6dcf96e40a Phase 3+4 rename reorg: repo dir renames + startnet.cmd menu
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>
2026-05-04 08:09:16 -04:00
cproudlock
7be5518fd7 Fix v2 imaging: copy common/ at imaging time + use $setupDir not $PSScriptRoot
Two bugs that have been silently masking GE-Enforce registration since
Stage 2a landed 2026-04-22, surfaced when v1 enforcers were retired
(commit 0badfc1) and could no longer cover for the missing v2 registration.

Bug 1: startnet.cmd at imaging time only xcopied Shopfloor\ and the
PCTYPE-specific dir from the imaging share to W:\Enrollment\shopfloor-setup\.
common\ was never copied. v1 dispatchers lived per-pctype and rode in via
the %PCTYPE% xcopy, so this was never noticed. v2's GE-Enforce.ps1 +
Register-GEEnforce.ps1 + lib\Install-FromManifest.ps1 all live in common\
and got skipped at imaging entirely.

Fix: add a third xcopy block for common\, mirroring the Shopfloor\ block
above it. Applies to playbook/startnet.cmd and startnet-template.cmd.

Bug 2: Run-ShopfloorSetup.ps1 line 288 set $commonSetupDir via
'Join-Path $PSScriptRoot common'. Run-ShopfloorSetup.ps1 lives at
C:\Enrollment\Run-ShopfloorSetup.ps1 (xcopied by startnet.cmd), so
$PSScriptRoot resolves to C:\Enrollment, and $commonSetupDir resolved
to C:\Enrollment\common - which is NOT where common\ lives even after
the bug 1 fix (correct path is C:\Enrollment\shopfloor-setup\common\).
The Test-Path -LiteralPath check on Register-GEEnforce.ps1 returned
false silently and GE-Enforce never registered.

Same bug existed for Register-MapSfldShare on line 321.

Fix: $PSScriptRoot -> $setupDir for both. $setupDir was already defined
on line 51 as Join-Path $enrollDir "shopfloor-setup", which is the path
the rest of the script uses consistently.

Pre-v1-cleanup, v1's per-pctype enforcer registrations on lines 322-357
(now deleted) ran independently and covered the gap, so PCs ended up
with v1 enforcers and the user thought v2 was running. Post-cleanup,
this bug means nothing gets registered.

PXE server has been patched directly: boot.wim re-baked with the new
startnet.cmd, /srv/samba/enrollment/shopfloor-setup/Run-ShopfloorSetup.ps1
replaced. New PXE-imaged PCs from this point forward will register
GE-Enforce correctly.

For PCs imaged before this fix: run Deploy-GEEnforce.ps1 from the SFLD
share's _meta/runtime/ to retrofit. Same one-liner used for promoting
v1 PCs to v2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 14:31:15 -04:00
cproudlock
eca9ee2b36 startnet.cmd: stage udc-backups to WinPE; mirror-from-gold: taxonomy layout
- playbook/startnet.cmd + startnet-template.cmd: after preinstall staging,
  xcopy Y:\pre-install\udc-backups to W:\PreInstall\udc-backups so UDC
  settings JSONs are available during image deployment. Harvested from
  live gold where this block existed but was never committed.

- scripts/mirror-from-gold.sh: update source paths to current taxonomy
  layout (pre-install/, installers-post/, blancco/, config/) and add
  ppkgs/, scripts/, shopfloor-setup/ sections. Added --delete for exact
  mirror semantics. Used to seed the spare PXE server at 10.9.100.2 on
  2026-04-16 from gold at 10.9.100.1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 21:37:23 -04:00
cproudlock
67845372b2 Harvest provtool diagnostics, enable ETW channel, skip Timeclock machine#
run-enrollment.ps1:
- Enable Provisioning-Diagnostics-Provider/Admin event log before invoking
  provtool (was disabled by default; no diagnostics survived early runs).
- After provtool returns, copy C:\ProgramData\Microsoft\Provisioning\*
  into C:\Logs\PPKG\ and snapshot HKLM\...\Sessions\* as
  provisioning-sessions.json, plus export the Admin event channel to
  Provisioning-Diagnostics-Admin.evtx. Gives us reviewable state
  without relying on provtool's failure-only diagnostic bundle.
- provtool arg order is positional path + /quiet + /source BPRT (verified
  against ProvEventLog from the PS cmdlet internal call).

startnet.cmd / startnet-template.cmd:
- Standard-Timeclock sub-type skips the machine-number prompt. Timeclock
  PCs do not use a machine number so forcing a prompt wasted tech time
  and left MACHINENUM at the 9999 default anyway. Machine sub-type is
  unaffected.
2026-04-15 14:22:43 -04:00
cproudlock
0292bc01ad Auto-flush stale SMB/conntrack state on DHCP lease, one-source PPKG model
Three changes that go together so a re-image never hits "System error 53":

1. dnsmasq dhcp-script hook (playbook/pxe-server-helpers/pxe-dhcp-hook.sh)
   Fires on every add/del lease event. Runs conntrack -D and ss -K for the
   client IP so any stale ESTABLISHED SMB session from a previous boot is
   cleared before the client reconnects. Runs as root (dnsmasq default).
   Wired into /etc/dnsmasq.conf via dhcp-script= directive in the playbook.

2. One-source PPKG (playbook/startnet.cmd + startnet-template.cmd)
   The 5 per-Office PPKG copies were bit-for-bit identical; only the
   filename differs because BPRT parses Office and Region out of the name.
   Store one source file (e.g. GCCH_Prod_SFLD_v4.11.ppkg) and construct
   the BPRT-tagged target filename at menu-selection time from variables:
     SOURCE_PPKG / PPKG_VER / PPKG_EXP / REGION / OFFICE
   copy /Y "Y:\ppkgs\%SOURCE_PPKG%" "W:\Enrollment\%PPKG%"
   Bumped PPKG_VER v4.10 -> v4.11 and PPKG_EXP 20260430 -> 20270430.
   Saves ~30G on disk per version.

3. run-enrollment.ps1 already committed in 5a9c3db uses provtool.exe
   directly (no PowerShell cmdlet 180s timeout). Included here because it
   is part of the same end-to-end PPKG path.
2026-04-15 09:03:16 -04:00
cproudlock
d6776f7c7f Reorganize repo, enrollment share taxonomy, Blancco USB-build fixes, v4.10 PPKGs
Workstation reorganization:
- All build/deploy/helper scripts moved into scripts/ (paths updated to use
  REPO_ROOT instead of SCRIPT_DIR so they resolve sibling dirs from the new
  depth)
- New config/ directory placeholder for site-specific overrides
- Removed stale: mok-keys/, test-vm.sh, test-lab.sh, setup-guide-original.txt,
  unattend/ (duplicate of moved playbook/FlatUnattendW10.xml)
- README.md and SETUP.md structure listings updated, dead "Testing with KVM"
  section removed
- .claude/ gitignored

Enrollment share internal taxonomy (forward-looking; existing servers
unaffected since they keep their current boot.wim with flat paths):
- Single SMB share kept (WinPE only mounts one Y: drive), but content now
  organised into ppkgs/, scripts/, config/, shopfloor-setup/, pre-install/{bios,
  installers}, installers-post/cmm/, blancco/, logs/
- README.md deployed to share root explaining each subdir
- New playbook tasks deploy site-config.json + wait-for-internet.ps1 +
  migrate-to-wifi.ps1 explicitly (were ad-hoc on legacy servers)
- BIOS subdir moved into pre-install/bios/, preinstall/ renamed to pre-install/
- startnet.cmd + startnet-template.cmd updated with new Y:\subdir\ paths
- Bumped GCCH PPKG references v4.9 -> v4.10

Blancco USB-build fixes (so next fresh USB install boots Blancco end-to-end
without the manual fixup we did against GOLD):
- grub-blancco.cfg: kernel/initrd switched HTTP -> TFTP (GRUB's HTTP module
  times out on multi-MB files); added modprobe.blacklist=iwlwifi,iwlmvm,btusb
  (WiFi drivers hang udev on Intel business PCs)
- grubx64.efi rebuilt from updated cfg
- Playbook task added to create /srv/tftp/blancco/ symlinks pointing at the
  HTTP-served binaries

run-enrollment.ps1: OOBEComplete is now set AFTER PPKG install (Win11 22H2+
hangs indefinitely if OOBEComplete is set before the bulk-enrollment PPKG runs).

Also includes deploy-bios.sh / pull-bios.sh / busybox-static / models.txt
that were sitting untracked at the repo root.
2026-04-14 16:01:02 -04:00
cproudlock
f3211dfd29 CMM test iteration: desktop shortcuts, rename to 09-Setup-*, defer NIC re-enable
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>
2026-04-11 21:03:09 -04:00
cproudlock
c595d3b9cb Shopfloor unattend: move Orders 4/5 logic to external PS1 scripts
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>
2026-04-11 16:50:58 -04:00
cproudlock
ee7d3bad66 Shopfloor imaging: CMM type, Configure-PC override fix, serial drivers
- 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>
2026-04-11 12:58:47 -04:00
cproudlock
bc123c1066 Machine number input at PXE menu for Standard PCs
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>
2026-04-11 08:50:02 -04:00
cproudlock
c06310f5bd Replace all Unicode characters with ASCII in playbook scripts
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>
2026-04-10 13:23:11 -04:00
cproudlock
ed803539e0 PC profiles: per-type/sub-type config + Standard Timeclock/Machine menu
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>
2026-04-10 11:19:51 -04:00
cproudlock
0aaf049942 Extract site-specific values to site-config.json
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>
2026-04-10 11:11:35 -04:00
cproudlock
b13e34c05a Imaging chain: Stage-Dispatcher + PPKG reboot + unattended sync_intune
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>
2026-04-10 09:55:00 -04:00
cproudlock
a1a78e2ba3 PXE preinstall pipeline + Set-MachineNumber helper for Standard PCs
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>
2026-04-08 14:06:26 -04:00
cproudlock
76165495ff Shopfloor PC type system, webapp enhancements, slim Blancco GRUB
- Shopfloor PC type menu (CMM, WaxAndTrace, Keyence, Genspect, Display, Standard)
- Baseline scripts: OpenText CSF, Start Menu shortcuts, network/WinRM, power/display
- Standard type: eDNC + MarkZebra with 64-bit path mirroring
- CMM type: Hexagon CLM Tools, PC-DMIS 2016/2019 R2
- Display sub-type: Lobby vs Dashboard
- Webapp: enrollment management, image config editor, UI refresh
- Upload-Image.ps1: robocopy MCL cache to PXE server
- Download-Drivers.ps1: Dell driver download pipeline
- Slim Blancco GRUB EFI (10MB -> 660KB) for old hardware compat
- Shopfloor display imaging guide docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:25:07 -04:00
cproudlock
6d0e6ee284 BIOS check fix, parallel downloads, shopfloor hardening
- 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>
2026-03-23 11:02:36 -04:00
cproudlock
5de807143b Fix WinPE SMB auth and timeout commands, restore autoinstall disk match
- 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>
2026-02-16 15:32:39 -05:00
cproudlock
0da52cb083 Auto-reboot after imaging, auto-download pip-wheels in build scripts
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>
2026-02-12 16:56:14 -05:00
cproudlock
1a5c4f7124 Eliminate USB requirement for WinPE PXE boot, add image upload script
- 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>
2026-02-12 16:40:27 -05:00