Commit Graph

17 Commits

Author SHA1 Message Date
cproudlock
cd00d6d2e1 OpenText: track Setup-OpenText scripts in repo, opt-in KillAfterDetection
Two related fixes from a debugging round on the test PC:

1. PreInstall runner: detection-during-install kill is now opt-in via
   "KillAfterDetection: true" on JSON entries that need it. Old behavior
   killed any installer as soon as its detection passed - which broke
   Oracle: Oracle creates its registry key partway through install,
   the runner detected it at the 25s poll, killed msiexec mid-install,
   and msiserver was still doing rollback when the next install (VC++
   2008) started - so VC++ 2008 hit ERROR_INSTALL_ALREADY_RUNNING
   (1618). Only UDC needs the detection-kill (its installer spawns a
   hidden WPF window and never exits). Other installers exit cleanly
   on their own and shouldn't be killed.

2. Track Setup-OpenText scripts in git. The bundled OpenText install
   scripts (Setup-OpenText.ps1, Setup-OpenText.cmd, version.txt) live
   at runtime in /home/camp/pxe-images/main/dependencies/opentext/
   alongside the binary install files (~106 MB of MSI/CAB/MSP/MST plus
   profile content). The binaries stay outside git but the script
   logic and version stamp are mirrored into playbook/preinstall/
   opentext/ here so git history captures changes to the install
   logic and version bumps. README.md explains the workflow.

   Latest Setup-OpenText.ps1 includes:
     - $SourceDir default moved into script body (PowerShell evaluates
       param([string]$X = $PSScriptRoot) defaults at parameter-binding
       time, when $PSScriptRoot may not yet be populated, so the
       default came out as empty string and Join-Path crashed)
     - Logging set up FIRST so any startup error gets captured
     - REBOOT=ReallySuppress dropped from both msiexec calls (base MSI
       and SP1 patch) - OpenText installs shell extensions that hook
       explorer.exe, and Restart Manager closes explorer to replace
       the shell DLLs. With REBOOT=ReallySuppress, RM closed explorer
       but interpreted the relaunch as a "reboot action" and refused
       to do it, leaving the user with no desktop. /norestart on its
       own prevents the actual Windows reboot but lets RM cleanly
       close-and-relaunch explorer mid-install.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:08:07 -04:00
cproudlock
5eacd1d596 PreInstall runner: surface installer log on EXE failures (LogFile field)
When the runner runs a Type:MSI install it injects /L*v <log> and tails
that log on failure to show what actually went wrong. Type:EXE installs
had no equivalent - if Setup-OpenText.cmd or any other EXE wrapper
failed, the installlog just showed "Exit code 1 - FAILED" with no clue
what happened inside.

Adds an optional LogFile field to JSON entries. When present on a
Type:EXE entry, the runner:
  - Logs "Installer log: <path>" before launching the installer
  - On failure, tails the last 30 lines of that file into the runner
    log (same pattern as the MSI verbose log scan)

Wired up on the OpenText entry to point at C:\Logs\PreInstall\Setup-
OpenText.log (which Setup-OpenText.ps1 already writes itself). Other
EXE entries can opt in by adding their own LogFile field.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 11:19:45 -04:00
cproudlock
a33a115394 Move Monitor-IntuneProgress.ps1 to lib/ - it was hanging the dispatcher
Run-ShopfloorSetup.ps1 line 46-47 does:

  Get-ChildItem -Path $baselineDir -Filter "*.ps1" -File | Sort-Object Name
  foreach ($script in $scripts) { & $script.FullName }

This picks up EVERY *.ps1 in Shopfloor\ and runs it as a baseline
script. Last commit (66d13d8) put Monitor-IntuneProgress.ps1 in that
same directory, which means the dispatcher was running it as the LAST
baseline script (M sorts after 00/04/05). The monitor is an infinite
poll loop that never returns until the SFLD lifecycle is complete -
so the dispatcher hung there forever, and Standard\01-eDNC.ps1 and
Standard\Set-MachineNumber.ps1 never ran.

Symptoms in the test run:
  - 00-PreInstall-MachineApps.ps1 ran (10 installed, 1 OpenText fail)
  - 04-NetworkAndWinRM.ps1 ran silently
  - 05-OfficeShortcuts.ps1 ran silently
  - Monitor-IntuneProgress.ps1 started (Clear-Host + status table) and
    hung in its main loop
  - eDNC + Set-MachineNumber never ran

Fix: move Monitor-IntuneProgress.ps1 into Shopfloor\lib\ so the
dispatcher's non-recursive Get-ChildItem doesn't see it. Update
sync_intune.bat's MONITOR path to the new location, and add a
comment explaining WHY the monitor lives under lib\ to prevent this
mistake from being repeated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 11:19:09 -04:00
cproudlock
66d13d8ad0 sync_intune: rewrite as 5-phase status monitor with reboot detection
Replaces the 3-step pass/fail polling that lived in the .bat with a
PowerShell monitor that renders a full status table for the SFLD
enrollment lifecycle and handles the pre-reboot -> reboot -> post-reboot
transition explicitly.

Three structural problems with the old script:

1. Step 3 ("SFLD - Consume Credentials task exists") fired too early.
   The task is created by SetupCredentials.log around 08:52 in the
   pre-reboot phase, NOT post-reboot, so passing all 3 gates didn't
   actually mean "fully done" - it just meant "credential setup ran".

2. No detection of the pre-reboot -> reboot -> post-reboot transition.
   The script never read DSCDeployment.log, so it couldn't tell the
   user "you need to reboot now to start the install phase". A device
   stuck waiting for reboot was indistinguishable from one still
   syncing.

3. No visibility into Phase 4 (per-script wrappers like Install-eDNC,
   Install-UDC, Install-VCRedists, Install-OpenText). When something
   hung you had to manually grep C:\Logs\SFLD\.

New layout:

  sync_intune.bat - thin launcher (~50 lines): self-elevate, invoke
                    Monitor-IntuneProgress.ps1, branch on exit code
                    (0 = done / 2 = reboot needed / else = error).

  Monitor-IntuneProgress.ps1 - the actual monitor (~340 lines):
    - 5-phase status table (Identity / SFLD config / DSC deployment +
      install / Custom scripts / Final) updated every 30s via Clear-
      Host + redraw, with the QR code anchored at the top.
    - Phase 4 auto-discovers custom scripts by parsing DSCInstall.log
      for "Downloading script: <name>" lines AND scanning C:\Logs\SFLD\
      Install-*.log files - so Display PCs running entirely different
      scripts surface their own list automatically without hardcoding.
      Statuses: pending / running / done / failed (mtime + tail-based).
    - Boot-loop-safe reboot detection via Test-RebootState: only signals
      'needed' if DSCDeployment.log was modified AFTER LastBootUpTime.
      Once we've rebooted past it, just waits for DSCInstall.log.
    - Caches monotonic Phase 1 indicators (AzureAdJoined, IntuneEnrolled,
      EnterpriseMgmt task) so dsregcmd /status (slow ~1-2s) only runs
      until the flag flips true, not on every poll.
    - Triggers Intune sync at startup, re-triggers every 3 minutes (was
      every 15 seconds in the old loop, which actively interrupted
      in-flight CSP work).

  Exit codes consumed by sync_intune.bat:
    0 - DSCInstall.log shows "Installation completed successfully"
    2 - DSCDeployment.log shows "Deployment completed successfully" AND
        the deploy log is newer than LastBootUpTime (= reboot needed)
    1 - error

Detection markers (decoded from a captured run at /home/camp/pxe-images/
Logs/ - see comment block at top of Monitor-IntuneProgress.ps1).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:56:20 -04:00
cproudlock
453b42a159 Shopfloor: 05-OfficeShortcuts.ps1 baseline (Excel/Word/PowerPoint)
Office Click-to-Run installs the binaries when an Office-bearing ppkg
is selected (e.g. GCCH_Prod_SFLD_StdOffice-x86_*) but doesn't create
desktop shortcuts - operators only see Office in the Start Menu's
Microsoft 365 folder. This baseline script fills that gap.

Self-detects Office by EXE existence at C:\\Program Files\\Microsoft
Office\\root\\Office16\\ or the (x86) equivalent. No Office found =
silent no-op, so it's safe to run on every PC type (Display kiosks,
Wax/Trace, Keyence, etc.) without needing a per-type filter.

Creates Excel.lnk / Word.lnk / PowerPoint.lnk in two places:
- C:\\Users\\Public\\Desktop\\  - visible to all users immediately
- C:\\Users\\Default\\AppData\\Roaming\\Microsoft\\Windows\\Start
  Menu\\Programs\\  - inherited by every NEW user profile created
  on the device (Azure AD operator logons after enrollment)

Numbered 05- so it runs after 00-PreInstall and 04-NetworkAndWinRM
in the Shopfloor baseline sequence. Idempotent - WScript.Shell's
CreateShortcut overwrites existing .lnks each run.

Outlook / OneNote / Access / Publisher intentionally not shortcutted
(scope decision; can be added by extending the $officeApps array).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:11:08 -04:00
cproudlock
7a27f1a0a1 sync_intune.bat: longer poll interval, in-place status, no sync flood
Two related fixes for the desktop helper:

1. Stop hammering the Intune sync trigger every 15 seconds. The old
   loop called :do_sync (Start-ScheduledTask on Schedule #3) on every
   failed check, which started a fresh CSP pull before the previous
   one had time to complete - the Intune engine treats a re-trigger
   as "start over" and kills in-flight policy application work, so
   nothing ever finished. New cadence: trigger sync once at the start
   of each step, then poll every 30 s, only re-trigger every 6 polls
   (~3 min). POLL_SECS and RETRIGGER_POLLS are top-of-script knobs.

2. Stop pushing the QR code off the top of the window. The old loop
   echoed "Checking again in 15s..." on a new line every iteration,
   so after a few minutes the QR code (which contains the device ID
   the operator scans) had scrolled out of view. Replaced the per-
   iteration echo with a single self-redrawing status line using a
   captured CR character (copy /Z trick) and <nul set /p, padded to
   clear leftover characters. Important transitions ("Re-triggering
   sync...", "[DONE] ...") still print echo. lines so they survive in
   the scrollback as permanent history.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:10:51 -04:00
cproudlock
564f14ffcf PreInstall runner: capture real exit codes, surface MSI errors
Three observability fixes that made the VC++ MSI failures actually
debuggable instead of showing "Exit code  - FAILED" with an empty
value for every install:

1. Switch from Start-Process -PassThru (without -Wait) to
   [System.Diagnostics.Process]::Start() with a ProcessStartInfo.
   PowerShell 5.1 has a known bug where Start-Process disposes the
   Process object's OS handle when control returns to the script,
   so $proc.ExitCode reads as $null even after WaitForExit() - which
   was causing every MSI install to be reported as failed regardless
   of the actual result.

2. Pass /L*v <log> to msiexec on every MSI install so we get a full
   verbose log per app at C:\Logs\PreInstall\msi-<safename>.log.

3. On install failure, scan the verbose log for *meaningful* lines
   (Note: 1: <code>, "return value 3", custom action errors, "Failed
   to", "Installation failed", common 2xxx error codes) instead of
   tailing the last 25 lines, which is rollback/cleanup noise. This
   surfaces the actual root-cause line directly in the runner log so
   you don't have to dig through C:\Logs to diagnose.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:08:42 -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
05fa74574a Intune sync: 3-step lockdown monitor, fix batch detection, remove backup_lockdown
sync_intune.bat now monitors three stages sequentially:
1. SFLD registry key (device configuration received)
2. DSCInstall.log success string (DSC installation complete)
3. SFLD - Consume Credentials scheduled task (lockdown complete)
Triggers Intune sync before each poll. Prompts reboot on completion.

Fixed batch delayed expansion bugs, removed nested if/goto blocks.
Removed backup_lockdown.bat and its desktop copy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 10:52:31 -04:00
cproudlock
e3f2bbc6a5 Add QR code display of Intune device ID to sync tool
Bundles QRCoder.dll (184KB, .NET 4.0) to render the Azure AD device
GUID as a scannable QR code in the console when sync_intune.bat runs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 10:15:20 -04:00
cproudlock
1ba4cce80f Remove shopfloor power and display settings script
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 09:46:38 -04:00
cproudlock
a570efda71 Remove shopfloor Start Menu shortcuts script
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 09:45:21 -04:00
cproudlock
9912b044a3 Shopfloor: single autologon, clear Start pins, Intune sync tool, update docs
- AutoLogonCount reduced from 2 to 1 in Run-ShopfloorSetup.ps1
- Remove default pinned Start Menu tiles and set blank layout for future users
- Add sync_intune.bat: triggers MDM sync and polls for SFLD group policies
- Update README.md and SETUP.md with current project state (boot chain, new
  scripts, samba shares, webapp pages, commit history)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 09:43:00 -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
cproudlock
6d0e6ee284 BIOS check fix, parallel downloads, shopfloor hardening
- Fix check-bios.cmd: replace parenthesized if blocks with goto labels
  (cmd.exe fails silently with if/else on network-mapped drives)
- Move BIOS check files to winpeapps/_shared/BIOS for reliable SMB access
- Add network wait loop before BIOS check in startnet.cmd
- Show firmware status in WinPE menu header (BIOS_STATUS variable)
- Add BypassNRO registry key to skip OOBE network requirement
- Refactor download-drivers.py with --parallel N flag (ThreadPoolExecutor)
- Set SupportUser AutoLogonCount to 3 in shopfloor unattend
- Add shutdown -a at start + shutdown /r /t 10 at end of Run-ShopfloorSetup.ps1
- Switch download-drivers.py from wget to curl for reliable stall detection

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 11:02:36 -04:00