From 55c1ab4814378179a362b9c6278a9b463563019d Mon Sep 17 00:00:00 2001 From: cproudlock Date: Wed, 27 May 2026 07:03:32 -0400 Subject: [PATCH] CMM first-run-as-admin, controller credential user-context fix, IE compat hash 09-Setup-CMM: add Step 2.6 that launches each installed PC-DMIS version once as admin before the PPKG locks the machine down. Also adds PC-DMIS 2026.1 to the ACL directory list. Controller credential: cmdkey /add under SYSTEM stored creds in the wrong vault. Switch to a Register script (MarkerFile detection, runs once) that creates an AtLogOn scheduled task under BUILTIN\Users so cmdkey runs in the ShopFloor user's session. IE compat: update test matrix hash for the new site list that adds wjfms3.apps.wlm.geaerospace.net. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../shopfloor-setup/common/test/matrix.json | 2 +- .../gea-shopfloor-cmm/09-Setup-CMM.ps1 | 39 +++++++- .../Register-ControllerCredentialTask.ps1 | 98 +++++++++++++++++++ .../manifest-entry-controller-credential.json | 10 +- 4 files changed, 141 insertions(+), 8 deletions(-) create mode 100644 playbook/shopfloor-setup/gea-shopfloor-collections/Register-ControllerCredentialTask.ps1 diff --git a/playbook/shopfloor-setup/common/test/matrix.json b/playbook/shopfloor-setup/common/test/matrix.json index 643f015..5d6f3eb 100644 --- a/playbook/shopfloor-setup/common/test/matrix.json +++ b/playbook/shopfloor-setup/common/test/matrix.json @@ -7,7 +7,7 @@ { "name": "Adobe Acrobat Reader DC", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{AC76BA86-7AD7-1033-7B44-AC0F074E4100}", "name": "DisplayVersion", "value": "25.001.20531" } }, { "name": "WJF Defect Tracker", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{CC1B4D32-1606-4A3F-8F24-31312F723D5C}", "name": "DisplayVersion", "value": "01.00.0102" } }, { "name": "3OF9 barcode font", "verify": { "method": "File", "path": "C:\\Windows\\Fonts\\3OF9.ttf" } }, - { "name": "Edge IE-Mode site list", "verify": { "method": "Hash", "path": "C:\\ProgramData\\Edge\\enterprise-mode-site-list.xml", "value": "16F2A6E45EFA19ED7B1C54B264D6B33597678D3A5303255BC7CEB7E8510C60FC" } } + { "name": "Edge IE-Mode site list", "verify": { "method": "Hash", "path": "C:\\ProgramData\\Edge\\enterprise-mode-site-list.xml", "value": "E13073B2D89E120560AF638F08519E94CC1DC880FFEF5D6A4C7011430E21E4EA" } } ], "fmsResolver": [ { "name": "FMS hosts pin", "verify": { "method": "FileGrep", "path": "C:\\Windows\\System32\\drivers\\etc\\hosts", "pattern": "10\\.233\\.112\\.158\\s+wjfms3\\.ae\\.ge\\.com" } } diff --git a/playbook/shopfloor-setup/gea-shopfloor-cmm/09-Setup-CMM.ps1 b/playbook/shopfloor-setup/gea-shopfloor-cmm/09-Setup-CMM.ps1 index ee0cc9c..a4e1748 100644 --- a/playbook/shopfloor-setup/gea-shopfloor-cmm/09-Setup-CMM.ps1 +++ b/playbook/shopfloor-setup/gea-shopfloor-cmm/09-Setup-CMM.ps1 @@ -139,12 +139,12 @@ else { # PC-DMIS writes settings, probe configs, and measurement data to its own # install directory at runtime. Without Modify permission for BUILTIN\Users, # non-admin accounts get a UAC elevation prompt on every launch. Granting -# the ACL here is the Hexagon-documented approach for non-admin deployment -# and avoids the need for a first-run-as-admin (which hits a license dialog -# and can't be automated silently). +# the ACL here is the Hexagon-documented approach for non-admin deployment. +# Step 2.6 below handles the required first-run-as-admin initialization. $pcdmisDirs = @( 'C:\Program Files\Hexagon\PC-DMIS 2016.0 64-bit', 'C:\Program Files\Hexagon\PC-DMIS 2019 R2 64-bit', + 'C:\Program Files\Hexagon\PC-DMIS 2026.1 64-bit', 'C:\ProgramData\Hexagon', 'C:\Program Files (x86)\General Electric\goCMM', 'C:\Program Files\DODA' @@ -171,6 +171,39 @@ foreach ($dir in $pcdmisDirs) { } } +# ============================================================================ +# Step 2.6: First-run-as-admin for each installed PC-DMIS version +# ============================================================================ +# PC-DMIS performs one-time initialization on first launch (COM registration, +# config file creation, internal setup). This must happen with admin rights +# before the PPKG locks the machine down. Launch each installed version, +# wait for it to initialize, then kill it. +$pcdmisExes = @( + 'C:\Program Files\Hexagon\PC-DMIS 2016.0 64-bit\PCDLRN.exe', + 'C:\Program Files\Hexagon\PC-DMIS 2019 R2 64-bit\PCDLRN.exe', + 'C:\Program Files\Hexagon\PC-DMIS 2026.1 64-bit\PCDLRN.exe' +) +foreach ($exe in $pcdmisExes) { + if (-not (Test-Path -LiteralPath $exe)) { continue } + $ver = Split-Path (Split-Path $exe -Parent) -Leaf + Write-CMMLog "First-run init: launching $ver" + try { + $proc = Start-Process -FilePath $exe -PassThru -ErrorAction Stop + $initTimeout = 45 + Write-CMMLog " PID $($proc.Id) started, waiting ${initTimeout}s for initialization..." + Start-Sleep -Seconds $initTimeout + if (-not $proc.HasExited) { + $proc.Kill() + $proc.WaitForExit(10000) + Write-CMMLog " Killed after ${initTimeout}s (first-run init complete)" + } else { + Write-CMMLog " Exited on its own (exit $($proc.ExitCode))" + } + } catch { + Write-CMMLog " First-run launch failed: $_" 'WARN' + } +} + # ============================================================================ # Step 3: Conditional cleanup of the bootstrap staging dir # ============================================================================ diff --git a/playbook/shopfloor-setup/gea-shopfloor-collections/Register-ControllerCredentialTask.ps1 b/playbook/shopfloor-setup/gea-shopfloor-collections/Register-ControllerCredentialTask.ps1 new file mode 100644 index 0000000..8ae951d --- /dev/null +++ b/playbook/shopfloor-setup/gea-shopfloor-collections/Register-ControllerCredentialTask.ps1 @@ -0,0 +1,98 @@ +# Register-ControllerCredentialTask.ps1 - Register a user-context AtLogOn +# scheduled task that runs Set-ControllerCredential.ps1 as the logged-in +# user (ShopFloor). Credential Manager entries are per-user, so cmdkey /add +# must run in the user's session, not SYSTEM. +# +# GE-Enforce (SYSTEM) runs THIS script via the manifest. It registers the +# task once (MarkerFile detection prevents re-registration on subsequent +# enforce cycles). The task itself fires at every logon and re-applies the +# credential, covering Defender / Intune scrubs between sessions. +# +# Idempotent: safe to re-run. Existing task is overwritten via -Force. + +$ErrorActionPreference = 'Continue' + +$logDir = 'C:\Logs\Shopfloor' +if (-not (Test-Path $logDir)) { + try { New-Item -Path $logDir -ItemType Directory -Force | Out-Null } catch { $logDir = $env:TEMP } +} +$logFile = Join-Path $logDir 'register-controller-cred.log' + +function Write-RegLog { + param([string]$Message, [string]$Level = 'INFO') + $line = '[{0}] [{1}] {2}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Level, $Message + Add-Content -Path $logFile -Value $line -ErrorAction SilentlyContinue + Write-Host $line +} + +Write-RegLog '=== Register-ControllerCredentialTask start ===' + +$taskName = 'Apply Controller Credential' + +# The actual cmdkey script lives alongside this registration script on the +# SFLD share. GE-Enforce mounts the share as W:, so the script path is +# relative to the share root. At task-fire time, however, the share may not +# be mounted. Copy the script to a local path so the logon task always works. +$localDir = 'C:\Program Files\GE\Shopfloor\controller-cred' +$localScript = Join-Path $localDir 'Set-ControllerCredential.ps1' + +# Resolve the source script. When GE-Enforce runs this, $PSScriptRoot +# points to the share-mounted dir (W:\gea-shopfloor-collections\apps\ or +# similar). Fall back to the same directory as this script. +$sourceScript = Join-Path $PSScriptRoot 'Set-ControllerCredential.ps1' +if (-not (Test-Path -LiteralPath $sourceScript)) { + Write-RegLog "Set-ControllerCredential.ps1 not found at $sourceScript" 'ERROR' + exit 1 +} + +# Stage the script locally. +if (-not (Test-Path $localDir)) { + New-Item -Path $localDir -ItemType Directory -Force | Out-Null +} +try { + Copy-Item -LiteralPath $sourceScript -Destination $localScript -Force -ErrorAction Stop + Write-RegLog "Staged $sourceScript -> $localScript" +} catch { + Write-RegLog "Failed to copy script locally: $_" 'ERROR' + exit 1 +} + +# Register the logon task for BUILTIN\Users (catches ShopFloor + any +# interactive user). RunLevel Limited -- no elevation needed for cmdkey. +try { + $action = New-ScheduledTaskAction ` + -Execute 'powershell.exe' ` + -Argument "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$localScript`"" + $trigger = New-ScheduledTaskTrigger -AtLogOn + $principal = New-ScheduledTaskPrincipal -GroupId 'S-1-5-32-545' -RunLevel Limited + $settings = New-ScheduledTaskSettingsSet ` + -AllowStartIfOnBatteries ` + -DontStopIfGoingOnBatteries ` + -StartWhenAvailable ` + -ExecutionTimeLimit (New-TimeSpan -Minutes 2) + Register-ScheduledTask ` + -TaskName $taskName ` + -Action $action ` + -Trigger $trigger ` + -Principal $principal ` + -Settings $settings ` + -Force ` + -ErrorAction Stop | Out-Null + Write-RegLog "Registered scheduled task '$taskName' (AtLogOn, BUILTIN\Users, Limited)" +} catch { + Write-RegLog "FAILED to register '$taskName': $_" 'ERROR' + exit 1 +} + +# Write marker file so the manifest's MarkerFile detection prevents +# re-running this registration on every enforce cycle. +$markerDir = 'C:\ProgramData\GE\Shopfloor\markers' +if (-not (Test-Path $markerDir)) { + New-Item -Path $markerDir -ItemType Directory -Force | Out-Null +} +$markerFile = Join-Path $markerDir 'controller-credential-task.marker' +Set-Content -Path $markerFile -Value (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') -Force +Write-RegLog "Marker written: $markerFile" + +Write-RegLog '=== Register-ControllerCredentialTask end ===' +exit 0 diff --git a/playbook/shopfloor-setup/gea-shopfloor-collections/manifest-entry-controller-credential.json b/playbook/shopfloor-setup/gea-shopfloor-collections/manifest-entry-controller-credential.json index d594806..de742e1 100644 --- a/playbook/shopfloor-setup/gea-shopfloor-collections/manifest-entry-controller-credential.json +++ b/playbook/shopfloor-setup/gea-shopfloor-collections/manifest-entry-controller-credential.json @@ -1,8 +1,10 @@ { - "_comment": "Drop this entry into the SFLD share at \\tsgwp00525\\sfld$\\v2\\shared\\dt\\shopfloor\\gea-shopfloor-collections\\manifest.json (Applications array). Edit Set-ControllerCredential.ps1 first to fill in the real $Username + $Password before pushing the script + manifest. Detection is omitted on purpose: Install-FromManifest treats missing DetectionMethod as 'not installed' and runs the script every enforce cycle, which is exactly what we want (Defender / Intune occasionally scrubs the entry; we re-apply on each cycle). cmdkey /add is idempotent so re-running is harmless. TargetMachineNumbers is optional; remove it to apply to every collections bay, or list specific machine numbers to scope down.", - "Name": "Controller credential for Okuma LOC650 bays (192.168.1.1)", + "_comment": "Drop this entry into the SFLD share at \\tsgwp00525\\sfld$\\v2\\shared\\dt\\shopfloor\\gea-shopfloor-collections\\manifest.json (Applications array). Place both Register-ControllerCredentialTask.ps1 AND Set-ControllerCredential.ps1 in the apps/ dir on the share. Edit Set-ControllerCredential.ps1 to fill in the real $Username + $Password before pushing. The Register script runs once under SYSTEM (MarkerFile detection), copies Set-ControllerCredential.ps1 locally, and creates an AtLogOn scheduled task that runs cmdkey /add in the ShopFloor user's session. The task re-applies every logon, covering Defender / Intune credential scrubs.", + "Name": "Controller credential task for Okuma LOC650 bays (192.168.1.1)", "PCTypes": ["gea-shopfloor-collections"], "TargetMachineNumbers": ["3201", "3202", "3203", "3204", "3205", "3206", "3207", "3208", "3209", "3210", "3211", "3212"], - "Script": "apps/Set-ControllerCredential.ps1", - "Type": "PS1" + "Script": "apps/Register-ControllerCredentialTask.ps1", + "Type": "PS1", + "DetectionMethod": "MarkerFile", + "DetectionPath": "C:\\ProgramData\\GE\\Shopfloor\\markers\\controller-credential-task.marker" }