docs/GE-ENFORCE.md - operator-facing guide grounded in the real engine + manifests (Fable-verified analysis): how GE-Enforce works (preinstall vs runtime phases, the enforce loop, manifest scopes/entries, self-heal detection, gates, enrollment), WHEN it installs/takes over in the imaging timeline (preinstall at imaging -> GE-Enforce laid down -> enrollment provisions creds -> runtime enforcement from first logon), how the shopdb plugin manages it (Manifests authoring + contextual targeting + simulate + publish/rollback + Export to Share Milestone 1, Enforcement Reports), day-to-day IT tasks, and a reference index. Complements GE-ENFORCE-CLIENT.md (client contract) and the proposal (plan). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
14 KiB
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:
- The engine + dispatcher on each PC - PowerShell that reads a manifest and enforces it every logon and periodically.
- 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-<type>/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):
- Read the PC's identity from
C:\Enrollment\(see 2.4). - 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). - Run the engine (
Install-FromManifest.ps1) againstcommon/manifest.json, then againstgea-shopfloor-<pctype>/manifest.json. Common runs first so shared prerequisites (e.g. Oracle Client) land before type-specific apps that depend on them. - 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 aPCTypesfilter 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 setPCTypes(the manifest already only runs there). Keyence is the exception: it usesPCTypesfor hardware SUBTYPE targeting (keyence-vr6000vskeyence-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 likeStandardmap tocollections/nocollections/common). Fleet-widecommonuses 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.
_CmmVersiongating 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
<shareroot>/<scope>/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-*overpreinstall.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.