Consolidates per-type enforcers (CMM, Keyence, Machine, Common, Acrobat)
into one dispatcher driven by pc-type.txt + site-config and a share-side
manifest layout. Same share is now the single source of truth for routine
software updates without re-imaging.
Runtime:
common/GE-Enforce.ps1 SYSTEM scheduled task. Reads
common/manifest.json plus optional
<pcType>/manifest.json and
<pcType-subType>/manifest.json.
Dispatches each entry through the lib.
Writes _outputs/logs/<hostname>/status.json
on the share after each cycle for fleet
monitoring.
common/Register-GEEnforce.ps1 Task registration. Triggers: AtLogOn +
every 5 min (jittered per-PC from
hostname hash) + daily at 05:45,
13:45, 21:45 EST shift windows.
Unregisters legacy per-type tasks on
install so the two coexist at most for
the duration of a single enforce cycle.
common/Deploy-GEEnforce.ps1 Retrofit helper for already-imaged PCs
(admin-run; copies runtime + registers
task + optional immediate trigger).
Library (common/lib/Install-FromManifest.ps1):
- New Type values: PS1, BAT, File, Registry, INF
- New DetectionMethod values: Always, MarkerFile, ValueMatches, pnputil
- TargetHostnames filter (exact + -like wildcards, ANDed with PCTypes)
- Schema version check (logs WARN on manifest newer than lib MAJOR)
- Auto-writes MarkerFile on successful one-shot PS1/BAT/CMD runs
- MSI log scan on failure surfaces meaningful install errors
- Lib version bumped 2.0 -> 2.1 for TargetHostnames
Observability:
common/monitor-fleet-status.py Scans _outputs/logs/*/status.json for
stale check-ins, failed scopes, and
version drift. Respects scope (dir-name),
PCTypes, and TargetHostnames filters so
entries excluded from a PC do not
false-flag as drift.
Regression harness:
common/test/ Parameterized VM harness + README
covering every action type plus
rollback, bad/missing SFLD creds, and
schema versioning.
Imaging integration:
Run-ShopfloorSetup.ps1 now stages GE-Enforce.ps1 and lib to
C:\Program Files\GE\Shopfloor\ and invokes Register-GEEnforce.ps1
at the end of setup. Legacy Register-CommonEnforce invocation is
kept for the transition; it and the legacy per-type enforcer files
are dead code once Register-GEEnforce runs and will be removed in a
dedicated cleanup pass.
Standard-Machine manifest:
eDNC entry bumped 6.4.3 -> 6.4.5. DetectionValue pinned to the
4-part FileVersion 6.4.5.0 verified against a fresh install in the
Win11 analyzer VM. UDC DetectionValue pinned to 1.0.34 (registry
stores 3-part for UDC; verified live).
scripts/mirror-from-gold.sh:
Restructured around share-root rsyncs (one pass per Samba share)
to close gaps in the prior per-subdir layout: winpeapps/_shared/
Applications (7.5 GB of Adobe + fonts + Java + Office + OpenText
+ printdrivers + wireless + Zscaler), additional winpeapps image
types, and enrollment flat-layout root files. Adds
--skip-clonezilla and --skip-reports.
Verified end-to-end in the Win11 analyzer VM:
- Every action Type and DetectionMethod round-tripped
- PCTypes filter (Oracle excluded on Shopfloor, Firefox included
on Shopfloor and DESKTOP-*, excluded elsewhere)
- TargetHostnames filter (exact, wildcard, no-match)
- Upgrade path: XML hash bump + fleet re-copy
- Rollback path: history-archive restore propagates via enforcer,
fleet converges back without per-PC intervention
- Status writeback + monitor script drift detection
- Graceful degradation on bad creds, missing creds, share
unreachable (all exit 0, log clearly, retry next cycle)
Not in this commit (follow-ups):
- Retire legacy per-type *-Enforce.ps1 files and simplify
09-Setup-*.ps1 scripts (coordinated multi-file cleanup)
- Stage 2b: InUseCheck close-and-reopen, ApplyMode gating,
UpdateWindow, .apply-now.txt sentinel, BITS pre-staging,
1618 mutex retry, PostInstallCheck, Uninstall action
- Management app (manifest CRUD + deploy + rollback + fleet view)
- ShopFloor autologon persistence bug (deferred for next imaging
attempt with live registry evidence)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Shopfloor enforcer regression tests
Lightweight harness for end-to-end validation of GE-Enforce.ps1 +
Install-FromManifest.ps1 against the v2 staging tree, using the Win11
analyzer VM as a synthetic shopfloor PC.
Files
vm-test-harness.ps1— setup + invocation of GE-Enforce inside the VM. Accepts-PCTypeand-PCSubTypeparameters. CreatesC:\Enrollment\stubs (pc-type.txt, pc-subtype.txt, site-config.json), stages the enforcer runtime from\\192.168.122.1\pxe-images\enforcer-stage\, injects a fake SFLD credential inHKLM:\SOFTWARE\GE\SFLD\Credentials\sambapointing at the host's samba share as if it were tsgwp00525, then runsGE-Enforce.ps1with output captured.
Prereqs
win11libvirt VM running, IP reachable at 192.168.122.210- qemu-guest-agent exec path available (
/tmp/guest-exec.sh) - host samba shares
pxe-images+windows-projectswritable bycampuser - enforcer staged at
/home/camp/pxe-images/enforcer-stage/(viacp <repo>/common/GE-Enforce.ps1 <repo>/common/lib/Install-FromManifest.ps1 /home/camp/pxe-images/enforcer-stage/) - v2 share staging at
/home/camp/pxe-images/tsgwp00525-v2/...
Usage
From the repo root on the host:
# Round 1: Shopfloor scope (exercises common manifest, PCTypes filter for Oracle)
B64=$(iconv -f UTF-8 -t UTF-16LE common/test/vm-test-harness.ps1 | base64 -w0)
/tmp/guest-exec.sh powershell.exe "[\"-NoProfile\",\"-EncodedCommand\",\"$B64\"]"
Or with non-default pcType (wrap in a tiny outer script that sets parameters):
cat > /tmp/round.ps1 <<'EOF'
$PCType = 'Standard'
$PCSubType = 'Machine'
EOF
sed -n '/^param(/,/^)/!p' common/test/vm-test-harness.ps1 >> /tmp/round.ps1
B64=$(iconv -f UTF-8 -t UTF-16LE /tmp/round.ps1 | base64 -w0)
/tmp/guest-exec.sh powershell.exe "[\"-NoProfile\",\"-EncodedCommand\",\"$B64\"]"
What each round validates
| Round | pcType / pcSubType | Exercises |
|---|---|---|
| 1 | Shopfloor / — | common manifest only, PCTypes filter (Oracle skips) |
| 2 | Standard / Machine | common + standard-machine manifests, eDNC upgrade detection, UDC skip, eMxInfo cmd |
| 3 | Keyence / — | common + keyence manifest, VR-6000 MSI detection, pnputil INF detection |
| 4 | Display / — | common + display manifest, kiosk-setup CMD wrapper |
| 5 (composite) | Shopfloor with a corrupted manifest / bad SFLD creds / tampered local XML | graceful-degradation paths + upgrade/rollback via hash mismatch |
See the main repo enforcer design doc (TBD) for scenario details.
Known cleanup after test runs
-
The harness intentionally leaves installed apps in place (Acrobat Reader DC, WJF Defect Tracker, 3OF9 font, Edge site-list XML, Firefox if tested). To reset to a clean baseline, revert the VM to the
clean-baselibvirt snapshot:virsh snapshot-revert win11 clean-base. -
Orphan
msiexec.exeworkers from long-running installs (UDC_Setup, PC-DMIS) can leave the MSI mutex held, blocking the next install with 1619/1618. Between rounds if you hit this:Get-Process -Name msiexec -ErrorAction SilentlyContinue | Stop-Process -ForceNote: a Stage 2b lib improvement is planned to retry once on 1618 after killing stale msiexec processes.