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