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) <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-05-27 07:03:32 -04:00
parent 5c3db71879
commit 55c1ab4814
4 changed files with 141 additions and 8 deletions

View File

@@ -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" } }

View File

@@ -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
# ============================================================================

View File

@@ -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

View File

@@ -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"
}