diff --git a/docs/GE-ENFORCE.md b/docs/GE-ENFORCE.md new file mode 100644 index 0000000..80ba106 --- /dev/null +++ b/docs/GE-ENFORCE.md @@ -0,0 +1,300 @@ +# GE-Enforce: concepts, the shopdb plugin, and imaging-time integration + +This guide explains how GE-Enforce works, how the shopdb `geenforce` plugin +manages it, and when GE-Enforce installs and takes over during the imaging +process. It is written for site IT. + +It pairs with two companion docs: +- `docs/GE-ENFORCE-CLIENT.md` - the client fetch/report contract + the reference + PowerShell kit (`plugins/geenforce/client/`). +- `docs/proposals/ge-enforce-plugin.md` - the design/plan and the staged cutover. + +The ground truth for behavior is the engine itself +(`Install-FromManifest.ps1`) and the on-share manifests; this guide describes +what they do, it does not replace them. + +--- + +## 1. What GE-Enforce is + +GE-Enforce is a **desired-state enforcement** system for shopfloor PCs. Instead +of a one-time install during imaging, it continuously makes each PC match a +declared list of what should be installed - and RE-installs anything that drifts +(uninstalled, corrupted, or overwritten). It is the shopfloor equivalent of a +lightweight, air-gapped-friendly configuration-management agent. + +Two things make up the system: + +1. **The engine + dispatcher on each PC** - PowerShell that reads a manifest and + enforces it every logon and periodically. +2. **The manifests** - JSON files that declare, per imaging PC type, what to + install / copy / write and how to detect whether it is already correct. + +The shopdb `geenforce` plugin adds a third piece: it lets you **author, publish, +and version those manifests in shopdb** (instead of hand-editing JSON on a file +share) and **see what every PC actually did** (fleet compliance reporting). + +--- + +## 2. How GE-Enforce works (the framework) + +### 2.1 Two phases + +Every shopfloor PC is governed in two distinct phases: + +| Phase | When | Runs what | Purpose | +|---|---|---|---| +| **Preinstall** | ONCE, at imaging | `preinstall.json` (via the imaging `00-PreInstall` step) | Day-zero foundation: PowerShell 7, the VC++ redistributable matrix, Oracle Client, Adobe Reader, HostExplorer, serial drivers, etc. "Install once at imaging, no drift correction." | +| **Runtime** | EVERY logon + periodically | `common/manifest.json` then the PC's `gea-shopfloor-/manifest.json` | Ongoing enforcement + self-heal: app versions, config-file drift, registry drift, per-cycle scripts (asset report, VNC firewall, EventSaver), version-gated installs. | + +The two phases share the SAME entry schema but are run by different runners. +Preinstall is a one-shot at imaging; runtime is the continuous enforcement loop. + +### 2.2 The runtime loop, step by step + +On each cycle (`GE-Enforce.ps1` on the PC): + +1. Read the PC's identity from `C:\Enrollment\` (see 2.4). +2. Look up the SFLD share credential in the registry and **mount the share** + (SYSTEM cannot reach the share as its computer account, so it mounts as the + provisioned SFLD user - `net use W: ...`). If no credential yet, exit 0 and + retry next cycle (Azure DSC has not provisioned it). +3. Run the engine (`Install-FromManifest.ps1`) against `common/manifest.json`, + then against `gea-shopfloor-/manifest.json`. Common runs first so + shared prerequisites (e.g. Oracle Client) land before type-specific apps that + depend on them. +4. Write a status file back to the share (and, in the shopdb model, POST a + report - see 4.3). + +Every failure is non-fatal (exit 0) so a network blip or a not-yet-provisioned +credential never blocks or breaks a PC. + +### 2.3 The manifest: scopes and entries + +A manifest is `{ "Version", "_comment", "Applications": [ entry, ... ] }`. Each +imaging PC type is a **scope** with its own manifest, plus the fleet-wide +`common` scope: + +- `common` - runs on EVERY PC type; entries use a `PCTypes` filter to target + subsets (e.g. "EventSaver on collections + heattreat, but not CMM"). +- `gea-shopfloor-collections`, `-nocollections`, `-cmm`, `-keyence`, `-common` + (lab/timeclock), `-genspect`, `-heattreat`, `-partmarker`, `-waxtrace` - each + runs only on PCs of that type, so its entries usually do NOT set `PCTypes` + (the manifest already only runs there). Keyence is the exception: it uses + `PCTypes` for hardware SUBTYPE targeting (`keyence-vr6000` vs `keyence-vr3000`). + +Each **entry** declares one action. Its `Type` picks the action: + +| Type | Action | +|---|---| +| MSI / EXE / CMD / BAT | run an installer with `InstallArgs` | +| PS1 | run a script from the share | +| INF | install a driver via `pnputil` | +| File | copy `Source` -> `Destination` | +| Registry | write a value | + +### 2.4 Self-heal via detection + +Every entry has a `DetectionMethod` that decides whether the action fires: + +| Method | Means "already correct" when... | +|---|---| +| Registry | the key/value exists (optionally equals a value) | +| File | the file exists | +| FileVersion | the file's version equals an exact 4-part string | +| Hash | the file's SHA256 matches (case-insensitive) | +| MarkerFile | a marker file exists (the engine writes it after a clean install) | +| ValueMatches | a registry value equals the entry's target | +| pnputil | a driver matching a pattern is present | +| Always / (none) | fires EVERY cycle (used for per-cycle scripts) | + +If detection says "not correct," the action runs. That is the self-heal: delete +`DncMain.exe` and next cycle re-installs eDNC; corrupt a config file whose Hash +no longer matches and next cycle re-copies it. **Entry order is execution +order** - config-restore entries sit AFTER their installer so a mid-cycle vendor +overwrite is healed on the same cycle. + +### 2.5 Targeting gates (all ANDed) + +An entry can be narrowed by any combination of: + +- `PCTypes` - which PC types (alias-aware: old names like `Standard` map to + `collections`/`nocollections`/`common`). Fleet-wide `common` uses this heavily. +- `TargetHostnames` - specific hostnames (supports `*` wildcards). +- `TargetMachineNumbers` - specific bay machine numbers (e.g. Okuma bays). +- `_CmmVersion` - CMM PCs only: install only on bays resolved to that PC-DMIS + version (requires engine lib >= 2.6). +- `PCTypesStrict` - disables alias expansion (PREINSTALL runner only; the runtime + engine ignores it). + +Different PC types have different niche gates: CMM uses a version gate, Keyence a +model subtype, Collections per-bay machine numbers. The shopdb editor shows only +the gates a given scope actually uses (see 4.1). + +### 2.6 What the PC needs to know about itself (enrollment) + +The runtime engine reads the PC's identity from `C:\Enrollment\`: + +- `pc-type.txt` / `pc-subtype.txt` - the imaging PC type (which scope to run). +- `machine-number.txt` - the bay number (the eDNC/DNC registry value wins if + present; the txt is the fallback). `9999` = placeholder/skip. +- `cmm/version.txt` - CMM bays only: the resolved PC-DMIS version for `_CmmVersion`. +- `site-config.json` - the share root and site settings. +- SFLD credentials at `HKLM:\SOFTWARE\GE\SFLD\Credentials` - provisioned by + Azure DSC after enrollment (this is what gates the runtime phase starting). + +--- + +## 3. When GE-Enforce installs / takes over (the imaging timeline) + +This is the "when to implement it during imaging" question. The order is: + +``` +PXE image applied + | + v +[1] PREINSTALL (00-PreInstall-* runs preinstall.json ONCE) + - foundational software: PowerShell 7, VC++ redists, Oracle Client, + Adobe Reader, HostExplorer, serial drivers, Display kiosk app, ... + - these are the things later runtime apps depend on + | + v +[2] GE-ENFORCE ITSELF is laid down during imaging + - the dispatcher (GE-Enforce.ps1), the engine lib (Install-FromManifest.ps1), + and a scheduled task (at-logon + periodic) are placed on the PC as part + of the image / common bootstrap + | + v +[3] ENROLLMENT + - C:\Enrollment\* written: pc-type, machine number, cmm version, site-config + - Azure DSC provisions the SFLD share credential in the registry + | + v +[4] FIRST LOGON -> RUNTIME ENFORCEMENT BEGINS + - the scheduled task runs GE-Enforce.ps1: mount share, run common + the + PC-type manifest, install/self-heal, report + - repeats every logon + periodically forever after +``` + +Key points on timing: + +- **Preinstall (step 1) is the imaging-time install.** Put anything that must + exist before first logon, or that never needs drift correction, here (runtimes, + redistributables, drivers). It runs once and is done. +- **Runtime enforcement (step 4) does not start until enrollment (step 3) + provisions the SFLD credential.** Before that, GE-Enforce exits 0 each cycle + and waits. So a freshly imaged PC that is not yet enrolled is inert, by design. +- **The engine lib version matters.** `_CmmVersion` gating needs lib >= 2.6 on + the PC; deploy the lib before a manifest that uses it. +- Some apps appear in BOTH phases: preinstalled at imaging for day-zero, then + carried by a runtime entry so drift is corrected later (Oracle, UDC, Adobe, + HostExplorer, Defect Tracker). + +Rule of thumb: **imaging-time (preinstall) = foundation that must be there or +never drifts; runtime = everything that needs to stay correct over the PC's +life.** + +--- + +## 4. How the shopdb plugin manages this + +The `geenforce` plugin turns the manifest from hand-edited JSON on a share into +shopdb data you author, version, publish, and monitor. It lives under the +top-level **GE-Enforce** section (Manifests | Enforcement Reports), not Settings, +because it is a full management surface. + +### 4.1 Manifests - authoring (GE-Enforce > Manifests) + +- **PC Types (scopes):** each imaging PC type is a row; add/edit/delete. The + ComputerType mapping the collector uses lives here too. +- **Entries:** an ordered list (Up/Down = the execution-order contract). Add/Edit + opens a typed form: the payload fields switch on `Type` (MSI shows Installer + + InstallArgs, PS1 shows Script + Args, File shows Source + Destination, Registry + shows the Reg* fields), a detection block, an InUseCheck editor, and a + **Targeting** section that shows only the gates the scope uses (CMM shows the + version gate; the common/preinstall scopes show PC types; a scope whose entries + use machine numbers shows those) with a "Show all targeting options" escape + hatch. +- **Simulate ("what would a PC get?"):** enter a PC profile (type, subtype, + hostname, machine number, CMM version) and see which entries apply and why the + rest are filtered - without reading a PowerShell log. +- **Publish / Versions / Roll Back:** editing changes a DRAFT only. Publish + freezes an immutable version; PCs are only ever served the published version; + Roll Back restores an earlier one. History (date, author, note) per version. + +### 4.2 Milestone 1 - export to the share (engine unchanged) + +Today the enforcement engine still reads manifests from the SFLD share. The +plugin's **Export to Share** button writes the current published manifest to +`//manifest.json` (backing up the old file to `_meta/history` +first). So the workflow is: + +**author + publish in shopdb -> Export to Share -> the unchanged engine picks it +up next cycle.** + +Nothing about the engine, the share layout, or the PCs changes. Rollback is +restoring the `_meta/history` backup (or re-publishing an older version and +re-exporting). This is the safe first milestone: all the authoring benefit, zero +client risk. + +Configure the share root once at the top of the Manifests page. + +### 4.3 Enforcement Reports - fleet compliance (GE-Enforce > Enforcement Reports) + +Each PC reports its enforcement result back to shopdb (see the client kit). The +Reports page shows, per PC: + +- **Received** - did the PC apply the latest published version? (applied vs + latest). "behind" means it has not picked up your newest publish yet. +- **Status** - `ok` (nothing needed), `selfhealed` (drift corrected), `failed`. +- **Counts** - installed / skipped / failed, plus per-entry detail (action, + self-heal flag, exit code, message) in the row's Detail view. + +This is the observed-state half of the loop: the manifest is what SHOULD be +installed; the report is what each PC ACTUALLY did. + +### 4.4 The client side (per PC) + +The engine sources the manifest and reports results using the reference kit in +`plugins/geenforce/client/` (`ShopdbEnforceClient.psm1` + +`Invoke-ShopdbEnforce.ps1`), configured from `HKLM:\SOFTWARE\GE\ShopDB` +(BaseUrl + a `geenforce.fetch`/`geenforce.report` service token). See +`docs/GE-ENFORCE-CLIENT.md` for the fetch/report contract, the last-known-good +cache, shadow mode, and the staged cutover from share-sourced to shopdb-sourced +manifests. Until that cutover, the client only REPORTS; the manifest still comes +from the share via Export to Share (4.2). + +--- + +## 5. Day-to-day: common tasks + +All in GE-Enforce > Manifests. No PowerShell, no editing JSON on the share. + +- **Add an app to a PC type:** open the PC type, Add Entry, pick the Type (the + form adapts), fill the installer + detection + any targeting, place it in order + with Up/Down (config restores go BELOW their installer), Preview, Publish, then + Export to Share. +- **Bump an app version:** drop the new installer in the scope's `apps/` folder + on the share, open the entry, update the Installer filename + the Detection + value (the new version), Publish, Export to Share. PCs self-heal next cycle. +- **Roll back a bad publish:** the PC type's Versions list -> Roll Back to the + last good version -> Export to Share. +- **Canary a risky change:** add the one test PC under Target hostnames (via + "Show all targeting options"), Publish; when happy, remove the filter and + Publish again. +- **Check "did PC Y get app X":** use Simulate with that PC's type / machine + number / CMM version; and check Enforcement Reports for what it actually did. + +--- + +## 6. Reference + +- Engine (behavior ground truth): `Install-FromManifest.ps1` (lib >= 2.6). +- Dispatcher: `GE-Enforce.ps1` (mount + run common then type scope). +- Preinstall runner: `00-PreInstall-*` over `preinstall.json` (imaging-time). +- shopdb model + API: `plugins/geenforce/` (models, importer/serializer, + filters mirror, service, routes). +- Behavioral parity gate (proves the shopdb model round-trips the real + manifests): `plugins/geenforce/parity.py` + `flask geenforce parity`. +- Client kit + contract: `plugins/geenforce/client/`, `docs/GE-ENFORCE-CLIENT.md`. +- Design + cutover plan: `docs/proposals/ge-enforce-plugin.md`.