Phase 2 of the gea-shopfloor-* rename. Pairs with the v2 share manifest
dir renames done in /home/camp/pxe-images/tsgwp00525-v2/ this session
(local-only, syncs to prod separately):
standard-machine -> gea-shopfloor-collections
cmm -> gea-shopfloor-cmm
keyence -> gea-shopfloor-keyence
genspect -> gea-shopfloor-genspect
waxandtrace -> gea-shopfloor-waxtrace
display -> gea-shopfloor-display
lab -> merged into gea-shopfloor-common
(new) -> gea-shopfloor-nocollections (clone of collections w/o UDC)
(new) -> gea-shopfloor-heattreat (placeholder)
(new) -> gea-shopfloor-common (Timeclock + Lab merge)
GE-Enforce now walks an alias group when the constructed dir name has
no manifest.json. Fleet PCs whose pc-type.txt still says "Standard" /
sub "Machine" continue to find their manifest at the new
gea-shopfloor-collections location, so the rename is invisible to them.
After Phase 4 (startnet.cmd) lands and freshly-imaged PCs write the new
strings directly, the alias resolution still works for both forms.
Smoke-tested on win11 VM as SYSTEM via qga: legacy Standard/Machine
and new gea-shopfloor-collections both reach the same manifest, fire
the same entries, complete cleanly.
Phases 3+4 (repo folder renames + startnet.cmd menu) deferred per
project-shopfloor-rename-reorg memory.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 1 of the gea-shopfloor-* rename per project-shopfloor-rename-reorg.
Manifests can use either old names (Standard, Standard-Machine, CMM,
Keyence, etc.) or new names (gea-shopfloor-collections,
gea-shopfloor-cmm, gea-shopfloor-keyence, etc.) interchangeably.
Equivalence sets defined inline. Each set is a list of names that all
match the same identity. The match logic resolves the current PC's
identity AND each PCTypes entry into their alias sets, then matches
if the sets intersect.
Standard maps to all three new shopfloor variants (collections,
nocollections, common) so an existing PCTypes=['Standard'] manifest
entry still applies when PC pc-type.txt becomes any of the three.
Standard-Machine maps to (collections, nocollections) only since
Timeclock subtype is now collapsed under common.
Smoke-tested on win11 VM as SYSTEM via qga: dispatcher run with
PCType='gea-shopfloor-collections' against the existing common
manifest (Standard-only PCTypes filters) fires Oracle / FMS hosts pin
correctly. Same run with PCType='Standard' PCSubType='Machine' fires
identically.
Phases 3+4 (repo folder renames + startnet.cmd menu reorg) deferred to
the next session - high breakage risk, must ship atomically.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds rows for Standard-Timeclock, CMM, Keyence, Lab, WaxAndTrace,
Genspect, Display, Shopfloor alongside the existing Standard-Machine.
Per-type apps verified against the corresponding v2 manifest's detection
methods (PC-DMIS 2016/2019R2/Protect Viewer/CLM/goCMM for CMM;
VR-6000/USB driver for Keyence; kiosk shortcut for Display).
Common app list deduped via "$ref": "common.<key>" pattern. Verifier
resolves refs into the per-type apps array at runtime so each row stays
short and PCTypes-filter-aware (Lab + Display + Shopfloor get fewer
common apps because the manifest's PCTypes filter excludes them from
FMS hosts pin / Oracle / OpenText respectively).
verify-state.ps1 changes:
- $ref resolution against the matrix.common namespace
- Registry method now permits no DetectionName (key-existence only,
e.g. Protect Viewer)
- New PnpUtilGrep method for INF-driver checks (Keyence USB driver)
Smoke-verified end-to-end on the win11 VM as SYSTEM via qga - 60 checks
across 9 PC types. Type-specific failures (5 CMM, 2 Keyence, 1 Display)
correctly surface "no payload staged" rather than masking it as pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Smokes end-to-end on the win11 VM in ~14s for Standard/Machine: 11/11
stage scripts exit 0 (6 Shopfloor baseline + 5 Standard per-PC-type),
transcripts land in C:\Logs\SFLD\ as expected.
Pieces:
- stage-image.ps1 - VM-side: clean prior state, robocopy shopfloor-setup
tree from samba share to C:\Enrollment\shopfloor-setup, drop pc-type +
pc-subtype + site-config, walk numbered stage scripts (^[0-9]{2}-) in
Shopfloor/ then <PCType>/, run each, collect rc + summary. Skips PPKG /
sync_intune / reboot - real machine identity is not touched.
- A-imaging/run.sh - host orchestrator: revert, stage repo tree to
/home/camp/pxe-images/test-stage-A, mount Z: in VM as SYSTEM, invoke
stage-image.ps1 with PCType/PCSubType params, collect transcripts.
Optional PREINSTALL_PATH env if you have the binary installer payload
available; default skips it (00-PreInstall logs "installer not found"
for every entry, expected for orchestration-only test - per-app installs
are covered by Path B).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Harness now passes 9/9 across baseline + heal + idempotent phases on the
win11 VM (Standard/Machine), with 6 drift scenarios applied + healed
between the baseline and heal cycles in ~30s total.
Fixes:
1. lib/qga-run.py - extracted the qga round-trip out of an inline
`python3 - <<PY` heredoc. The inline form clobbered stdin (heredoc
replaces stdin to feed python the script, leaving sys.stdin empty
for the PowerShell snippet the function caller piped in).
2. lib/qga.sh - dropped `set -euo pipefail`. When sourced, it leaked
into the harness shell. Then any captured `out=$(qga_run_ps ...)`
that exited non-zero (verify-state.ps1 returns 1 on any FAIL,
normal during drift phases) would silently abort the harness.
Callers handle non-zero with `|| rc=$?`.
3. B-enforce/run.sh do_verify - rewritten to capture rc, parse summary
line, distinguish expect_pass=true vs false, route to ok / fail
helper without aborting the harness on a normal non-zero verify.
4. matrix.json WJF Defect Tracker entry - switched detection from File
to Registry (uninstall key DisplayVersion). The MSI does not drop
the Defect_Tracker.exe artifact at the documented path even though
the manifest's File detection treats it as installed; the uninstall
reg entry is the reliable install marker. v2 manifest's File
detection path may also need fixing, separate task.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Initial harness scaffolding per SCOPE.md. Drives the win11 analyzer VM
via qemu-guest-agent (runs as NT AUTHORITY\SYSTEM, same context as
GE-Enforce in production - see reference-vm-qga-as-system memory note
for why this is preferred over WinRM).
Pieces:
- lib/qga.sh - host-side helpers (qga round-trip, snapshot revert,
share mount via cmdkey + net use, file upload). Source from any
harness script.
- lib/verify-state.ps1 - VM-side detection runner. Parses matrix.json,
walks each app's verify block, prints PASS/FAIL with detail, exits
0 only if every check passes. Methods: Registry, File, FileVersion,
Hash, FileGrep.
- matrix.json - PC-type matrix data. Currently only Standard/Machine
rows populated (apps + drift scenarios). Extending to other PC types
is just adding rows.
- B-enforce/run.sh - 5-phase orchestrator (stage / baseline / tamper /
heal / idempotent). Defaults to Standard/Machine. SKIP_REVERT=1 for
faster iteration without burning the snapshot revert.
- B-enforce/tamper.ps1 - applies driftScenarios from matrix.json.
Methods: RegRemove, RegSet, FileDelete, FileOverwrite, FileGrepDelete.
Path A (imaging-time install) and remaining 8 PC-type rows are next.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two test paths: (A) imaging-time install via PXE preinstall +
Run-ShopfloorSetup.ps1 per PC type, (B) manifest-engine ongoing
enforcement via GE-Enforce + Install-FromManifest against the v2 share.
Locks the matrix before harness code lands: 9 PC-type rows, expected
install state per type, drift scenarios per app for Path B's
tamper+heal cycle. Decisions: skip JSON CI report (air-gapped solo
workflow), interactive stdout + exit 0/1 only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
FMSHostPrimary -> wjfms3.ae.ge.com (was a mix of WJFMS3, wjfms3.ae.ge.com,
WJFMS3.ae.ge.com, WJFMS3.AE.GE.COM across 147 bays)
FMSHostSecondary -> 10.233.112.158 (was a mix of WJFMS3/4 short + .ae.ge.com)
Reasoning: eDNC's CPreScan + DNCdll CDoPersonnel resolve FMS hosts via MFC
CSocket, which calls inet_addr first then gethostbyname. Modern getaddrinfo
(used by PowerShell / Resolve-DnsName) succeeds on the GE corporate net for
this FQDN, but the legacy gethostbyname path does not - eDNC sits there
unable to resolve. Pinning the secondary to a dotted IP makes inet_addr
succeed before any gethostbyname is attempted, so the secondary connect
always works regardless of resolver state. Primary stays as FQDN so the
hosts file pin (added in a separate change to common/manifest.json) gives
gethostbyname an immediate hit. Both values fit the 20-byte buffer cap
that CPreScan + CDoPersonnel use when reading FMSHost* from registry.
Per-bay backups are consumed by Update-MachineNumber's Import-EDncRegBackup
at imaging time, so freshly-imaged PCs land with correct values. Existing
PCs are healed by the matching Type=Registry drift-catcher entries in the
v2 standard-machine manifest.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The shim Setup-OpenText.cmd hands "%~dp0..\apps\opentext" to the PS1.
PowerShell's Join-Path leaves the literal ".." segment in the path it
passes to msiexec, and the Windows Installer service rejects the package
with 1619 (ERROR_INSTALL_PACKAGE_OPEN_FAILED) for that reason. Every
other API resolved the path fine, masking the issue. Resolving SourceDir
once at script entry collapses ".." so the downstream msiexec /i and /p
calls receive a clean drive-rooted path.
Verified end-to-end on the win11 VM via the GE-Enforce dispatcher: msiexec
/i and /p both return 3010 (treated as success), profiles + shortcuts +
marker land cleanly, total 36s.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bounds growth of C:\Logs\Shopfloor (per-day enforce-YYYYMMDD.log files),
C:\Logs\SFLD (Start-Transcript -Append accumulates), and C:\Logs\Keyence.
Today's enforce log is never touched (LastWriteTime = now). Cheap flat
scan per cycle; logs only when something actually got pruned.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
These apps are Standard-Machine-only. Their presence in the global fallback
list (used when a pcProfile doesn't override) was a footgun: any newly-added
PC type without an explicit pcProfile would inherit UDC. Standard-Machine's
own pcProfile already declares them, so removing from the global is a no-op
for current PC types and cleaner for future ones.
Global baseline now: Defect Tracker + WJ Shopfloor + Plant Apps + Edge.
Standard-Machine (Standard PC type with subtype Machine) keeps full UDC/
eDNC/NTLARS set as before.
Captures the full picture of how the manifest engine works, why scripts
don't need self-heal entries (run from share), credential context
(SYSTEM = computer account, requires Mount-SFLDShare for file-level
reads), C:\Enrollment vs SFLD share copy distinction, and update
workflows. Written in response to a session that wasted time adding
redundant manifest entries because this wasn't documented.
Companion to scripts/diagnostics/Capture-LockdownState.ps1 and the
auditing script in pxe-images/Audit-SFLDShare.ps1.
Symptom: every Restore-UDCData log entry showed bay-level files as 'absent'
even when they actually existed on the share - on a device where another
PC's run had successfully consumed and migrated the same backup. Endless
'no work this cycle' loop on the device that should have done the consume.
Cause: script ran as NT AUTHORITY\SYSTEM (manifest engine on logon).
SYSTEM authenticates to remote SMB as the COMPUTER ACCOUNT
(DOMAIN\HOSTNAME$), not as a user. The SFLD share's ACL grants top-level
enumeration to authenticated computers (so Test-Path on share root +
bay dir returned True) but file-level read only to the SFLD user. With
no explicit user creds, Test-Path on bay-level files returns False -
indistinguishable from 'file not found' - so the script silently logged
'absent' on files that actually exist. A different PC with proper creds
consumed bay 3207 first; ours kept polling forever.
Update-MachineNumber.ps1's branch already worked around this by calling
Mount-SFLDShare (Restore-EDncReg.ps1's helper that reads
HKLM:\SOFTWARE\GE\SFLD\Credentials\* and net-use's the share with the
SFLD user identity).
Fix: Restore-UDCData.ps1 now does the same. Replaces raw-UNC Test-Path
polling with Mount-SFLDShare, probes via the W: drive letter, and
unmounts on every exit path. If creds are missing in registry the script
fails fast with a clear ERROR rather than masquerading as 'no backup'.
scripts/diagnostics/Capture-LockdownState.ps1 captures Windows endpoint
state at three lifecycle checkpoints so the deltas isolate which phase
delivered (or failed to deliver) each component:
- pre-category - PPKG-enrolled, no Intune category yet
- post-category - category-driven assignments arrived, pre-lockdown
- post-lockdown - kiosk + autologon + AppLocker fully landed
Bumped from the previous 2-stage (pre/post) version. Legacy 'pre'/'post'
aliases preserved.
Captures additions driven by the SFLD-DSC v2.0.2 post-mortem:
- IMECache file listing (catches missing sastoken.txt)
- DSCDeployment.log + version.txt copied from C:\pc\
- SFLD\DSC payload listing
- C:\Logs\BPRT\ runtime state (criticalChecks.json, packageInfo.json)
- C:\WCDApps\ deploy verification
- Windows\Provisioning\Diagnostics copy
- Tasks-RunHistory.csv with LastRunTime + LastTaskResult per task
- DeviceManagement-Events.csv (MDM 429s, AAD token failures)
- Provisioning-Events.csv (PPKG runtime errors)
- MDM-Certificates.csv (enrollment cert health)
scripts/diagnostics/snapshot-runbook.txt: step-by-step ops guide
covering when to fire each stage, where output lands, how to ship it
back via image-upload share, and which files to compare first when
diffing.
Wrapper invoked by Install-FromManifest.ps1 (Type=CMD) when Hash detection
on C:\ProgramData\UDC\udc_webserver_settings.json fails. Mirrors the
Install-eMxInfo.cmd pattern: copies the colocated json from the SFLD
machineapps share into ProgramData\UDC.
Manifest entry (with DetectionValue 4E04A865...DEA3) goes in
machineapps-manifest.json on the SFLD share - separate from this repo.
When the tech transitions a 9999-placeholder PC to its real machine number,
also restore the per-bay udc_settings_<num>.json from
\\tsgwp00525\shared\spc\udc\settings_backups\. PXE-time preinstall can't reach
this share (no SFLD creds yet), so 00-PreInstall uses the local C:\Enrollment
mirror; post-config the share is reachable, so the renumber path goes direct
to the canonical source.
Adds udcSettingsSharePath to site-config.json under Standard-Machine.
Bundles in prior uncommitted work in the same file: ntlars reg restore,
UDC data restore (CurrentData.json + ArchivedData/), MTConnect Devices.xml
inline rewrite + service restart, and one-shot consume of per-bay UDC
backup -> migrated/<timestamp>/.
Add staging block that copies udc_webserver_settings.json from the enrollment
share to C:\ProgramData\UDC during preinstall, mirroring the existing
udc_settings.json pattern. New PCs were imaging without UDC web server
config because the file was never wired into the imaging flow (only the
remote-maintenance task in powershell/remote-execution touched it).
Also folds in two prior uncommitted hardening blocks in the same script:
firewall NotifyOnListen=False (suppress Oracle OUI's listen-port prompt)
and NetFx3 pre-enable (Oracle 11.2's welcome path needs .NET 3.5).
Production case: bay 3207 had ArchivedData\ on the share with full
production records but no CurrentData.json at the bay root. The previous
Restore logic treated CurrentData.json as the marker for "valid backup"
and exited early when absent, so the script silently no-op'd every cycle
even though there was real archive data ready to restore.
Asymmetric with Backup-UDCData.ps1, which already handles missing
CurrentData.json gracefully (it copies whatever exists). Possible causes
of CurrentData.json absence in a backup: source PC had no live UDC
session at backup time (UDC inactive / not recording), backup partially
failed for that one file (no Backup-side log to confirm without rerun).
Either way, an ArchivedData-only backup is still a valid backup.
Behavior change:
- Early-exit only when BOTH CurrentData.json AND ArchivedData\ are
absent. Otherwise proceed with whatever exists.
- Copy step for CurrentData.json wrapped in srcCurExists guard.
- consumeOk now requires: every present source successfully copied,
AND at least one thing was actually copied.
- Move-to-migrated wraps CurrentData.json move in Test-Path guard
(was already guarded for ArchivedData).
- restore.manifest.json gains CurrentDataPresent and ArchivedDataPresent
booleans so future audits can see which side actually restored.
- UDC relaunch now fires when EITHER copy succeeded (was only on
CurrentData.json copy).
Verbose logs now distinguish three cases at the early-exit:
- Both absent: "no work to do this cycle" (the 99% path)
- Only ArchivedData\: WARN with explanation, proceed
- Only CurrentData.json: WARN with explanation, proceed
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two bugs that have been silently masking GE-Enforce registration since
Stage 2a landed 2026-04-22, surfaced when v1 enforcers were retired
(commit 0badfc1) and could no longer cover for the missing v2 registration.
Bug 1: startnet.cmd at imaging time only xcopied Shopfloor\ and the
PCTYPE-specific dir from the imaging share to W:\Enrollment\shopfloor-setup\.
common\ was never copied. v1 dispatchers lived per-pctype and rode in via
the %PCTYPE% xcopy, so this was never noticed. v2's GE-Enforce.ps1 +
Register-GEEnforce.ps1 + lib\Install-FromManifest.ps1 all live in common\
and got skipped at imaging entirely.
Fix: add a third xcopy block for common\, mirroring the Shopfloor\ block
above it. Applies to playbook/startnet.cmd and startnet-template.cmd.
Bug 2: Run-ShopfloorSetup.ps1 line 288 set $commonSetupDir via
'Join-Path $PSScriptRoot common'. Run-ShopfloorSetup.ps1 lives at
C:\Enrollment\Run-ShopfloorSetup.ps1 (xcopied by startnet.cmd), so
$PSScriptRoot resolves to C:\Enrollment, and $commonSetupDir resolved
to C:\Enrollment\common - which is NOT where common\ lives even after
the bug 1 fix (correct path is C:\Enrollment\shopfloor-setup\common\).
The Test-Path -LiteralPath check on Register-GEEnforce.ps1 returned
false silently and GE-Enforce never registered.
Same bug existed for Register-MapSfldShare on line 321.
Fix: $PSScriptRoot -> $setupDir for both. $setupDir was already defined
on line 51 as Join-Path $enrollDir "shopfloor-setup", which is the path
the rest of the script uses consistently.
Pre-v1-cleanup, v1's per-pctype enforcer registrations on lines 322-357
(now deleted) ran independently and covered the gap, so PCs ended up
with v1 enforcers and the user thought v2 was running. Post-cleanup,
this bug means nothing gets registered.
PXE server has been patched directly: boot.wim re-baked with the new
startnet.cmd, /srv/samba/enrollment/shopfloor-setup/Run-ShopfloorSetup.ps1
replaced. New PXE-imaged PCs from this point forward will register
GE-Enforce correctly.
For PCs imaged before this fix: run Deploy-GEEnforce.ps1 from the SFLD
share's _meta/runtime/ to retrofit. Same one-liner used for promoting
v1 PCs to v2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vendor stamps the 6.4.5 MSI as eDNC_6-4-5.msi (underscore + hyphens) but
prior versions shipped as eDNC-6.4.3.msi (dash). The previous filter only
matched the dash form so the imaging-time install would skip 6.4.5 outright.
Filter is now eDNC*.msi which catches both. Imaging dir is expected to hold
exactly one version at a time; rollback to a prior version is handled
post-imaging via the SFLD share's standard-machine/apps/ alternates, not by
keeping multiple MSIs in the imaging path.
Also updated the Write-Warning fallback to mention the new filter pattern.
PXE server's /srv/samba/enrollment/shopfloor-setup/Standard/eDNC/ has been
swapped 6.4.3 -> 6.4.5 alongside this commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two production-debuggability gaps closed.
1. Logging is now always-on. The previous version exited silently on the
common no-op paths (no UDC installed, no backup waiting, share not
reachable), leaving zero log evidence when techs reported "restore
didn't happen". New behavior writes a header + identity + share-path
+ decision-point line to a single rotating log file every cycle.
Errors include exception type, position, and full ScriptStackTrace.
Log lives at C:\Logs\UDC\Restore-UDCData.log with a 1 MB cap and
one-generation rotation to .old.log.
2. Share-reachability is now polled instead of probed once. The SFLD
share over the SMB redirector takes 20-60 s to become reachable
from SYSTEM context after a cold logon, especially on the first
GE-Enforce cycle of the boot. The old single Test-Path returned
false in that window and the script silently exited, missing the
backup. New behavior polls Test-Path on the share root every 3 s
for up to 60 s (both tunable via -ShareTimeoutSec / -SharePollSec)
before deciding "no backup". If the share never comes up in that
window the script exits 1 instead of 0 so the dispatcher logs a
visible failure.
Both behaviors propagated to the host staging copy at
/home/camp/pxe-images/Restore-UDCData.ps1 and to the v2 share-staged
copy at tsgwp00525-v2/.../standard-machine/scripts/Restore-UDCData.ps1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
UDC's per-bay archive directory is C:\ProgramData\UDC\ArchivedData, not
ArchiveData. The previous spelling was a typo introduced when the scripts
were first written; it would have meant Backup-UDCData.ps1 found no archive
content (silent zero-file backups), and Restore-UDCData.ps1 wrote into a
location UDC does not read from.
Path swap is straight string replacement across both scripts plus the .bat
wrapper's usage comment. Manifest field names in backup.manifest.json /
restore.manifest.json (ArchivedDataPresent, ArchivedDataFiles,
ArchivedDataBytes) updated to match.
Update-MachineNumber.ps1's parallel UDC-restore branch (still uncommitted
in a prior workstream) has the same fix in the working tree, captured in
that branch's eventual commit.
The v2 share-staged copy at tsgwp00525-v2\standard-machine\scripts\
Restore-UDCData.ps1 also got the fix and is ready for push.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Stage 2a (GE-Enforce.ps1, landed 2026-04-22) is now the only ongoing-update
enforcer. The legacy per-pctype tasks (Machine-Enforce, Common-Enforce,
CMM-Enforce, Keyence-Enforce, Acrobat-Enforce) were kept as transition
belt-and-suspenders; with retrofitted PCs handled, the v1 path is dead and
gets removed entirely.
Deleted (13 files):
Standard/{Machine-Enforce,Register-MachineEnforce}.ps1
Standard/machineapps-manifest.template.json
common/{Common-Enforce,Acrobat-Enforce,Register-CommonEnforce,Register-AcrobatEnforce}.ps1
common/common-apps-manifest.template.json
CMM/CMM-Enforce.ps1
Keyence/Keyence-Enforce.ps1
{CMM,Keyence,Standard}/lib/Install-FromManifest.ps1 (orphan dups of common/lib)
Trimmed:
Run-ShopfloorSetup.ps1: dropped the legacy register-* invocations (Common,
Machine) and the transition-period comment. Sole enforcer registration
is now Register-GEEnforce.
09-Setup-Keyence.ps1: keeps imaging-time install (step 1); removes the
enforcer staging (step 2) and scheduled-task registration (step 3).
Library lookup repointed to common/lib/Install-FromManifest.ps1.
09-Setup-CMM.ps1: same treatment - keeps .NET 3.5 enable, install,
PC-DMIS ACL grants, and bootstrap cleanup. Library repointed to common/lib.
cmm-manifest.json + keyence-manifest.json: _comment fields updated to
reflect imaging-time-only role (ongoing enforcement now goes through
the v2 share manifests via GE-Enforce).
Verified clean: no orphan references to *-Enforce.ps1 / Register-*Enforce.ps1
/ machineapps-manifest / common-apps-manifest in any code path that runs.
A few historical mentions remain in unmodified header comments (GE-Enforce.ps1,
Deploy-GEEnforce.ps1, Monitor-IntuneProgress.ps1) describing what the new
dispatcher replaced; left as historical context.
Run-ShopfloorSetup.ps1 also picks up an unrelated 1-line hunk adding
SetShopfloorAutoLogon.bat to the desktop-copy list (already in the working
tree from a prior session). The file itself is not yet tracked; the
desktop-copy step is Test-Path-guarded so this is harmless until the
.bat is committed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Backup-UDCData.bat / Backup-UDCData.ps1: tech-runnable, UAC-self-elevating.
Run on the OLD PC before retirement; reads bay number from
udc_settings.json, copies CurrentData.json + ArchiveData/ to
\\tsgwp00525\...\backup\udc\<bay>\, drops backup.manifest.json. Refuses
the 9999 placeholder so backups never collide across PCs.
Restore-UDCData.ps1: idempotent, designed for the manifest engine. 99%
of cycles silent no-op (sub-second, zero side effects); 1% (cycle after
a backup lands at this PC's bay) restores files locally, moves consumed
backup to <bay>\migrated\<timestamp>\, writes restore.manifest.json,
relaunches UDC. Round-trip + no-op fast path verified end-to-end on the
win11 analyzer VM. Already wired into the Standard-Machine GE-Enforce
manifest at standard-machine\manifest.json on the v2 share.
Complementary to the placeholder-to-real branch in Update-MachineNumber.ps1:
that branch covers the 9999 -> real flow, this one covers the
pre-imaged-then-swapped flow where Update-MachineNumber already ran
before any backup existed. Both safely no-op if the other consumed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Companion to the previous commit (4550d43). Three files that should have
been in the same commit but got left out of `git add`:
- .gitignore: negate rule for boot-tools/blancco/grub-blancco.cfg so the
tracked cfg (source of truth for grubx64.efi rebuilds) survives
the blanket boot-tools/ ignore.
- playbook/blancco-init.sh: rewritten for modprobe-with-deps, full NIC
driver coverage, set -x trace to /dev/console, dmesg + PCI-device +
/proc/modules dump + interactive shell on "no NIC after 60s".
Replaces the narrow insmod-loop version that silently hung on
unsupported NICs.
- playbook/pxe_server_setup.yml "Build Blancco PXE initramfs" task now
sweeps the full drivers/net/ tree (ethernet + phy + mdio + usb + fddi
+ wan) plus overlay / squashfs / loop / ptp / libphy / mii deps, runs
depmod to regenerate modules.dep inside the initramfs (required for
modprobe dependency resolution), and symlinks the full applet list
blancco-init.sh needs (modprobe, insmod, dmesg, find, env, etc).
Result: ~20 MB initramfs vs the old 2 MB narrow build.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the "option B" gap left earlier in this session. Fresh PXE servers
built from this commit will boot Blancco identically to .1/.2 today
instead of reproducing the Apr-14 regression (commit d6776f7).
Three moving parts:
1. boot-tools/blancco/grub-blancco.cfg (new, tracked via .gitignore
negation) carries the slim Ubuntu-kernel cmdline. prepare-boot-tools.sh
already rebuilds grubx64.efi from this file via grub-mkstandalone, so
binary doesn't need to be tracked. The server-side grub-blancco.cfg
still has the native-kernel cmdline but it's orphaned - nothing in
the active boot flow reads it.
2. playbook/blancco-init.sh rewritten for modprobe-with-deps, full NIC
driver coverage, set -x trace to /dev/console, dmesg+lspci+/proc/modules
dump + interactive shell drop on "no NIC after 60s". Replaces the
narrow insmod-loop version that silently hung on unsupported NICs.
3. playbook/pxe_server_setup.yml "Build Blancco PXE initramfs" task now
sweeps the full drivers/net/ tree (ethernet + phy + mdio + usb + fddi
+ wan) plus overlay / squashfs / loop / ptp / libphy / mii deps, runs
depmod to regenerate modules.dep inside the initramfs (required for
modprobe's dependency resolution), and symlinks the full applet list
blancco-init.sh needs (modprobe, insmod, dmesg, find, env, etc).
Result: ~20 MB initramfs vs the old 2 MB narrow build. Size is a
non-issue (1-2 s HTTP load at gigabit) versus the hours of debugging
the narrow build cost.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Shebang was \`#!/usr/bin/env python3\r\` which errored with
\`/usr/bin/env: python3\r: No such file or directory\`. Pure line-ending
fix, no content change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1. Deploy gea-standard / gea-engineer FlatUnattendW10.xml
The playbook only copied the shopfloor variant before; standard and
engineer's unattend was hand-staged on the running servers. New task
loops the repo's playbook/FlatUnattendW10.xml into Deploy/ for each
entry in standard_types (new var covering gea-standard, gea-engineer,
ge-standard, ge-engineer). force: yes because repo drift vs deployed
copy is what produced the Win10/Win11 search-cleanup regression
earlier this session (d49f516).
2. Deploy Oracle Client 11.2 preinstall payload
preinstall.json now leads with Oracle 11.2 (commit 3a29784). The CMD
wrapper is tracked in the repo at playbook/preinstall/oracle/; the
686 MB Oracle_OracleDatabase_11r2_V03.zip is too large to commit and
rides on USB under oracle/ alongside BIOS exes. Three tasks:
mkdir staging dir, copy CMD from usb_mount, copy zip from usb_root
with a soft-fail + warning if absent.
3. No change needed for sync-preinstall.sh — Oracle 10.2.0.3 flat
installer was already dropped in 9235d19.
YAML lints clean. Fresh server built from this commit will bring up
Blancco-agnostic imaging paths correctly; Blancco-specific gaps
(grubx64.efi native-vs-slim, narrow kexec-initrd driver tree,
narrow blancco-init.sh) are still deferred per earlier "option B"
decision and remain server-side-pinned only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two things in one pass because the repo copy was 162 lines behind the
deployed one already:
1. Sync repo to the currently-deployed FlatUnattendW10.xml baseline
(Java JRE 8 u441 + Java auto-update pins + Cortana/Bing/Search
disable block that had been added on-server but never committed).
2. Prune three ineffective registry entries and replace the Bing
suppression with a documented equivalent that works on both Win10
and Win11:
- DROP #32 HKLM\...\Search\CortanaEnabled=0
Undocumented at HKLM (the real key is HKCU). No effect.
- DROP #37 AllowCortanaAboveLock=0
Deprecated per AboveLock Policy CSP. Cortana app was
removed from Win11 in Canary 25967 anyway.
- REPLACE #34 BingSearchEnabled (HKLM, undocumented) with
DisableSearchBoxSuggestions=1 written into the
Default User hive so every new account inherits it.
This is the Microsoft-documented kill-switch for
Bing / web results in Start-menu search on both
Win10 and Win11.
Validated XML well-formed (xmllint + Python ET). RunSynchronous orders
remain unique and ascending after the deletions. Deployed to both PXE
servers under /srv/samba/winpeapps/{gea-engineer,gea-standard}/Deploy/
with timestamped .pre-winsearch-cleanup-* backups.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Oracle 11.2 is now installed at image build time (preinstall) rather
than deferred to the runtime enforcer. eDNC / NTLARS / UDC / CMM tooling
all link against the Oracle home, so shipping an image without Oracle
means the first-boot experience is broken until the enforcer completes.
Uses the EXE-launches-CMD trick (same pattern as OpenText Setup-OpenText
.cmd) since the preinstall runner only knows MSI/EXE. The wrapper itself
accepts the zip next to the .cmd (preinstall flat layout) OR under
..\apps\ (SFLD share layout for the runtime enforcer), so one script
serves both paths. First entry in Applications[] so downstream apps see
Oracle already present.
The 686 MB Oracle_OracleDatabase_11r2_V03.zip lives outside git and is
pushed to /srv/samba/enrollment/pre-install/installers/oracle/ separately.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
10.2.0.3 was being installed into every Standard/CMM/Genspect/Keyence/
WaxAndTrace/Display image at build time. Oracle 11.2 is now installed
and version-enforced at runtime by the GE-Enforce common manifest
entry (Install-Oracle11r2.cmd), so baking 10.2 into the image creates
drift: 10.2 on disk, 11.2 expected by registry. Remove from preinstall.json
and drop the flat installer from sync-preinstall.sh so new builds come
up clean and the enforcer does the install on first boot.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The config.img internal preferences.xml has <network><enabled>false</enabled>
(set up by b7cd097 for native-kernel BMC licensing over WiFi). The sibling
playbook/blancco-preferences.xml was left at true, so the Ubuntu-kernel
switch_root path downloaded a preferences.xml that re-enabled wired LAN,
killing the WiFi default route and preventing BMC reach.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User reports Blancco was working before our mirror/session activity
today - then my attempted fixes (grubx64.efi rebuild, kexec-initrd
driver sweep, verbose blancco-init.sh) made it worse:
- First attempt (narrow igc driver add) did not help because the
switch-root path was not the one actually loaded by grubx64.efi's
embedded config.
- Second attempt (swapped grub embedded config to Ubuntu-kernel path)
got further, but then kexec-initrd modules failed on insmod.
- Third attempt (full ethernet tree sweep) pulled in broken ancient
drivers (winbond-840, w5100-spi, xirc2ps_cs) that failed with
unknown-symbol errors and prevented good drivers from loading.
Full revert: .gitignore, blancco-init.sh, pxe_server_setup.yml back to
the pre-session commit 6dcf832 state. Removes boot-tools/blancco/grub-
blancco.cfg from git (it was only added this session).
Runtime on both PXE servers was also restored: grubx64.efi and
kexec-initrd.img reverted from the .bak files taken before each
modification this session.
Whatever was there before today is now restored byte-for-byte on both
servers. If there is still a Blancco boot issue on specific modern
hardware that the user needs to fix, we will diagnose that narrowly
against the actual failure mode on that specific machine, not by
making sweeping preemptive changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previous approach listed ~6 specific drivers (e1000e, igb, tg3, bnx2,
bnxt_en, b44) and silenced insmod errors (2>/dev/null). On modern Dell
fleet (Latitude 5330/5440, Pro-series, newer OptiPlex) this missed
igc (Intel I225/I226) entirely, and for the drivers we did include,
dependency modules they need at insmod time (libeth, libie, dca,
i2c-algo-bit, macsec, mii, libphy, ptp, ...) were never bundled.
insmod does not resolve dependencies, so NIC drivers that need
helpers failed to load silently.
playbook/pxe_server_setup.yml (kexec-initrd build):
- Sweep the whole drivers/net/ethernet tree (~170 drivers, all
vendors, ~15 MB total). Drivers for hardware not present skip
without binding.
- Add common helper dirs: drivers/net/{phy,mdio}, drivers/i2c/algos,
drivers/dca, drivers/ptp, net/macsec, drivers/ssb.
- overlay.ko kept.
playbook/blancco-init.sh:
- Load helpers BEFORE main NIC drivers (libeth/libie, dca,
i2c-algo-bit, macsec, mii, ssb, libphy, mdio*, phy*, ptp*),
then iterate remaining modules.
- Remove 2>/dev/null on insmod so actual failures surface on the
boot console.
- Print kernel version + /sys/class/net before/after driver load,
plus dmesg grep for NIC driver activity.
- On "no interface found" failure, dump dmesg tail and drop to a
busybox shell for manual debug rather than just hanging.
Separate from this commit but related: kexec-initrd.img on both PXE
servers (.1 and .2) was rebuilt inline with these changes. Pre-rebuild
binary kept as kexec-initrd.img.bak-<timestamp>.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two changes in one:
1. grub-blancco.cfg reverts to booting vmlinuz-ubuntu + kexec-initrd.img
(the switch_root path from commit dd2fec5). b7cd097 had switched
back to Blancco's native vmlinuz-bde-linux via TFTP for "hardware
compat," but that turned out to regress on modern Dell fleet
hardware - Blancco's packaged kernel lacks igc (Intel I225/I226 on
Latitude 5330/5440, Pro-series, newer OptiPlex). Symptom was
successful TFTP fetch of kernel+initrd, then silence at the Blancco
UI ("no network card found") because the rootfs HTTP fetch has no
NIC to use.
The switch_root initramfs built by the Ansible playbook now carries
igc, bnxt_en, r8169, atlantic, and other modern NIC modules, so the
Ubuntu kernel path reliably finds a working NIC across the whole
fleet before HTTP-fetching airootfs.sfs and kexec'ing into Blancco.
Native kernel still runs under Blancco's control after kexec - only
the *loader* kernel changes.
The grubx64.efi binary (embedding this config via grub-mkstandalone)
was rebuilt and pushed to /var/www/html/blancco/ on both PXE servers
(.1 and .2). Old binary kept as grubx64.efi.bak-<timestamp> on each.
2. .gitignore: negate boot-tools/blancco/grub-blancco.cfg so this one
file is version-controlled. boot-tools/ is otherwise local cache
artifacts (kernel/initrd/sfs extracted from Blancco ISO, grubx64.efi
built output). grub-blancco.cfg is source-of-truth and must not be
re-derived; any new PXE server build needs the exact same content
or the Blancco boot chain regresses.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Live kernel NetBIOS name instead of the PowerShell process-env cache.
$env:COMPUTERNAME is populated when PowerShell starts and does not
update if the PC gets renamed (common on Intune-managed Autopilot /
AADJ devices that come up with a DESKTOP-XXXXXXXX name and get
renamed by policy post-imaging). Until the next reboot, the env var
stays stale while 'hostname.exe' already reports the new name.
That mismatch showed up live on the first production retrofit: the
status.json was written under _outputs/logs/DESKTOP-XXXXXXXX/
instead of under the device's current name, and the
TargetHostnames filter and monitor drift-check would likewise see
the stale name.
[Environment]::MachineName reads from the kernel on each call, so
it always returns the current NetBIOS name. Swapped at all five
callsites in GE-Enforce.ps1, Register-GEEnforce.ps1, and
Install-FromManifest.ps1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
FIPS-enforced PCs (System cryptography GPO) reject non-approved
algorithms at the .NET crypto API level. MD5 throws
"This implementation is not part of the Windows Platform FIPS
validated cryptographic algorithms" on .Create(), which aborts
Register-GEEnforce before the scheduled task is built.
SHA-256 is FIPS 180-4 approved and its default .NET provider is
validated, so SHA256.Create() works under FIPS mode. Functionally
equivalent for the 0-4 minute modulo we need for jitter.
Hit this live on the first production retrofit. Enforcer runtime
files were copied and legacy tasks were unregistered, but the new
task creation aborted. Rerunning Deploy-GEEnforce.ps1 is idempotent
and recovers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds partial Stage 2b support for InUseCheck entries in manifests. When
an entry declares InUseCheck.Behavior = ForceClose or CloseAndReopen and
the listed processes are running at install time, the lib now:
1. Calls CloseMainWindow() on each matching Process handle (polite WM_CLOSE).
2. Waits GracefulCloseTimeoutSec (default 10) for exit.
3. Hard-kills the process if it did not exit gracefully.
4. Proceeds with the install.
"CloseAndReopen" is currently treated the same as ForceClose - no reopen
happens today. Stage 2b will add the user-session scheduled-task trick
to relaunch the closed app in the logged-in user's session. In practice
for the 24/7 ShopFloor persistent-user pattern the operator relaunches
the app manually (or the app is registered as Startup and reopens on
the next reboot), which is acceptable.
Concrete impact: the eDNC entry in standard-machine/manifest.json lists
InUseCheck.Processes = DncMain + NTLARS with Behavior=CloseAndReopen. On
a retrofit or upgrade cycle that finds eDNC 6.4.3 needs to go to 6.4.5,
the lib now force-closes DncMain and NTLARS before msiexec rather than
risking Restart Manager silently scheduling a pending-file-replace that
does not actually upgrade until the next reboot (which on a 24/7 PC
might be never).
Verified in the Win11 analyzer VM against manifests declaring InUseCheck
on eDNC - logs show "InUseCheck: DncMain (PID ...) asked to close"
followed by either graceful exit or the force-kill path, then install
proceeds without 3010.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Adds a CMM-style logon enforcer so VR-6000 updates push fleet-wide
without re-imaging.
- keyence-manifest.json: declares VR-6000 MSI (ProductCode-keyed) and
KEYENCE VR USB driver (pnputil-keyed). Single source of truth for
both imaging-time and ongoing-enforcement paths.
- lib/Install-FromManifest.ps1: forked from CMM/lib; adds DetectionMethod
"pnputil" (regex-matches `pnputil /enum-drivers` output) and Type
"INF" (invokes `pnputil /add-driver /install`). Everything else
unchanged so CMM-style error parsing + MSI log scanning carry over.
- Keyence-Enforce.ps1: forked from CMM-Enforce.ps1. SYSTEM scheduled
task, logon trigger, mounts tsgwp00525 SFLD share with creds from
HKLM:\SOFTWARE\GE\SFLD\Credentials (provisioned by Azure DSC),
hands off to Install-FromManifest against the share manifest.
- 09-Setup-Keyence.ps1: rewritten around the manifest. Runs
Install-FromManifest at imaging time, stages runtime scripts to
C:\Program Files\GE\Keyence, registers "GE Keyence Enforce"
scheduled task. Idempotent.
- site-config.json: add keyenceSharePath to the Keyence profile
pointing at \\tsgwp00525\shared\dt\shopfloor\keyence\machineapps.
To push a new VR-6000 version: drop the new MSI + updated manifest on
the tsgwp00525 share, every Keyence PC upgrades on next logon.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- shopfloor-setup/Keyence/09-Setup-Keyence.ps1: populate placeholder with
MSI install via msiexec and driver install via pnputil. Idempotent on
ProductCode {058E7194-...} and DriverStore entry. Logs to C:\Logs\Keyence\.
- shopfloor-setup/Keyence/installers/VR-6000 Series Software.msi: main
product (1.7 MB; pulled from Keyence6000.exe Inno wrapper's Windows
Installer cache, built with InstallShield 2019).
- shopfloor-setup/Keyence/drivers/: KEYENCE VR Series USB driver
package (.inf + .cat + Wdf/WinUsb co-installers). 2.7 MB, pulled from
DriverStore\FileRepository\keyence_vr_series.inf_amd64_b5e5eb0924d7b4ce.
- preinstall.json: add VC++ 2013 x64 Min + Add entries (PCTypes: ["*"])
as prereqs for VR-6000. GUIDs {A749D8E6-B613-...} and {929FBD26-9020-...}.
Staging footprint for non-Keyence PCs is unchanged (the 4.4 MB Keyence
payload lives under shopfloor-setup/Keyence/ which startnet.cmd only
xcopies for PCTYPE=Keyence). Rollout still requires dropping the two
VC++ 2013 x64 MSIs into \$PXE_IMAGES_DIR/dependencies/vcredist/2013-x64-{min,add}/
on the workstation running sync-preinstall.sh.
Rationale for bundling the MSI + driver locally rather than running
Keyence6000.exe: the Inno wrapper calls an InstallShield child (Setup.exe)
without silent flags, which hangs indefinitely in session 0 during
automated imaging. msiexec + pnputil from the extracted bundle runs
fully non-interactive.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- playbook/startnet.cmd + startnet-template.cmd: after preinstall staging,
xcopy Y:\pre-install\udc-backups to W:\PreInstall\udc-backups so UDC
settings JSONs are available during image deployment. Harvested from
live gold where this block existed but was never committed.
- scripts/mirror-from-gold.sh: update source paths to current taxonomy
layout (pre-install/, installers-post/, blancco/, config/) and add
ppkgs/, scripts/, shopfloor-setup/ sections. Added --delete for exact
mirror semantics. Used to seed the spare PXE server at 10.9.100.2 on
2026-04-16 from gold at 10.9.100.1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Register-MapSfldShare.ps1: swap scheduled task for HKLM\Run entry. Task with -GroupId runs in session 0 with no HKCU, so /persistent:yes fails and the drive mapping isn't visible to Explorer. Run key fires at Explorer startup in the interactive user's session with full token + HKCU. Unregisters legacy 'GE Shopfloor Map S: Drive' task for PCs already imaged.
- Run-ShopfloorSetup.ps1: stop bumping AutoLogonCount (99 at start, 4 at end). Windows decrements per-logon and at 0 clears AutoAdminLogon + DefaultPassword, which nukes the lockdown-configured ShopFloor autologon. Re-enable-wired-NICs task now gates on Autologon_Remediation.log 'Autologon set for ShopFloor' instead of SFLD creds, so wired stays off through the whole Intune+DSC+lockdown chain.
- Monitor-IntuneProgress.ps1: Phase 4 treats 'no custom scripts' as COMPLETE when DSC install is done (was WAITING, which stalled the state machine on PC types without scripts). Push retrigger out to 15min when entering lockdown-wait so a stale 5min retrigger doesn't fire mid-Remediation. Removed the AutoLogonCount delete in Invoke-SetupComplete since we no longer set it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AutoLogonCount depletion:
Run-ShopfloorSetup set AutoLogonCount=4 for SupportUser. Windows
decrements per-logon; at 0 it clears AutoAdminLogon + DefaultPassword,
nuking the lockdown-configured ShopFloor autologon. Fix: delete
AutoLogonCount in Invoke-SetupComplete before the lockdown reboot.
ShopFloor's Autologon.exe-set config persists indefinitely.
Sync_intune window on ShopFloor:
The marker-check path used 'exit 0' but the task runs with -NoExit,
leaving a dangling PowerShell window on every ShopFloor logon. Fix:
[Environment]::Exit(0) kills the host outright, defeating -NoExit.
S: drive mapping:
Vendor ConsumeCredentials.ps1 calls New-StoredCredential -Persist
LocalMachine (needs admin) before net use. ShopFloor is non-admin so
cred-store fails silently and net use has no auth. Fix: new
Map-SfldShare.ps1 reads HKLM creds and passes them inline to
net use /user: -- no Credential Manager needed, works as Limited.
Register-MapSfldShare updated to stage + reference our script.
Wired NIC re-enable:
SYSTEM task polls for SFLD creds (Phase 5), re-enables wired NICs,
self-deletes. Replaces the broken Enable-NetAdapter in Monitor
(Limited principal can't enable NICs). No-WiFi devices unaffected
(migrate-to-wifi never disables, re-enable is a no-op).
Sync throttle:
15 min retrigger when only waiting for lockdown (was 5 min for all
phases). Avoids interrupting the Intune Remediation script.
Defect Tracker path:
All references corrected to C:\Program Files (x86)\WJF_Defect_Tracker.
QR code retry:
Build-QRCodeText retried every poll cycle until DeviceId appears
(was single-shot that could miss the dsregcmd timing window).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces the Acrobat-only enforcer with a generic Common-Enforce that
handles all cross-PC-type apps from one manifest + one scheduled task
on the SFLD share at \\tsgwp00525\shared\dt\shopfloor\common\apps\.
Renames:
Acrobat-Enforce.ps1 -> Common-Enforce.ps1
Register-AcrobatEnforce -> Register-CommonEnforce
acrobat-manifest.json -> common-apps-manifest.json
common.acrobatSharePath -> common.commonAppsSharePath
'GE Acrobat Enforce' task -> 'GE Common Apps Enforce' task
C:\Program Files\GE\Acrobat -> C:\Program Files\GE\CommonApps
Register-CommonEnforce cleans up the legacy 'GE Acrobat Enforce' task
if present from a prior image.
WJF Defect Tracker (replaces ClickOnce):
- Added to preinstall.json (PCTypes=*, fleet-wide imaging-time install)
- MSI staged on PXE at pre-install/installers/
- Added to common-apps-manifest with FileVersion detection on
C:\Program Files\WJF_Defect_Tracker\Defect_Tracker.exe
- site-config + 06-OrganizeDesktop: shortcut changed from ClickOnce
'existing' to exe-path pointing at the MSI-installed binary
- Update workflow: drop new MSI on share, bump DetectionValue
CMM 09-Setup-CMM: added goCMM + DODA to the ACL grant list.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PC-DMIS writes settings, probe configs, and measurement data to its own
Program Files install directory at runtime. Without Modify permission
for BUILTIN\Users, non-admin accounts (ShopFloor) get a UAC elevation
prompt on every launch. The "run as admin once" workaround can't be
automated because PC-DMIS shows a license dialog on first run that
blocks silently.
Fix: grant BUILTIN\Users Modify with inheritance on:
- C:\Program Files\Hexagon\PC-DMIS 2016.0 64-bit
- C:\Program Files\Hexagon\PC-DMIS 2019 R2 64-bit
- C:\ProgramData\Hexagon
Runs as Step 2.5 in 09-Setup-CMM.ps1 after Install-FromManifest
completes. If the exe also has an embedded requireAdministrator manifest
(separate from the file-permission issue), that will need an additional
fix after testing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Pre-create Windows Firewall inbound-allow rules for UDC.exe and
MTConnect agent.exe before UDC_Setup.exe runs, suppressing the
interactive "allow through firewall?" dialogs during silent install.
- Set Adobe Acrobat Reader (Acrobat.Document.DC) as the default .pdf
handler via dism /import-defaultappassociations. Runs in
03-ShellDefaults.ps1 so the OEMDefaultAssociations.xml is in place
before ShopFloor's profile is created on first logon. Edge no longer
claims .pdf on new profiles.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause found via decompiling UDC_Setup.exe: it never writes
udc_settings.json from CLI args. Instead it pulls
Settings_Backups\udc_settings_<num>.json from \\tsgwp00525\shared\SPC\UDC
-- which is unreachable at imaging time (no SFLD creds yet). Silent
File.Exists() false, settings never copy, UDC lands on Evendale defaults.
Fix: stage 80 udc_settings_*.json backups under
shopfloor-setup/Standard/udc-backups/ (same tree as ntlars-backups,
xcopy'd to C:\Enrollment\ by existing startnet.cmd). 00-PreInstall
pre-creates C:\ProgramData\UDC\udc_settings.json from the matching
backup BEFORE UDC_Setup.exe runs. Installer's server-side copy silently
fails (unreachable), our pre-staged file survives.
Also:
- preinstall.json UDC InstallArgs corrected: "West Jefferson" -9999
(quoted spaced site + dash-prefixed number, confirmed via decompile)
- Update-MachineNumber.ps1 UDC.exe relaunch: quoted site + dash number
- Monitor-IntuneProgress: action prompts (Select Device Category after
Phase 1; Initiate ARTS Lockdown after Phase 5/creds), Display flow
(3-phase: Registration -> Config -> Lockdown), Phase 6 IME-based
lockdown detection
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>