Closes the "how do sites actually deploy GE-Enforce" gap (esp. OOBE-ppkg sites without a PXE/WinPE step). Site-neutral + imaging-path independent. - plugins/geenforce/client/Install-GEEnforce.ps1: a bootstrap that writes the PC's identity (C:\Enrollment\pc-type.txt is what determines the PC type; plus machine-number/cmm version/cmm id/site-config as needed), sets the shopdb BaseUrl + token in HKLM:\SOFTWARE\GE\ShopDB, deploys the client kit, optionally copies the engine from -EngineSource, and registers the SYSTEM scheduled task (at logon + every N min). Idempotent; fails loud (installer, not the fail-safe runtime). Engine is REFERENCED not vendored - it belongs to the GE-Enforce framework; the script warns if absent but still labels the PC. - docs/GE-ENFORCE-DEPLOY.md: the deploy contract - the three things a PC needs (client, identity, credential), the identity table (what determines PC type, no auto-detection - the provisioner supplies it; shopdb cannot set it at imaging), and how to invoke per path (PXE step, OOBE ppkg via ProvisioningCommands, Intune, manual), the engine boundary, and verification. - Cross-linked from docs/GE-ENFORCE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
330 lines
16 KiB
Markdown
330 lines
16 KiB
Markdown
# 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 `gea-shopfloor-<type>/manifest.json`, then an optional `<type>-<subtype>` manifest | 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 SHAPE (field names) but are run by different
|
|
runners with different capabilities. Preinstall is a one-shot at imaging that
|
|
implements only `Type=MSI` and `Type=EXE`, with only `Registry` / `File`
|
|
detection (other types/detections are skipped). Runtime is the continuous
|
|
enforcement loop and implements the full Type + DetectionMethod matrix below.
|
|
|
|
### 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 `gea-shopfloor-<pctype>/manifest.json`, then a `<pctype>-<subtype>`
|
|
manifest if one exists. 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 string matches exactly (fleet convention is a 4-part string like 6.4.5.0; the engine does a raw string compare, it does not enforce 4 parts) |
|
|
| 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: a tagged entry applies when it equals the bay's
|
|
resolved PC-DMIS version (`C:\Enrollment\cmm\version.txt`). IMPORTANT: if no
|
|
version is resolved (file missing/empty - a pre-picker bay), ALL tagged
|
|
entries apply (deliberate legacy "install-all" behavior), so such a bay gets
|
|
every PC-DMIS version, not none. 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` - the imaging PC type (which scope to run). `pc-subtype.txt` is
|
|
LEGACY (no longer written at imaging since the 2026-05-04 rename reorg; the
|
|
dispatcher still honors it if present on older fleet PCs).
|
|
- `machine-number.txt` - the bay number FALLBACK; the eDNC/DNC registry
|
|
`MachineNo` value wins if present. `9999` is the imaging placeholder by
|
|
convention - the enforcement engine does NOT special-case it; it is simply a
|
|
value that won't match a real bay number in a `TargetMachineNumbers` gate.
|
|
(The 9999-skip you may see is only in the status write-back, not enforcement.)
|
|
- `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).
|
|
|
|
Note: all of the identity files above (pc-type, machine-number, cmm version,
|
|
site-config) are written in WinPE at the PXE menu, BEFORE the image boots - the
|
|
preinstall phase already reads them. What happens post-imaging is only Intune
|
|
enrollment + the Azure DSC credential (see the timeline below).
|
|
|
|
---
|
|
|
|
## 3. When GE-Enforce installs / takes over (the imaging timeline)
|
|
|
|
This is the "when to implement it during imaging" question. The order is:
|
|
|
|
```
|
|
[0] WinPE / PXE menu (BEFORE the image boots)
|
|
- identity written to C:\Enrollment: pc-type.txt, machine-number.txt,
|
|
cmm/version.txt, site-config.json (startnet.cmd). The PC already knows
|
|
what it is before Windows starts.
|
|
|
|
|
v
|
|
PXE image applied, Windows boots
|
|
|
|
|
v
|
|
[1] PREINSTALL (00-PreInstall runner runs preinstall.json ONCE)
|
|
- reads the step-0 identity files, then installs the foundation:
|
|
PowerShell 7, VC++ redists, Oracle Client, Adobe Reader, HostExplorer,
|
|
serial drivers, Display kiosk app, ... (things later runtime apps need)
|
|
- preinstall implements MSI/EXE + Registry/File detection only
|
|
|
|
|
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 + every ~5 min + shift windows) are
|
|
registered as part of the image / shopfloor setup
|
|
|
|
|
v
|
|
[3] ENROLLMENT (post-imaging)
|
|
- Intune / GCCH enrollment, THEN Azure DSC provisions the SFLD share
|
|
credential into HKLM:\SOFTWARE\GE\SFLD\Credentials
|
|
- (the identity files already exist from step 0 - enrollment adds only the
|
|
credential, which is what unblocks runtime)
|
|
|
|
|
v
|
|
[4] FIRST LOGON -> RUNTIME ENFORCEMENT BEGINS
|
|
- the scheduled task runs GE-Enforce.ps1: mount share, run common + the
|
|
PC-type (+ subtype) manifests, 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
|
|
scope carries an optional `computertypeid` reference field, but the collector's
|
|
imaging-pc-type -> ComputerType mapping is configured separately at
|
|
Settings > Collector PC Types.)
|
|
- **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-*` 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`.
|
|
- Agent deployment (per PC, any imaging path): `docs/GE-ENFORCE-DEPLOY.md` +
|
|
`plugins/geenforce/client/Install-GEEnforce.ps1`.
|
|
- Design + cutover plan: `docs/proposals/ge-enforce-plugin.md`.
|