Unified Common-Enforce for cross-type apps, add WJF Defect Tracker

Replaces the Acrobat-only enforcer with a generic Common-Enforce that
handles all cross-PC-type apps from one manifest + one scheduled task
on the SFLD share at \\tsgwp00525\shared\dt\shopfloor\common\apps\.

Renames:
  Acrobat-Enforce.ps1        -> Common-Enforce.ps1
  Register-AcrobatEnforce    -> Register-CommonEnforce
  acrobat-manifest.json      -> common-apps-manifest.json
  common.acrobatSharePath    -> common.commonAppsSharePath
  'GE Acrobat Enforce' task  -> 'GE Common Apps Enforce' task
  C:\Program Files\GE\Acrobat -> C:\Program Files\GE\CommonApps

Register-CommonEnforce cleans up the legacy 'GE Acrobat Enforce' task
if present from a prior image.

WJF Defect Tracker (replaces ClickOnce):
  - Added to preinstall.json (PCTypes=*, fleet-wide imaging-time install)
  - MSI staged on PXE at pre-install/installers/
  - Added to common-apps-manifest with FileVersion detection on
    C:\Program Files\WJF_Defect_Tracker\Defect_Tracker.exe
  - site-config + 06-OrganizeDesktop: shortcut changed from ClickOnce
    'existing' to exe-path pointing at the MSI-installed binary
  - Update workflow: drop new MSI on share, bump DetectionValue

CMM 09-Setup-CMM: added goCMM + DODA to the ACL grant list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-04-16 11:13:05 -04:00
parent 1a5feefb01
commit f73f999938
9 changed files with 264 additions and 28 deletions

View File

@@ -136,7 +136,9 @@ else {
$pcdmisDirs = @(
'C:\Program Files\Hexagon\PC-DMIS 2016.0 64-bit',
'C:\Program Files\Hexagon\PC-DMIS 2019 R2 64-bit',
'C:\ProgramData\Hexagon'
'C:\ProgramData\Hexagon',
'C:\Program Files (x86)\General Electric\goCMM',
'C:\Program Files\DODA'
)
foreach ($dir in $pcdmisDirs) {
if (-not (Test-Path -LiteralPath $dir)) {

View File

@@ -245,13 +245,13 @@ Write-Host "Auto-logon set to 4 remaining logins."
# for version-pinned app enforcement. Initial install already handled by
# preinstall flow; enforcers only kick in when detection fails.
$commonSetupDir = Join-Path $PSScriptRoot 'common'
$registerAcrobat = Join-Path $commonSetupDir 'Register-AcrobatEnforce.ps1'
if (Test-Path -LiteralPath $registerAcrobat) {
$registerCommon = Join-Path $commonSetupDir 'Register-CommonEnforce.ps1'
if (Test-Path -LiteralPath $registerCommon) {
Write-Host ""
Write-Host "=== Registering Acrobat enforcer ==="
try { & $registerAcrobat } catch { Write-Warning "Acrobat enforce registration failed: $_" }
Write-Host "=== Registering Common Apps enforcer ==="
try { & $registerCommon } catch { Write-Warning "Common enforce registration failed: $_" }
} else {
Write-Host "Register-AcrobatEnforce.ps1 not found (optional) - skipping"
Write-Host "Register-CommonEnforce.ps1 not found (optional) - skipping"
}
# Map S: drive on user logon for every account in BUILTIN\Users. The

View File

@@ -339,7 +339,7 @@ function Add-ShopfloorToolsApps {
@{ Name = 'eDNC'; Kind = 'exe'; ExePath = 'C:\Program Files (x86)\Dnc\bin\DncMain.exe' }
@{ Name = 'NTLARS'; Kind = 'exe'; ExePath = 'C:\Program Files (x86)\Dnc\Common\NTLARS.exe' }
@{ Name = 'WJ Shopfloor'; Kind = 'existing'; SourceName = 'WJ Shopfloor.lnk' }
@{ Name = 'Defect_Tracker'; Kind = 'existing'; SourceName = 'Defect_Tracker.lnk' }
@{ Name = 'Defect_Tracker'; Kind = 'exe'; ExePath = 'C:\Program Files\WJF_Defect_Tracker\Defect_Tracker.exe' }
)
}

View File

@@ -0,0 +1,123 @@
# Common-Enforce.ps1 - On-logon enforcer for cross-PC-type apps (Acrobat
# Reader, WJF Defect Tracker, future common apps).
#
# Runs under a SYSTEM scheduled task triggered at user logon on every PC
# regardless of PC type. Mounts the tsgwp00525 SFLD share (common\apps
# path) using SFLD creds provisioned by Azure DSC, reads
# common-apps-manifest.json from the share, and hands off to
# Install-FromManifest.ps1 which installs anything whose detection fails.
#
# Update workflow: drop new installer on the share, bump DetectionValue in
# common-apps-manifest.json, every PC catches up on next logon.
#
# Graceful degradation:
# - SFLD creds missing (Azure DSC has not run yet) -> log + exit 0
# - Share unreachable (network, VPN) -> log + exit 0
# - Install failure -> log + exit 0
#
# Never returns non-zero to the task scheduler; failures show up in the log.
$ErrorActionPreference = 'Continue'
$installRoot = 'C:\Program Files\GE\CommonApps'
$libPath = Join-Path $installRoot 'lib\Install-FromManifest.ps1'
$logDir = 'C:\Logs\CommonApps'
$logFile = Join-Path $logDir ('enforce-{0}.log' -f (Get-Date -Format 'yyyyMMdd'))
$driveLetter = 'T:'
if (-not (Test-Path $logDir)) {
New-Item -Path $logDir -ItemType Directory -Force | Out-Null
}
function Write-EnforceLog {
param([string]$Message, [string]$Level = 'INFO')
$line = "[{0}] [{1}] {2}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Level, $Message
Write-Host $line
Add-Content -Path $logFile -Value $line -ErrorAction SilentlyContinue
}
Write-EnforceLog "================================================================"
Write-EnforceLog "=== Common-Enforce session start (PID $PID, user $env:USERNAME) ==="
Write-EnforceLog "================================================================"
$getProfileScript = 'C:\Enrollment\shopfloor-setup\Shopfloor\lib\Get-PCProfile.ps1'
if (-not (Test-Path $getProfileScript)) {
Write-EnforceLog "Get-PCProfile.ps1 not found at $getProfileScript - cannot locate share" "ERROR"
exit 0
}
. $getProfileScript
if (-not $siteConfig -or -not $siteConfig.common -or -not $siteConfig.common.commonAppsSharePath) {
Write-EnforceLog "No common.commonAppsSharePath in site-config - nothing to enforce" "WARN"
exit 0
}
$sharePath = $siteConfig.common.commonAppsSharePath
Write-EnforceLog "Share: $sharePath"
function Get-SFLDCredential {
param([string]$ServerName)
$basePath = 'HKLM:\SOFTWARE\GE\SFLD\Credentials'
if (-not (Test-Path $basePath)) { return $null }
foreach ($entry in Get-ChildItem -Path $basePath -ErrorAction SilentlyContinue) {
$props = Get-ItemProperty -Path $entry.PSPath -ErrorAction SilentlyContinue
if (-not $props -or -not $props.TargetHost) { continue }
if ($props.TargetHost -eq $ServerName -or
$props.TargetHost -like "$ServerName.*" -or
$ServerName -like "$($props.TargetHost).*") {
return @{
Username = $props.Username
Password = $props.Password
TargetHost = $props.TargetHost
KeyName = $entry.PSChildName
}
}
}
return $null
}
$serverName = ($sharePath -replace '^\\\\', '') -split '\\' | Select-Object -First 1
$cred = Get-SFLDCredential -ServerName $serverName
if (-not $cred -or -not $cred.Username -or -not $cred.Password) {
Write-EnforceLog "No SFLD credential for $serverName yet (Azure DSC has not provisioned it) - will retry at next logon"
exit 0
}
Write-EnforceLog "Credential: $($cred.KeyName) (user: $($cred.Username))"
net use $driveLetter /delete /y 2>$null | Out-Null
$netResult = & net use $driveLetter $sharePath /user:$($cred.Username) $($cred.Password) /persistent:no 2>&1
if ($LASTEXITCODE -ne 0) {
Write-EnforceLog "net use failed (exit $LASTEXITCODE): $netResult" "WARN"
Write-EnforceLog "Share unreachable - probably off-network. Will retry at next logon."
exit 0
}
Write-EnforceLog "Mounted $sharePath as $driveLetter"
try {
$manifestOnShare = Join-Path $driveLetter 'common-apps-manifest.json'
if (-not (Test-Path $manifestOnShare)) {
Write-EnforceLog "common-apps-manifest.json not found on share - nothing to enforce" "WARN"
return
}
if (-not (Test-Path $libPath)) {
Write-EnforceLog "Install-FromManifest.ps1 not found at $libPath" "ERROR"
return
}
Write-EnforceLog "Handing off to Install-FromManifest.ps1 (InstallerRoot=$driveLetter)"
& $libPath -ManifestPath $manifestOnShare -InstallerRoot $driveLetter -LogFile $logFile
$rc = $LASTEXITCODE
Write-EnforceLog "Install-FromManifest returned $rc"
}
finally {
net use $driveLetter /delete /y 2>$null | Out-Null
Write-EnforceLog "Unmounted $driveLetter"
Write-EnforceLog "=== Common-Enforce session end ==="
}
exit 0

View File

@@ -0,0 +1,91 @@
# Register-CommonEnforce.ps1 - Stage Common-Enforce.ps1 + Install-FromManifest
# and register the 'GE Common Apps Enforce' logon task. Cross-PC-type: called
# from Run-ShopfloorSetup.ps1 for every shopfloor image.
#
# Replaces the former Acrobat-only enforcer with a single task that handles
# all common apps (Acrobat, Defect Tracker, future additions) from one
# manifest on the SFLD share.
$ErrorActionPreference = 'Continue'
$installRoot = 'C:\Program Files\GE\CommonApps'
$runtimeLib = Join-Path $installRoot 'lib\Install-FromManifest.ps1'
$runtimeEnforce = Join-Path $installRoot 'Common-Enforce.ps1'
$logDir = 'C:\Logs\CommonApps'
$setupLog = Join-Path $logDir 'setup.log'
$sourceLib = Join-Path $PSScriptRoot 'lib\Install-FromManifest.ps1'
$sourceEnforce = Join-Path $PSScriptRoot 'Common-Enforce.ps1'
if (-not (Test-Path $logDir)) { New-Item -Path $logDir -ItemType Directory -Force | Out-Null }
if (-not (Test-Path $installRoot)) { New-Item -Path $installRoot -ItemType Directory -Force | Out-Null }
if (-not (Test-Path (Join-Path $installRoot 'lib'))) {
New-Item -Path (Join-Path $installRoot 'lib') -ItemType Directory -Force | Out-Null
}
function Write-SetupLog {
param([string]$Message, [string]$Level = 'INFO')
$line = "[{0}] [{1}] {2}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Level, $Message
Write-Host $line
Add-Content -Path $setupLog -Value $line -ErrorAction SilentlyContinue
}
Write-SetupLog "=== Register-CommonEnforce start ==="
foreach ($pair in @(
@{ Src = $sourceLib; Dst = $runtimeLib },
@{ Src = $sourceEnforce; Dst = $runtimeEnforce }
)) {
if (-not (Test-Path $pair.Src)) {
Write-SetupLog "Source not found: $($pair.Src) - cannot stage" "ERROR"
continue
}
Copy-Item -Path $pair.Src -Destination $pair.Dst -Force
Write-SetupLog "Staged $($pair.Src) -> $($pair.Dst)"
}
# Clean up old Acrobat-only enforcer if present (from prior images).
foreach ($oldTask in @('GE Acrobat Enforce')) {
$old = Get-ScheduledTask -TaskName $oldTask -ErrorAction SilentlyContinue
if ($old) {
Write-SetupLog "Removing legacy task '$oldTask'"
Unregister-ScheduledTask -TaskName $oldTask -Confirm:$false -ErrorAction SilentlyContinue
}
}
$taskName = 'GE Common Apps Enforce'
$existing = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if ($existing) {
Write-SetupLog "Removing existing scheduled task '$taskName'"
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
}
Write-SetupLog "Registering scheduled task '$taskName' (logon trigger, SYSTEM)"
try {
$action = New-ScheduledTaskAction `
-Execute 'powershell.exe' `
-Argument "-NoProfile -ExecutionPolicy Bypass -File `"$runtimeEnforce`""
$trigger = New-ScheduledTaskTrigger -AtLogOn
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-ExecutionTimeLimit (New-TimeSpan -Minutes 30) `
-MultipleInstances IgnoreNew
Register-ScheduledTask `
-TaskName $taskName `
-Action $action `
-Trigger $trigger `
-Principal $principal `
-Settings $settings `
-Description 'GE Common Apps: enforce Acrobat, Defect Tracker, and other cross-type apps against tsgwp00525 SFLD share on user logon' | Out-Null
Write-SetupLog "Scheduled task registered"
} catch {
Write-SetupLog "Failed to register scheduled task: $_" "ERROR"
}
Write-SetupLog "=== Register-CommonEnforce end ==="

View File

@@ -1,17 +0,0 @@
{
"Version": "1.0",
"_comment": "Adobe Acrobat Reader DC enforcement manifest. This is the TEMPLATE kept in the repo; the authoritative copy lives on the SFLD share at \\\\tsgwp00525.wjs.geaerospace.net\\shared\\dt\\shopfloor\\common\\acrobat\\acrobat-manifest.json. Acrobat-Enforce.ps1 reads the share copy on every user logon via the 'GE Acrobat Enforce' scheduled task. To push a new Acrobat update to the entire fleet: drop the new AcroRdrDCUpd*.msp in the share, update Install-AcroReader.cmd to reference its filename, bump DetectionValue below to the new version string, and the enforcer catches every PC on next logon. Initial install still happens during preinstall (not this enforcer) - the enforcer only kicks in when detection fails, which on fresh installs is never.",
"Applications": [
{
"_comment": "Two-step install (MSI + MST transform, then MSP patch) done via the vendor-shipped Install-AcroReader.cmd wrapper. Wrapper uses %~dp0 so it finds AcroRead.msi, AcroRead.mst, Data1.cab, and AcroRdrDCUpd*.msp next to itself on the share. DetectionValue pins the patched version: bump it when replacing the MSP.",
"Name": "Adobe Acrobat Reader DC",
"Installer": "Install-AcroReader.cmd",
"Type": "CMD",
"LogFile": "C:\\Logs\\Acrobat\\install.log",
"DetectionMethod": "Registry",
"DetectionPath": "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{AC76BA86-7AD7-1033-7B44-AC0F074E4100}",
"DetectionName": "DisplayVersion",
"DetectionValue": "25.001.20531"
}
]
}

View File

@@ -0,0 +1,27 @@
{
"Version": "1.0",
"_comment": "Common cross-PC-type app enforcement manifest. TEMPLATE in repo; authoritative copy on SFLD share at \\\\tsgwp00525.wjs.geaerospace.net\\shared\\dt\\shopfloor\\common\\acrobat\\acrobat-manifest.json. Acrobat-Enforce.ps1 reads the share copy on every user logon. Update workflow: drop new installer on share, bump DetectionValue, next logon catches it.",
"Applications": [
{
"_comment": "Two-step install (MSI + MST transform, then MSP patch) done via the vendor-shipped Install-AcroReader.cmd wrapper.",
"Name": "Adobe Acrobat Reader DC",
"Installer": "Install-AcroReader.cmd",
"Type": "CMD",
"LogFile": "C:\\Logs\\Acrobat\\install.log",
"DetectionMethod": "Registry",
"DetectionPath": "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{AC76BA86-7AD7-1033-7B44-AC0F074E4100}",
"DetectionName": "DisplayVersion",
"DetectionValue": "25.001.20531"
},
{
"_comment": "WJF Defect Tracker. Replaces the old ClickOnce deployment. MSI installs to C:\\Program Files\\WJF_Defect_Tracker\\. Update workflow: drop new MSI on share, bump DetectionValue to new ProductVersion, next logon upgrades. ProductCode changes per version so msiexec /i handles the upgrade.",
"Name": "WJF Defect Tracker",
"Installer": "WJF_Defect_Tracker.msi",
"Type": "MSI",
"InstallArgs": "/qn /norestart ALLUSERS=1 REBOOT=ReallySuppress TARGETDIR=\"C:\\Program Files\\WJF_Defect_Tracker\"",
"DetectionMethod": "FileVersion",
"DetectionPath": "C:\\Program Files\\WJF_Defect_Tracker\\Defect_Tracker.exe",
"DetectionValue": "1.0.0.102"
}
]
}

View File

@@ -26,7 +26,7 @@
"common": {
"_comment": "Cross-PC-type share paths used by logon enforcers (Acrobat-Enforce, future analogues). One SFLD share path per app; enforcer mounts the share with SFLD creds from HKLM:\\SOFTWARE\\GE\\SFLD\\Credentials and applies acrobat-manifest.json etc.",
"acrobatSharePath": "\\\\tsgwp00525.wjs.geaerospace.net\\shared\\dt\\shopfloor\\common\\acrobat"
"commonAppsSharePath": "\\\\tsgwp00525.wjs.geaerospace.net\\shared\\dt\\shopfloor\\common\\apps"
},
"startupItems": [
@@ -51,7 +51,7 @@
{ "name": "eDNC", "kind": "exe", "exePath": "C:\\Program Files (x86)\\Dnc\\bin\\DncMain.exe" },
{ "name": "NTLARS", "kind": "exe", "exePath": "C:\\Program Files (x86)\\Dnc\\Common\\NTLARS.exe" },
{ "name": "WJ Shopfloor", "kind": "existing", "sourceName": "WJ Shopfloor.lnk" },
{ "name": "Defect_Tracker", "kind": "existing", "sourceName": "Defect_Tracker.lnk" }
{ "name": "Defect_Tracker", "kind": "exe", "exePath": "C:\\Program Files\\WJF_Defect_Tracker\\Defect_Tracker.exe" }
],
"pcProfiles": {
@@ -67,7 +67,7 @@
],
"desktopApps": [
{ "name": "WJ Shopfloor", "kind": "existing", "sourceName": "WJ Shopfloor.lnk" },
{ "name": "Defect_Tracker", "kind": "existing", "sourceName": "Defect_Tracker.lnk" }
{ "name": "Defect_Tracker", "kind": "exe", "exePath": "C:\\Program Files\\WJF_Defect_Tracker\\Defect_Tracker.exe" }
],
"edgeHomepage": "http://tsgwp00524.logon.ds.ge.com/",
"edgeStartupTabs": [
@@ -99,7 +99,7 @@
{ "name": "eDNC", "kind": "exe", "exePath": "C:\\Program Files (x86)\\Dnc\\bin\\DncMain.exe" },
{ "name": "NTLARS", "kind": "exe", "exePath": "C:\\Program Files (x86)\\Dnc\\Common\\NTLARS.exe" },
{ "name": "WJ Shopfloor", "kind": "existing", "sourceName": "WJ Shopfloor.lnk" },
{ "name": "Defect_Tracker", "kind": "existing", "sourceName": "Defect_Tracker.lnk" }
{ "name": "Defect_Tracker", "kind": "exe", "exePath": "C:\\Program Files\\WJF_Defect_Tracker\\Defect_Tracker.exe" }
]
},