Commit Graph

29 Commits

Author SHA1 Message Date
cproudlock
df443d5d28 ntlars-backups: normalize all 147 per-bay .reg files to wjfms3 + IP secondary
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>
2026-05-02 16:57:50 -04:00
cproudlock
42f6410d25 Revert Install-UDCWebServerConfig.cmd - v2 manifest Type=File handles natively, wrapper was redundant 2026-05-01 12:09:13 -04:00
cproudlock
e9fc284dcb Restore-UDCData: mount share with SFLD creds instead of raw UNC from SYSTEM
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'.
2026-05-01 11:50:04 -04:00
cproudlock
a72db8af5e Add Install-UDCWebServerConfig.cmd for ongoing manifest enforcement
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.
2026-04-30 12:58:00 -04:00
cproudlock
6e9053b83c 00-PreInstall: pre-stage udc_webserver_settings.json + firewall/NetFx3 hardening
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).
2026-04-30 12:16:41 -04:00
cproudlock
4f4f1f43e8 Restore-UDCData: handle ArchivedData-only backups (no CurrentData.json)
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>
2026-04-29 14:49:38 -04:00
cproudlock
26ecd0da0a 01-eDNC.ps1: match both eDNC-*.msi and eDNC_*.msi naming styles
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>
2026-04-29 13:45:38 -04:00
cproudlock
6b3690e286 Restore-UDCData: verbose per-cycle logging + share-reachability retry
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>
2026-04-29 12:49:04 -04:00
cproudlock
e169f8d0f5 Standard-Machine: UDC backup/restore use ArchivedData (not ArchiveData)
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>
2026-04-29 11:45:39 -04:00
cproudlock
0badfc1983 Retire v1 per-pctype enforcers; GE-Enforce is the sole dispatcher
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>
2026-04-29 09:55:40 -04:00
cproudlock
8564a37541 Standard-Machine: UDC data backup + restore scripts for PC swap workflow
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>
2026-04-29 09:27:20 -04:00
cproudlock
eb68793e79 Stage 2a: unified GE-Enforce framework + share-root mirror
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>
2026-04-22 11:19:23 -04:00
cproudlock
85e74e5dd1 UDC settings: pre-stage from server backups, fix arg format, action prompts
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>
2026-04-16 08:44:34 -04:00
cproudlock
8528a1bcae Install-FromManifest: add FileVersion detection for version-pinned upgrades
File-existence detection on NTLARS.exe couldn't tell eDNC 6.4.3 from 6.4.4
(both installers leave the same binary in place), so the enforcer skipped
upgrades. FileVersion compares the vendor-stamped FileVersion field on a
named binary against the manifest's DetectionValue with exact-string match.

Added to all three lib copies (common, Standard, CMM). Standard manifest
template flipped to FileVersion against DncMain.exe -- the eDNC main
binary is more reliably version-stamped than the bundled NTLARS sub-tool.

Update workflow now: drop the new vendor MSI on the SFLD share, bump
Installer + DetectionValue in machineapps-manifest.json, next user logon
runs Machine-Enforce which detects mismatch and installs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 16:15:32 -04:00
cproudlock
6db170bf54 Shell defaults + eDNC reg restore from machine-number backups
- 03-ShellDefaults.ps1: Default-User TaskbarAl=0 (left), HKLM policies to
  hide Start Recommended section, kill Bing web search + suggestions,
  disable Cortana. LTSC-honoured; runs fleet-wide via baseline loop.

- ntlars-backups/: 147 per-machine eDNC registry backups renamed to
  flat <MachineNumber>.reg scheme. Historical off-by-one entries from
  the original dump rewritten to match CSV-target MachineNo.

- Standard/03-RestoreEDncConfig.ps1: at imaging time, if tech typed a
  real machine number at PXE (not 9999), import <num>.reg from the local
  staged copy. Restores eFocas IP, PPDCS serial, Hssb relays -- not just
  the bare MachineNo. Skipped on Timeclock / 9999 / missing backup.

- Update-MachineNumber.ps1: when tech later sets a real number from 9999,
  pull <num>.reg from tsgwp00525 SFLD share (ntlarsBackupSharePath in
  site-config) and reg-import it before writing the new MachineNo.

- Restore-EDncReg.ps1: shared helper (Mount-SFLDShare + Import-EDncRegBackup)
  used by both callers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 15:42:21 -04:00
cproudlock
cc9aad0ea1 Install-FromManifest: add Hash detection for content-versioned files
Needed for eMxInfo.txt (site-specific eDNC config). The file has no
DisplayVersion in the registry and no canonical MSI; we ship it as a
standalone secret on the SFLD share and key drift correction off its
SHA256. When the yearly replacement drops, bump the hash in
machineapps-manifest.json and every Standard-Machine PC catches up on
next logon.

Patched Install-FromManifest in all three copies (CMM, common, Standard)
for consistency. Also adds the eMxInfo.txt entry to the Standard
machineapps-manifest template and an Install-eMxInfo.cmd template that
copies the file into both 32/64-bit eDNC Program Files paths.
2026-04-15 12:37:35 -04:00
cproudlock
3ef981f19e Add Standard-Machine logon enforcer for UDC/eDNC/NTLARS
Reason: Intune DSC's main-category YAML was pushing these to every main
device, including Timeclocks - DSC has no awareness of our pc-subtype
distinction. After UDC/eDNC/NTLARS are removed from the DSC YAML, ongoing
version drift would no longer be corrected. This enforcer replaces that,
scoped correctly by subtype.

Structure mirrors CMM (CMM-Enforce.ps1) and common (Acrobat-Enforce.ps1):
- Machine-Enforce.ps1: SYSTEM logon task; mounts SFLD share with HKLM-
  backed creds; hands off to Install-FromManifest.
- machineapps-manifest.template.json: repo reference; authoritative copy
  lives on the share at \\tsgwp00525.wjs.geaerospace.net\shared\dt\
  shopfloor\main\machineapps\machineapps-manifest.json.
- Register-MachineEnforce.ps1: idempotent setup; stages scripts to
  C:\Program Files\GE\MachineApps and registers the task.
- lib/Install-FromManifest.ps1: copy of the common/ version (already has
  Type=CMD support).

Sub-type gating belt-and-suspenders:
- Run-ShopfloorSetup.ps1 only calls Register-MachineEnforce when
  $pcType -eq "Standard" -and $pcSubType -eq "Machine".
- Machine-Enforce.ps1 itself re-reads pc-subtype.txt and exits early if
  not "Machine", so a mistakenly-deployed copy no-ops.

site-config.json:
- Added "machineappsSharePath" to Standard-Machine pcProfile.

Drive letter U: to stay clear of CMM (S:) and Acrobat (T:) enforcers
that may run concurrently at logon.

Update workflow:
  drop new UDC/eDNC/NTLARS installer on the SFLD share,
  bump DetectionValue in machineapps-manifest.json,
  every Machine PC catches up on next user logon.
2026-04-15 12:16:17 -04:00
cproudlock
855af7312b Sub-type aware preinstall, USB drivers/PPKGs, Lab OpenText
- PreInstall runner reads pc-subtype.txt and matches PCTypes against
  both base type (Standard) and composite key (Standard-Machine).
- UDC scoped to Standard-Machine only. eDNC and MachineNumberACLs
  skip on Standard-Timeclock sub-type.
- Lab added to OpenText PCTypes.
- build-usb.sh copies enrollment/ (PPKGs) and drivers-staging/ (Dell
  driver packs) onto USB for self-contained deployment.
- Playbook deploys PPKGs and drivers from USB to PXE server shares.
- Gitignore enrollment/, drivers-staging/, *.ppkg (large binaries).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:00:23 -04:00
cproudlock
bc123c1066 Machine number input at PXE menu for Standard PCs
Adds a machine number prompt to startnet.cmd after the Standard sub-type
selection. Tech enters the number during the PXE boot process. Defaults
to 9999 if Enter is pressed (existing placeholder behavior).

Written to C:\Enrollment\machine-number.txt alongside pc-type.txt.

Consumers:
  00-PreInstall-MachineApps.ps1 - replaces 9999 in UDC InstallArgs with
    the entered number, so UDC installs with the correct machine number
    from the start (no post-setup Set-MachineNumber needed).
  01-eDNC.ps1 - writes the machine number to the DNC\General\MachineNo
    registry value during eDNC install.
  Configure-PC.ps1 - existing $needsMachineNumber check already skips
    the prompt when UDC/eDNC aren't at 9999, so no change needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 08:50:02 -04:00
cproudlock
6c76719a47 Logging, PCTypes, edge profiles for all types
Three final optimization batches:

1. Start-Transcript added to 4 scripts that lacked standalone logging:
   04-NetworkAndWinRM.ps1, 05-OfficeShortcuts.ps1, 01-eDNC.ps1,
   02-MachineNumberACLs.ps1. Each writes to C:\Logs\SFLD\<name>.log
   with append mode. Stop-Transcript added before exit points.

2. preinstall.json: Oracle Client PCTypes changed from ["*"] to
   ["Standard", "CMM", "Genspect", "Keyence", "WaxAndTrace", "Display"].
   Lab Workstations don't need Oracle Client (shopfloor data app
   dependency). VC++ redists stay at ["*"] (harmless shared deps).

3. Edge profiles added to all remaining PC types in site-config.json:
   CMM, Genspect, Keyence, WaxAndTrace, Standard-Timeclock all get the
   standard 3-tab setup (Plant Apps + Homepage + Dashboard) with
   homepage = tsgwp00524. Display-Lobby and Display-Dashboard get
   Shopfloor Dashboard as both homepage and single tab.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:57:22 -04:00
cproudlock
7c8eb6899d Shared machine-number helper, site-config for OpenText + PreInstall, placeholder type dirs
Three optimization batches from the pipeline audit:

1. Shared Update-MachineNumber.ps1 helper (lib/)
   Extracts duplicated machine-number update logic from Configure-PC.ps1,
   Check-MachineNumber.ps1, and Set-MachineNumber.ps1 into a shared
   dot-sourceable helper at Shopfloor/lib/Update-MachineNumber.ps1.

   Exports:
     Get-CurrentMachineNumber → @{ Udc = $string; Ednc = $string }
     Update-MachineNumber -NewNumber <n> [-Site <s>] → @{ UdcUpdated; EdncUpdated; Errors }

   All three consumers now dot-source the helper instead of duplicating
   ~50 lines each. Set-MachineNumber.ps1 also migrated from inline
   Get-SiteConfig to dot-sourcing Get-PCProfile.ps1 for consistency.

2. Site-config integration for remaining scripts
   Setup-OpenText.ps1: exclude lists (profiles + shortcuts) now read from
     site-config.json opentext section, falling back to West Jefferson
     defaults. Inline Get-SiteConfig since the script runs from
     C:\PreInstall\installers\opentext\ (can't dot-source Get-PCProfile).

   00-PreInstall-MachineApps.ps1: after parsing preinstall.json, scans
     InstallArgs for "West Jefferson" and replaces with site-config
     siteName if different. Inline Get-SiteConfig for same reason.

3. Placeholder type-specific directories
   Created skeleton 01-Setup-*.ps1 scripts for all PC types so the
   directory structure is in place and Run-ShopfloorSetup's type-specific
   loop has something to iterate over:
     Genspect/01-Setup-Genspect.ps1
     Keyence/01-Setup-Keyence.ps1
     WaxAndTrace/01-Setup-WaxAndTrace.ps1
     Lab/01-Setup-Lab.ps1
   Each logs a "no type-specific apps configured yet" banner and exits.
   Fill in app installs when details are finalized; for share-based
   installs, copy the CMM/01-Setup-CMM.ps1 pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:44:10 -04:00
cproudlock
0aaf049942 Extract site-specific values to site-config.json
New site-config.json file at C:\Enrollment\ (staged by startnet.cmd from
the enrollment share) contains all West Jefferson-specific values that were
previously hardcoded across 7 scripts. To deploy at a different GE site,
clone site-config.json and change the values - scripts need zero changes.

Config schema (v1.0):
  siteName / siteNameCompact  - UDC/eDNC site args
  urls{}                      - Edge startup tab fallback URLs
  edgeStartupTabs[]           - ordered tab list with .url file basenames
  opentext{}                  - excluded .hep profiles and .lnk shortcuts
  startupItems[]              - Configure-PC toggle list (exe/existing/url)
  taskbarPins[]               - 07-TaskbarLayout pin order with lnk paths
  desktopApps[]               - 06-OrganizeDesktop Phase 2 app list

Every script uses the same inline Get-SiteConfig helper that reads the
JSON and returns $null if missing/corrupt. All consumers fall back to the
current hardcoded West Jefferson defaults when $siteConfig is null, so
PXE servers without a site-config.json continue working identically.

Scripts updated:
  06-OrganizeDesktop.ps1   - desktopApps array from config
  07-TaskbarLayout.ps1     - pinSpec array from config
  08-EdgeDefaultBrowser.ps1 - startup tab loop from config
  Configure-PC.ps1         - startup items + site name from config
  Check-MachineNumber.ps1  - site name from config
  Set-MachineNumber.ps1    - site name from config
  01-eDNC.ps1              - siteName + siteNameCompact from config
  startnet.cmd             - copies site-config.json from enrollment share

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:11:35 -04:00
cproudlock
e17b3a521d Fix 5 bugs from shopfloor-setup transcript review
1. UDC JSON ACL: set on directory C:\ProgramData\UDC\ with
   ContainerInherit+ObjectInherit instead of the file. UDC_Setup.exe
   gets killed by KillAfterDetection before UDC.exe creates
   udc_settings.json, so the file doesn't exist at ACL-grant time.
   Directory-level ACL with inheritance covers any file created later.

2. Set-MachineNumber.ps1 auto-running: the type-specific loop's
   Get-ChildItem -Filter "*.ps1" picked up the desktop tool alongside
   the numbered installer scripts. Added Where-Object { $_.Name -match
   '^\d' } so only numbered-prefix scripts (01-eDNC, 02-ACLs) run.

3. WJ Shopfloor copy-to-self: Phase 1 sweep moved WJ Shopfloor.lnk
   into Shopfloor Tools\, then Phase 2's Find-ExistingLnk found it
   there and tried to Copy-Item to the same path. Now checks if
   resolved source path == destination and prints "exists: (already
   in Shopfloor Tools)" instead of erroring.

4. NTLARS missing from taskbar pins: the $pinSpec entry was never
   added to 07-TaskbarLayout.ps1 despite the comment update. Added
   between eDNC and Defect_Tracker in pin order.

5. shutdown /a stderr noise: 15+ red "Unable to abort system shutdown"
   lines in the transcript from shutdown.exe writing to stderr when no
   shutdown is pending. Changed all occurrences in Run-ShopfloorSetup,
   00-PreInstall-MachineApps to: cmd /c "shutdown /a 2>nul" *>$null
   which suppresses both native stderr and PS error stream.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 09:28:25 -04:00
cproudlock
cb2a9d48a1 Shopfloor: Configure-PC tool, machine-number logon prompt, execution order fixes
New tools:

Configure-PC.bat/.ps1 - Interactive desktop tool for SupportUser to
configure a shopfloor PC after imaging. Two sections:
  1. Machine number: if UDC/eDNC are still at placeholder 9999, prompt
     to set the real number right now (updates UDC JSON + eDNC registry,
     restarts UDC.exe with new args).
  2. Auto-startup toggle: pick which apps start at user logon from a
     numbered list (UDC, eDNC, Defect Tracker, WJ Shopfloor, Plant Apps).
     Creates/removes .lnk files in AllUsers Startup folder. Toggle UI
     shows [ON]/[  ] state, safe to re-run anytime. Plant Apps URL
     resolved from .url file at runtime with hardcoded fallback to
     https://mes-wjefferson.apps.lr.geaerospace.net/run/...
  3. Item 6 in the toggle list: register/unregister a "Check Machine
     Number" logon task for standard (non-admin) users. When enabled,
     the task fires at every logon, checks for 9999, pops an InputBox
     if found, updates both apps, then unregisters itself on success.

Check-MachineNumber.ps1 - The logon task script. Runs as the logged-in
user (needs GUI for InputBox), not SYSTEM. Writing to ProgramData + HKLM
is possible because 02-MachineNumberACLs.ps1 pre-grants BUILTIN\Users
write access on the two specific targets during imaging.

02-MachineNumberACLs.ps1 - Standard type-specific script (runs after
01-eDNC.ps1). Opens C:\ProgramData\UDC\udc_settings.json for Users:Modify
and HKLM:\...\GE Aircraft Engines\DNC\General for Users:SetValue. Narrow
scope, not blanket admin.

Execution order fixes in Run-ShopfloorSetup.ps1:

The dispatcher now has two lists: $skipInBaseline (scripts NOT run in the
alphabetical baseline loop) and $runAfterTypeSpecific (scripts run
explicitly after type-specific scripts complete). This fixes the bug where
06/07 ran before 01-eDNC.ps1 installed DnC, so eDNC/NTLARS shortcuts were
silently skipped.

New execution order:
  Baseline: 00-PreInstall, 04-NetworkAndWinRM (skipping 05-08 + tools)
  Type-specific: 01-eDNC, 02-MachineNumberACLs
  Finalization: 06-OrganizeDesktop, 07-TaskbarLayout

06 internally calls 05 (Office shortcuts, Phase 0) and 08 (Edge config,
Phase 4) as sub-phases, so they also benefit from running late. Office
isn't installed until after the first reboot (ppkg streams C2R), so 05
no-ops at imaging time but succeeds when 06's SYSTEM logon task re-runs
it on the second boot. 08 resolves startup-tab URLs from .url files
delivered by DSC (even later); same self-heal via the logon task.

Other fixes in this commit:

- OpenText Setup-OpenText.ps1 Step 4: exclude WJ_Office.lnk, IBM_qks.lnk,
  mmcs.lnk desktop shortcuts (matching the Step 3 .hep profile exclusion
  from the previous commit). Removes stale copies from prior installs.
- 05-OfficeShortcuts.ps1: widened Office detection to 6 path variants
  covering C2R + MSI + Office15/16, with diagnostic output on miss.
- 06-OrganizeDesktop.ps1: removed Phase 3 (desktop-root pin copies for
  eDNC/NTLARS) so shortcuts live in Shopfloor Tools only, not duplicated
  at root. Emptied $keepAtRoot. Added Phase 0 (call 05) and Phase 4
  (call 08). Lazy folder creation + empty-folder cleanup. Scheduled task
  now runs as SYSTEM (was BUILTIN\Users with Limited which failed the
  admin check). Added NTLARS to 07's taskbar pin list.
- 08-EdgeDefaultBrowser.ps1: Plant Apps URL fallback hardcoded from
  device-config.yaml.
- All new scripts have Start-Transcript logging to C:\Logs\SFLD\ with
  timestamps and running-as identity.
- Run-ShopfloorSetup.ps1: Start-Transcript + Stop-Transcript wrapping
  entire dispatcher run, writes to C:\Logs\SFLD\shopfloor-setup.log.
  Configure-PC.bat added to SupportUser desktop copy list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 08:44:28 -04:00
cproudlock
c464f45f4f Shopfloor sync_intune + Set-MachineNumber hardening
Long debugging round on the shopfloor test PC with several overlapping
bugs. This commit folds all the fixes together.

sync_intune.bat
- Slim down to an elevation thunk that launches a NEW elevated PS
  window via Start-Process -Verb RunAs (with -NoExit so the window
  doesn't vanish on error). All UI now lives in the PS monitor, not
  mixed into the cmd launcher.
- Goto-based control flow. Earlier version had nested if (...) blocks
  with literal parens inside echo lines (e.g. "wrappers (Install-eDNC,
  ...etc)."); cmd parses if-blocks by counting parens character-by-
  character, so the ")" in "etc)." closed the outer block early and
  the leftover "." threw ". was unexpected at this time.", crashing
  the elevated cmd /c window before pause ran.
- Multi-location Monitor-IntuneProgress.ps1 lookup so the user's
  quick-test workflow (drop both files on the desktop) works without
  manually editing the hardcoded path. Lookup order:
    1. %~dp0lib\Monitor-IntuneProgress.ps1
    2. %~dp0Monitor-IntuneProgress.ps1
    3. C:\Users\SupportUser\Desktop\Monitor-IntuneProgress.ps1
    4. C:\Enrollment\shopfloor-setup\Shopfloor\lib\Monitor-IntuneProgress.ps1
- Prints "Launching: <path>" as its first line so you can see which
  copy it actually loaded. This caught a bug where a stale desktop
  copy was shadowing the canonical file via fallback #2.

Set-MachineNumber.bat
- Same multi-location lookup pattern. Old version used
  %~dp0Set-MachineNumber.ps1 and bombed when the bat was copied to
  the desktop without its .ps1 sibling.
- Goto-based dispatch, no nested parens, for the same parser reason.

Monitor-IntuneProgress.ps1
- Start-Transcript at the top, writing to C:\Logs\SFLD\ (falls back
  to %TEMP% if C:\Logs\SFLD isn't writable yet) with a startup banner
  including a timestamp. Every run leaves a captured trace.
- Main polling loop wrapped in try/catch/finally. Unhandled exceptions
  print a red report with type, message, position, and stack trace,
  then block on Wait-ForAnyKey so the window can't auto-close on a
  silent crash.
- Console window resize at startup via $Host.UI.RawUI.WindowSize /
  BufferSize, wrapped in try/catch (Windows Terminal ignores it, but
  classic conhost honors it).
- Clear-KeyBuffer / Read-SingleKey / Wait-ForAnyKey helpers. Drain any
  buffered keystrokes from the polling loop before each prompt so an
  accidental keypress can't satisfy a pause prematurely.
- Invoke-SetupComplete / Invoke-RebootPrompt final-state handlers.
  The REBOOT REQUIRED branch now shows a yellow 3-line header, a
  four-line explanation, and a cyan "Press Y to reboot now, or N to
  cancel:" prompt via Read-SingleKey @('Y','N'). Y triggers
  Restart-Computer -Force (with shutdown.exe fallback), N falls
  through to Wait-ForAnyKey.
- Display order: status table FIRST, QR LAST. The cursor ends below
  the QR so the viewport always follows it - keeps the QR on screen
  regardless of window height. Works on both classic conhost and
  Windows Terminal (neither reliably honors programmatic resize).
- Half-block QR renderer: walks QRCoder's ModuleMatrix directly and
  emits U+2580 / U+2584 / U+2588 / space, one output line per two
  matrix rows. Halves the rendered height vs AsciiQRCode full-block.
  Quiet zone added manually via $pad=4 since QRCoder's ModuleMatrix
  doesn't include one. Trade-off: may not be perfectly square on all
  fonts, but the user accepted that for the smaller footprint after
  multiple iterations comparing full-block vs half-block vs PNG popup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:30:12 -04:00
cproudlock
ded0a7184b Shopfloor scripts: em-dash to hyphen, add positional Level param
Replace em-dash characters with plain hyphens across the 5 shopfloor
setup scripts (avoids cp1252 mojibake in .bat files and keeps the
PowerShell sources consistent). Also adds [Parameter(Position=1)] to
Write-PreInstallLog so the Level argument can be passed positionally.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:24:37 -04:00
cproudlock
a1a78e2ba3 PXE preinstall pipeline + Set-MachineNumber helper for Standard PCs
Adds a local-install pipeline so Standard shopfloor PCs get Oracle, the
VC++ redists (2008-2022), and UDC installed during PXE imaging via Samba
instead of pulling ~215 MB per device from Azure blob over the corporate
WAN. Intune DSC then verifies (already-installed apps are skipped) and
the only Azure traffic on the happy path is ~11 KB of CustomScripts
wrapper polling.

New files:
- playbook/preinstall/preinstall.json — curated app list with PCTypes
  filter and per-app detection rules. Install order puts VC++ 2008
  LAST so its (formerly) reboot-triggering bootstrapper doesn't kill
  the runner mid-loop. (2008 itself now uses extracted vc_red.msi with
  REBOOT=ReallySuppress; the reorder is defense in depth.)
- playbook/shopfloor-setup/Shopfloor/00-PreInstall-MachineApps.ps1 —
  the runner. Numbered 00- so it runs first in the baseline sequence.
  Reads preinstall.json, filters by PCTYPE, polls for completion via
  detection check (handles UDC's hung WPF process by killing it once
  detection passes), uses synchronous WriteThrough logging that
  survives hard reboots, preserves log history across runs.
- playbook/shopfloor-setup/Standard/Set-MachineNumber.{ps1,bat} — desktop
  helper for SupportUser. Reads current UDC + eDNC machine numbers,
  prompts via VB InputBox, validates digits-only, kills running UDC,
  edits both C:\ProgramData\UDC\udc_settings.json and HKLM\…\GE Aircraft
  Engines\DNC\General\MachineNo, relaunches UDC. Lets a tech assign a
  real machine number to a mass-produced PC without admin/LAPS.
- playbook/sync-preinstall.sh — workstation helper to push installer
  binaries from /home/camp/pxe-images/main/ to the live PXE Samba.

Changes:
- playbook/startnet.cmd + startnet-template.cmd — add xcopy to stage
  preinstall bundle from Y:\preinstall\ to W:\PreInstall\ during the
  WinPE imaging phase, gated on PCTYPE being set.
- playbook/pxe_server_setup.yml — create /srv/samba/enrollment/preinstall
  + installers/ directories and deploy preinstall.json there.
- playbook/shopfloor-setup/Run-ShopfloorSetup.ps1 — bump AutoLogonCount
  to 99 at start (defense against any installer triggering an immediate
  reboot mid-dispatcher; final line still resets to 2 on successful
  completion). Copy Set-MachineNumber.{ps1,bat} to SupportUser desktop
  on Standard PCs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:06:26 -04:00
cproudlock
9c54307b1b Shopfloor cleanups: drop OpenText CSF + MarkZebra, gitignore eMxInfo
- Delete 02-OpenTextCSF.ps1 (CSF profile delivery moved to Intune YAML's
  CopyFiles section in main/device-config.yaml — no longer needed at the
  PXE/baseline layer)
- Strip MarkZebra install + post-config from 01-eDNC.ps1 (no longer
  needed; only eDNC core install + Dnc x86→x64 mirror + Site reg + eMxInfo
  deployment remain). Section numbering tightened.
- Add SITESELECTED="West Jefferson" to eDNC msiexec args so the MSI's
  site-specific Components (NtLarsWjfRegComp — FTP/FMS/PPDCS hosts +
  credentials) actually install. Without it, only the bare Site value was
  being set and all the connection details were unconfigured.
- gitignore: blanket-block any **/eMxInfo*.txt from being committed —
  the file contains obfuscated eDNC site credentials and must never go
  in git. Canonical source lives at /home/camp/pxe-images/main/eMxInfo.txt
  outside the repo.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:05:52 -04:00
cproudlock
76165495ff Shopfloor PC type system, webapp enhancements, slim Blancco GRUB
- Shopfloor PC type menu (CMM, WaxAndTrace, Keyence, Genspect, Display, Standard)
- Baseline scripts: OpenText CSF, Start Menu shortcuts, network/WinRM, power/display
- Standard type: eDNC + MarkZebra with 64-bit path mirroring
- CMM type: Hexagon CLM Tools, PC-DMIS 2016/2019 R2
- Display sub-type: Lobby vs Dashboard
- Webapp: enrollment management, image config editor, UI refresh
- Upload-Image.ps1: robocopy MCL cache to PXE server
- Download-Drivers.ps1: Dell driver download pipeline
- Slim Blancco GRUB EFI (10MB -> 660KB) for old hardware compat
- Shopfloor display imaging guide docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:25:07 -04:00