From 6d5fee786ccd377eed1dda335af6edca4a985853 Mon Sep 17 00:00:00 2001 From: cproudlock Date: Thu, 6 Aug 2026 17:36:17 -0400 Subject: [PATCH] Proposal: fold in what a day of fixing the current pipeline taught New section 9a - five design rules, each from a bug fixed on 2026-08-06 and each cheap to honour in a new design but expensive to retrofit: Absent and empty are different. Four scripts tested config as "not null AND Count > 0", so an explicitly empty list fell through to a hardcoded default - a kiosk configured with "desktopApps": [] was given the full machine-tool set. Configuring "none" produced "everything". Deny by default. preinstall.json had 16 of 21 entries at PCTypes ['*'] and the SMB manifest 18 of 25 unfiltered. Both filters worked perfectly; they just were not applied. Gate at the point of action. Call-site gates on PC type were bypassed by the finalization phase, silently. The image carries configuration. WJ Shopfloor.lnk is inside the WIM, so no pipeline change removes it. The ppkg is a hard boundary. Chrome, RealVNC, Tanium, CyberArk and the rest come from the package - thinning the image does not thin them. Also corrects a premise in section 4.2: a first-boot selection UI cannot assume the API is reachable, because ShopDB needs the AESFMA wifi SSID and the imaging LAN has no route to it. Points at Install-ShopdbKiosk-WhenOnline.ps1 as the reusable shape - arm a task at imaging, do nothing until the API answers, act once, verify, self-remove. Plus two risks, the diagnostics collector in the reference map, and a gotchas section: HardwareDriver.json defeats ConvertFrom-Json on every PowerShell version, Compress-Archive caps at 2 GB against an 8 GB package, and PowerShell Core normalises backslashes on Linux so bay scripts can be tested here. --- docs/ZERO-TOUCH-IMAGING-PROPOSAL.md | 103 ++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/docs/ZERO-TOUCH-IMAGING-PROPOSAL.md b/docs/ZERO-TOUCH-IMAGING-PROPOSAL.md index c9c49fe..9abd0bd 100644 --- a/docs/ZERO-TOUCH-IMAGING-PROPOSAL.md +++ b/docs/ZERO-TOUCH-IMAGING-PROPOSAL.md @@ -114,6 +114,20 @@ Shopfloor -> Collections | No Collections | Common | Heattreat | Part Marker The tool selection is the important half: it drives PC-DMIS version, FormTracePak version, DODA flag, NTLARS/eDNC registry, UDC settings, machine number. +**The network constraint that shapes this.** ShopDB is only reachable once the +bay has joined the **AESFMA wifi SSID** - not on the imaging LAN, and not +necessarily at first boot. So a first-boot selection UI cannot assume the API is +there. It must either work offline and sync later, or wait. Confirmed on +579C144: the bay held `172.16.9.81` and `172.24.19.142`, neither in the +production ranges, and `sync_intune` sat retrying every 30 seconds. + +**The pattern that solves it, already built and worth reusing.** +`Install-ShopdbKiosk-WhenOnline.ps1` is a SYSTEM task armed at imaging that does +nothing until ShopDB answers, then acts once, verifies, and unregisters itself. +Any step in the new architecture that needs the API - selection sync, config +pull, enrollment completion - can use the same shape rather than assuming +connectivity. + **Design points:** - **Server is the source of truth.** The selection POSTs to ShopDB keyed on @@ -401,6 +415,78 @@ silently. A clean slate is the moment to fix that. See `docs/OWNERSHIP.md`. --- +## 9a. Design rules learned the hard way (2026-08-06) + +A day of fixing the current pipeline produced five rules. They are cheap to +honour in a new design and expensive to retrofit. + +### Absent and empty are different + +Four separate scripts tested config as +`$null -ne $cfg -and $cfg.Count -gt 0`, so an explicitly empty list fell through +to a hardcoded default. A Display kiosk configured with + +```json +"desktopApps": [], "startupItems": [], "taskbarPins": [] +``` + +was given UDC, eDNC, NTLARS, WJ Shopfloor, Defect Tracker and Plant Apps - +**configuring "none" produced "everything"**. Plant Apps launched +`msedge --new-window` on a screen with no keyboard. + +Measured, not assumed: an empty JSON array round-trips as `Object[]` with +`Count = 0`, and an absent key as `$null`. They are distinguishable; the code +simply conflated them. + +*Rule: in the API model, "no items" and "not configured" must be different +values on the wire, and the default for an unrecognised or missing scope is +NOTHING, not a fallback set.* + +### Deny by default, always + +`preinstall.json` had 16 of 21 entries at `PCTypes: ["*"]`, so a kiosk installed +Adobe, OpenText, Defect Tracker, the serial drivers and twelve legacy VC++ +redistributables. The SMB manifest had 18 of 25 entries unfiltered. In both +cases the filter worked perfectly - it simply was not applied. + +*Rule: every payload declares which PC types it is for. An undeclared payload +installs nowhere and fails validation, rather than installing everywhere.* + +### Gate at the point of action, not the call site + +`Run-ShopfloorSetup.ps1` gated both the S: mapper and the machine-number prompt +on PC type. Both registered anyway on a Display, with no "Skipping" line in the +log - something in the finalization phase reaches those registrars past the +call-site gate. The fix was to gate inside each registrar. + +*Rule: authority checks belong with the thing being done. A caller-side check is +a convenience, never the control.* + +### The image itself carries configuration + +`WJ Shopfloor.lnk`, dated April, sits in the all-users Startup folder of every +bay - **inside the WIM**. No script change removes it, and no amount of thinning +the pipeline touches it. + +*Rule: "thin image" must include auditing what is baked into the WIM, not just +what the pipeline adds afterwards.* + +### The provisioning package is a hard boundary + +These are installed by the ppkg, not by anything PXE controls: + +``` +Google Chrome (+2 updater tasks) RealVNC Tanium +CyberArk EPM .NET 3.5 Report IP Sysinternals Autologon DSC +PowerShell 7 x64 AND x86 +``` + +*Rule: thinning the image does not thin the package. Anything that must not be +on a kiosk and comes from the ppkg needs a package change or an explicit +post-install removal - it cannot be solved by scoping our own payloads.* + +--- + ## 10. Phased delivery Each phase is useful standing alone. No phase requires the next. @@ -454,6 +540,8 @@ Each phase is useful standing alone. No phase requires the next. | Re-implementing PESetup | ongoing MCL format tracking + a likely compliance conversation | scope it against "one click per build" - zero-touch is the only thing that needs it | | Big payloads over the corporate network | PC-DMIS, FormTracePak ISOs (~2 GB each), Keyence | GE-Enforce supports `smb` / `http` / `inline` per entry - decide per payload | | Three images drift apart | drift caused the 2026-08-06 outage | prefer one image; `share-drift.py` guards what remains | +| API unreachable at first boot | ShopDB needs AESFMA; the imaging LAN cannot reach it | wait-for-network task pattern (ยง4.2); never assume connectivity | +| ppkg-delivered apps on a kiosk | Chrome, RealVNC, Tanium etc. are outside PXE control | package change or explicit post-install removal - scoping our payloads cannot fix it | --- @@ -490,6 +578,7 @@ Each phase is useful standing alone. No phase requires the next. | `scripts/lint-unattend.py` | schema limits: Path 259, CommandLine 1024, Description 256 | | `scripts/lint-driver-catalogue.py` | reimplements `GetDriverByModel` | | `scripts/share-drift.py` | repo vs live share, git-owned vs unreconciled | +| `playbook/shopfloor-setup/Collect-ImagingDiagnostics.ps1` | one-pass bay diagnostics - identity, enrollment, installed apps, all four autostart surfaces, GE-Enforce reachability | | `scripts/build-pctype-media.py` | per-PCTYPE media views (retired by Phase 6) | ### 13.2 PXE server (`pxe@172.16.9.1`) @@ -549,6 +638,20 @@ C:\Windows\Panther\ setupact.log, setuperr.log, unattend.xml, PESetu C:\Windows\Panther\UnattendGC\ oobeSystem pass ``` +### 13.5a Gotchas any new tooling will hit + +- **`HardwareDriver.json` cannot be read by `ConvertFrom-Json`.** It carries both + casings of the same fields (`fileName`/`FileName`, `destinationDir`/ + `DestinationDir`) and PowerShell rejects the document - on 5.1 *and* 7. + `-AsHashtable` is PS6+ and WinPE runs 5.1. Extract fields by regex, or parse in + Python where duplicate keys are tolerated. +- **`Compress-Archive` caps at 2 GB**, and `run-enrollment` harvests the 8 GB + provisioning package into `C:\Logs\PPKG`, so any log collection must exclude + `*.ppkg`. +- **Backslash-vs-forward-slash**: PowerShell Core normalises `\` to `/` on Linux, + so `Join-Path` with Windows separators works cross-platform - handy for testing + bay scripts on the dev box. + ### 13.6 How to re-derive things ```bash