Shopfloor imaging: CMM type, Configure-PC override fix, serial drivers

- CMM imaging pipeline: WinPE-staged bootstrap + on-logon enforcer
  against tsgwp00525 share, manifest-driven installer runner shared via
  Install-FromManifest.ps1. Installs PC-DMIS 2016/2019 R2, CLM 1.8,
  goCMM; enables .NET 3.5 prereq; registers GE CMM Enforce logon task
  for ongoing version enforcement.
- Shopfloor serial drivers: StarTech PCIe serial + Prolific PL2303
  USB-to-serial via Install-Drivers.cmd wrapper calling pnputil
  /add-driver /subdirs /install. Scoped to Standard PCs.
- OpenText extended to CMM/Keyence/Genspect/WaxAndTrace via
  preinstall.json PCTypes; Defect Tracker added to CMM profile
  desktopApps + taskbarPins.
- Configure-PC startup-item toggle now persists across the logon
  sweep via C:\\ProgramData\\GE\\Shopfloor\\startup-overrides.json;
  06-OrganizeDesktop Phase 3 respects suppressed items.
- Get-ProfileValue helper added to Shopfloor/lib/Get-PCProfile.ps1;
  distinguishes explicit empty array from missing key (fixes Lab
  getting Plant Apps in startup because empty array was falsy).
- 06-OrganizeDesktop gains transcript logging at C:\\Logs\\SFLD\\
  06-OrganizeDesktop.log and now deletes the stale Shopfloor Intune
  Sync task when C:\\Enrollment\\sync-complete.txt is present (task
  was registered with Limited principal and couldn't self-unregister).
- startnet.cmd CMM xcopy block (gated on pc-type=CMM) stages the
  bundle to W:\\CMM-Install during WinPE.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-04-11 12:58:47 -04:00
parent bc123c1066
commit ee7d3bad66
17 changed files with 1069 additions and 196 deletions

View File

@@ -46,6 +46,50 @@ if (-not $isAdmin) {
exit 1
}
# Transcript. 06 runs as a SYSTEM scheduled task at every logon via the
# 'Organize Public Desktop' task, so without a transcript its decisions
# (what got added, what got suppressed) are invisible. Append mode keeps
# a history across logons; session header + PID makes individual runs
# easy to find.
$transcriptDir = 'C:\Logs\SFLD'
if (-not (Test-Path $transcriptDir)) {
try { New-Item -ItemType Directory -Path $transcriptDir -Force | Out-Null } catch {}
}
$transcriptPath = Join-Path $transcriptDir '06-OrganizeDesktop.log'
try { Start-Transcript -Path $transcriptPath -Append -Force | Out-Null } catch {}
Write-Host ""
Write-Host "================================================================"
Write-Host "=== 06-OrganizeDesktop session start (PID $PID, $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')) ==="
Write-Host "Running as: $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)"
Write-Host "================================================================"
# ============================================================================
# Cleanup: remove 'Shopfloor Intune Sync' task after sync completes
#
# Monitor-IntuneProgress.ps1 registers that task as BUILTIN\Users + RunLevel
# Limited (needs a user context to show the QR code / Configure-PC prompt),
# and a Limited task cannot Unregister-ScheduledTask itself. So when sync
# completes it just writes C:\Enrollment\sync-complete.txt as a marker and
# the task stays around firing on every logon as a no-op.
#
# 06 runs as SYSTEM via its own sweep task, which DOES have permission to
# delete any task. When we see the marker, drop the stale task so Task
# Scheduler doesn't keep the dead entry forever.
# ============================================================================
$syncCompleteMarker = 'C:\Enrollment\sync-complete.txt'
$syncTaskName = 'Shopfloor Intune Sync'
if (Test-Path -LiteralPath $syncCompleteMarker) {
$syncTask = Get-ScheduledTask -TaskName $syncTaskName -ErrorAction SilentlyContinue
if ($syncTask) {
try {
Unregister-ScheduledTask -TaskName $syncTaskName -Confirm:$false -ErrorAction Stop
Write-Host "Removed stale '$syncTaskName' task (sync complete, marker present)."
} catch {
Write-Warning "Failed to remove '$syncTaskName' task: $_"
}
}
}
# Load site config + PC profile
. "$PSScriptRoot\lib\Get-PCProfile.ps1"
@@ -280,11 +324,9 @@ function Add-ShopfloorToolsApps {
#
# Kind = 'exe' -> build a fresh .lnk from ExePath
# Kind = 'existing' -> copy an existing .lnk via Find-ExistingLnk
$cfgApps = if ($pcProfile -and $pcProfile.desktopApps) { $pcProfile.desktopApps }
elseif ($siteConfig -and $siteConfig.desktopApps) { $siteConfig.desktopApps }
else { $null }
$cfgApps = Get-ProfileValue 'desktopApps'
if ($cfgApps) {
if ($null -ne $cfgApps -and $cfgApps.Count -gt 0) {
$apps = @($cfgApps | ForEach-Object {
$entry = @{ Name = $_.name; Kind = $_.kind }
if ($_.kind -eq 'exe') { $entry.ExePath = $_.exePath }
@@ -490,16 +532,27 @@ Add-ShopfloorToolsApps
Write-Host ""
Write-Host "=== Phase 3: auto-apply startup items from PC profile ==="
# Creates .lnk files in AllUsers Startup folder based on the profile's
# startupItems. This is the auto-apply step that eliminates the need
# for Configure-PC to ask about startup items during the imaging chain.
# Configure-PC.bat on the desktop still lets the tech modify them later.
# startupItems. This runs as SYSTEM at every logon via the sweep scheduled
# task, so any item the tech removes via Configure-PC would bounce right
# back unless we remember the removal. startup-overrides.json tracks labels
# the tech has explicitly suppressed; items in that list are skipped here
# and re-enabling them via Configure-PC removes them from the list.
$startupDir = 'C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup'
$overridesPath = 'C:\ProgramData\GE\Shopfloor\startup-overrides.json'
$cfgStartup = if ($pcProfile -and $pcProfile.startupItems) { $pcProfile.startupItems }
elseif ($siteConfig -and $siteConfig.startupItems) { $siteConfig.startupItems }
else { $null }
$suppressed = @()
if (Test-Path -LiteralPath $overridesPath) {
try {
$overrides = Get-Content -LiteralPath $overridesPath -Raw | ConvertFrom-Json
if ($overrides.suppressed) { $suppressed = @($overrides.suppressed) }
} catch {
Write-Warning " Failed to parse $overridesPath : $_"
}
}
if ($cfgStartup -and $cfgStartup.Count -gt 0) {
$cfgStartup = Get-ProfileValue 'startupItems'
if ($null -ne $cfgStartup -and $cfgStartup.Count -gt 0) {
if (-not (Test-Path $startupDir)) {
New-Item -ItemType Directory -Path $startupDir -Force | Out-Null
}
@@ -509,6 +562,10 @@ if ($cfgStartup -and $cfgStartup.Count -gt 0) {
) | Where-Object { Test-Path -LiteralPath $_ } | Select-Object -First 1
foreach ($si in $cfgStartup) {
if ($suppressed -contains $si.label) {
Write-Host " suppressed: $($si.label) (tech disabled via Configure-PC)"
continue
}
$lnkPath = Join-Path $startupDir "$($si.label).lnk"
# Skip if already exists (idempotent - don't overwrite user changes)
if (Test-Path -LiteralPath $lnkPath) {
@@ -567,4 +624,7 @@ Write-Host ""
Write-Host "=== Phase 6: register logon sweeper scheduled task ==="
Register-SweepScheduledTask -ScriptPath $scriptPath
Write-Host ""
Write-Host "=== 06-OrganizeDesktop session end ($(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')) ==="
try { Stop-Transcript | Out-Null } catch {}
exit 0