Fix 7 factual errors in GE-Enforce doc (Fable fact-check vs real source)
All checks were successful
CI / backend (push) Successful in 1m39s
CI / naming (push) Successful in 2s
CI / frontend (push) Successful in 8s

Verified claim-by-claim against the engine/dispatcher/preinstall runner/manifests.
Corrections (3 were ship-blocking):

- Timeline (ship-blocking): identity files (pc-type/machine-number/cmm version/
  site-config) are written in WinPE at the PXE menu BEFORE the image boots, not
  during a post-imaging 'enrollment' step; preinstall already reads them. Added a
  step [0]; enrollment is now only Intune + Azure DSC credential provisioning.
- _CmmVersion (ship-blocking): a CMM bay with NO resolved version gets ALL
  PC-DMIS versions (legacy install-all), not none.
- machine-number 9999 (ship-blocking): the enforcement engine does not
  special-case 9999; it is a placeholder that won't match real bay gates (the
  9999-skip is status-write-back only).
- Preinstall runner implements only MSI/EXE + Registry/File detection, not the
  full matrix (that is runtime-only).
- Runtime processes up to three scopes: common, type, then optional type-subtype.
- pc-subtype.txt is legacy (no longer written at imaging since 2026-05-04).
- The collector ComputerType mapping lives at Settings > Collector PC Types, not
  the geenforce scope (scope computertypeid is a local reference field).
- FileVersion is a raw string compare; 4-part is convention, not engine-enforced.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-07-12 22:47:59 -04:00
parent ab0c9a454b
commit e6ed47c533

View File

@@ -45,10 +45,13 @@ Every shopfloor PC is governed in two distinct phases:
| Phase | When | Runs what | Purpose | | 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." | | **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. | | **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 schema but are run by different runners. The two phases share the same entry SHAPE (field names) but are run by different
Preinstall is a one-shot at imaging; runtime is the continuous enforcement loop. 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 ### 2.2 The runtime loop, step by step
@@ -60,9 +63,9 @@ On each cycle (`GE-Enforce.ps1` on the PC):
provisioned SFLD user - `net use W: ...`). If no credential yet, exit 0 and provisioned SFLD user - `net use W: ...`). If no credential yet, exit 0 and
retry next cycle (Azure DSC has not provisioned it). retry next cycle (Azure DSC has not provisioned it).
3. Run the engine (`Install-FromManifest.ps1`) against `common/manifest.json`, 3. Run the engine (`Install-FromManifest.ps1`) against `common/manifest.json`,
then against `gea-shopfloor-<pctype>/manifest.json`. Common runs first so then `gea-shopfloor-<pctype>/manifest.json`, then a `<pctype>-<subtype>`
shared prerequisites (e.g. Oracle Client) land before type-specific apps that manifest if one exists. Common runs first so shared prerequisites (e.g.
depend on them. 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 4. Write a status file back to the share (and, in the shopdb model, POST a
report - see 4.3). report - see 4.3).
@@ -101,7 +104,7 @@ Every entry has a `DetectionMethod` that decides whether the action fires:
|---|---| |---|---|
| Registry | the key/value exists (optionally equals a value) | | Registry | the key/value exists (optionally equals a value) |
| File | the file exists | | File | the file exists |
| FileVersion | the file's version equals an exact 4-part string | | 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) | | Hash | the file's SHA256 matches (case-insensitive) |
| MarkerFile | a marker file exists (the engine writes it after a clean install) | | MarkerFile | a marker file exists (the engine writes it after a clean install) |
| ValueMatches | a registry value equals the entry's target | | ValueMatches | a registry value equals the entry's target |
@@ -122,8 +125,11 @@ An entry can be narrowed by any combination of:
`collections`/`nocollections`/`common`). Fleet-wide `common` uses this heavily. `collections`/`nocollections`/`common`). Fleet-wide `common` uses this heavily.
- `TargetHostnames` - specific hostnames (supports `*` wildcards). - `TargetHostnames` - specific hostnames (supports `*` wildcards).
- `TargetMachineNumbers` - specific bay machine numbers (e.g. Okuma bays). - `TargetMachineNumbers` - specific bay machine numbers (e.g. Okuma bays).
- `_CmmVersion` - CMM PCs only: install only on bays resolved to that PC-DMIS - `_CmmVersion` - CMM PCs only: a tagged entry applies when it equals the bay's
version (requires engine lib >= 2.6). 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 - `PCTypesStrict` - disables alias expansion (PREINSTALL runner only; the runtime
engine ignores it). engine ignores it).
@@ -135,14 +141,24 @@ the gates a given scope actually uses (see 4.1).
The runtime engine reads the PC's identity from `C:\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). - `pc-type.txt` - the imaging PC type (which scope to run). `pc-subtype.txt` is
- `machine-number.txt` - the bay number (the eDNC/DNC registry value wins if LEGACY (no longer written at imaging since the 2026-05-04 rename reorg; the
present; the txt is the fallback). `9999` = placeholder/skip. 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`. - `cmm/version.txt` - CMM bays only: the resolved PC-DMIS version for `_CmmVersion`.
- `site-config.json` - the share root and site settings. - `site-config.json` - the share root and site settings.
- SFLD credentials at `HKLM:\SOFTWARE\GE\SFLD\Credentials` - provisioned by - SFLD credentials at `HKLM:\SOFTWARE\GE\SFLD\Credentials` - provisioned by
Azure DSC after enrollment (this is what gates the runtime phase starting). 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) ## 3. When GE-Enforce installs / takes over (the imaging timeline)
@@ -150,29 +166,38 @@ The runtime engine reads the PC's identity from `C:\Enrollment\`:
This is the "when to implement it during imaging" question. The order is: This is the "when to implement it during imaging" question. The order is:
``` ```
PXE image applied [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 v
[1] PREINSTALL (00-PreInstall-* runs preinstall.json ONCE) PXE image applied, Windows boots
- foundational software: PowerShell 7, VC++ redists, Oracle Client, |
Adobe Reader, HostExplorer, serial drivers, Display kiosk app, ... v
- these are the things later runtime apps depend on [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 v
[2] GE-ENFORCE ITSELF is laid down during imaging [2] GE-ENFORCE ITSELF is laid down during imaging
- the dispatcher (GE-Enforce.ps1), the engine lib (Install-FromManifest.ps1), - 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 and a scheduled task (at-logon + every ~5 min + shift windows) are
of the image / common bootstrap registered as part of the image / shopfloor setup
| |
v v
[3] ENROLLMENT [3] ENROLLMENT (post-imaging)
- C:\Enrollment\* written: pc-type, machine number, cmm version, site-config - Intune / GCCH enrollment, THEN Azure DSC provisions the SFLD share
- Azure DSC provisions the SFLD share credential in the registry 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 v
[4] FIRST LOGON -> RUNTIME ENFORCEMENT BEGINS [4] FIRST LOGON -> RUNTIME ENFORCEMENT BEGINS
- the scheduled task runs GE-Enforce.ps1: mount share, run common + the - the scheduled task runs GE-Enforce.ps1: mount share, run common + the
PC-type manifest, install/self-heal, report PC-type (+ subtype) manifests, install/self-heal, report
- repeats every logon + periodically forever after - repeats every logon + periodically forever after
``` ```
@@ -205,8 +230,10 @@ because it is a full management surface.
### 4.1 Manifests - authoring (GE-Enforce > Manifests) ### 4.1 Manifests - authoring (GE-Enforce > Manifests)
- **PC Types (scopes):** each imaging PC type is a row; add/edit/delete. The - **PC Types (scopes):** each imaging PC type is a row; add/edit/delete. (The
ComputerType mapping the collector uses lives here too. 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 - **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 + opens a typed form: the payload fields switch on `Type` (MSI shows Installer +
InstallArgs, PS1 shows Script + Args, File shows Source + Destination, Registry InstallArgs, PS1 shows Script + Args, File shows Source + Destination, Registry