Files
pxe-server/playbook/shopfloor-setup/gea-shopfloor-collections/Diagnose-eDNCDetection.ps1
2026-07-23 09:21:49 -04:00

233 lines
9.9 KiB
PowerShell

<#
Diagnose-eDNCDetection.ps1
One-shot diagnosis of the eDNC 6.4.5 enforce loop: GE-Enforce re-runs
eDNC_6-4-5.msi every 5 minutes, the MSI aborts 1603 with "The same or newer
version of this software is already installed", and InUseCheck kills DncMain
in the process. Root cause is the manifest entry's Detection* fields not
matching what is actually installed.
Runs the whole triage and prints a verdict:
1. Installed state - DncMain.exe / NTLARS.exe file versions, x86+x64
uninstall registry entries for eDNC (DisplayVersion,
ProductCode).
2. Manifest entry - the live "eDNC (bundles NTLARS)" entry from the SFLD
share manifest (uses W: if enforce has it mounted,
else direct UNC, else -ManifestPath).
3. Comparison - replays Install-FromManifest's Test-AppInstalled
logic against the entry and says WHY it misses.
4. Verdict - suggested corrected manifest JSON.
Read-only: no installs, no share writes. Run as administrator on the
problem PC (registry + share read).
Params:
-ManifestPath explicit path to the collections manifest.json (skips
W:/UNC auto-discovery)
#>
param(
[string]$ManifestPath
)
$ErrorActionPreference = 'Continue'
function Section($t){ Write-Host ''; Write-Host ("==== {0} ====" -f $t) -ForegroundColor Cyan }
function KV($k,$v){ Write-Host (" {0,-24}: {1}" -f $k, $v) }
Write-Host '########################################################'
Write-Host '# eDNC Detection Loop Diagnosis'
Write-Host ("# {0} host {1}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), [System.Environment]::MachineName)
Write-Host '########################################################'
# ---------------------------------------------------------------------------
# 1. Installed state
# ---------------------------------------------------------------------------
Section '1. Installed eDNC state on this PC'
$dncMain = 'C:\Program Files (x86)\Dnc\bin\DncMain.exe'
$ntlars = 'C:\Program Files (x86)\Dnc\Common\NTLARS.exe'
$dncMainVer = ''
$ntlarsVer = ''
if (Test-Path $dncMain) {
$dncMainVer = (Get-Item $dncMain).VersionInfo.FileVersion
KV 'DncMain.exe' ("present, FileVersion {0}" -f $dncMainVer)
} else {
KV 'DncMain.exe' 'MISSING'
}
if (Test-Path $ntlars) {
$ntlarsVer = (Get-Item $ntlars).VersionInfo.FileVersion
KV 'NTLARS.exe' ("present, FileVersion {0}" -f $ntlarsVer)
} else {
KV 'NTLARS.exe' 'MISSING'
}
# Uninstall entries - both registry views. Win32_Product deliberately avoided
# (it triggers MSI self-repair on enumeration).
$uninstallRoots = @(
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
$edncProducts = @()
foreach ($root in $uninstallRoots) {
$edncProducts += Get-ItemProperty -Path $root -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -like '*eDNC*' -or $_.DisplayName -like '*DNC*Client*' }
}
if ($edncProducts) {
foreach ($p in $edncProducts) {
Write-Host ''
KV 'DisplayName' $p.DisplayName
KV 'DisplayVersion' $p.DisplayVersion
KV 'ProductCode' $p.PSChildName
KV 'InstallDate' $p.InstallDate
KV 'RegistryView' ($(if ($p.PSPath -like '*WOW6432Node*') { 'x86 (WOW6432Node)' } else { 'x64' }))
}
} else {
Write-Host ' No eDNC uninstall entry found in either registry view.' -ForegroundColor Yellow
}
# ---------------------------------------------------------------------------
# 2. Manifest entry from the share
# ---------------------------------------------------------------------------
Section '2. Manifest entry (share)'
$candidates = @()
if ($ManifestPath) { $candidates += $ManifestPath }
$candidates += 'W:\gea-shopfloor-collections\manifest.json'
$candidates += '\\tsgwp00525.wjs.geaerospace.net\shared\dt\shopfloor\gea-shopfloor-collections\manifest.json'
$manifestFile = $null
foreach ($c in $candidates) {
if (Test-Path $c) { $manifestFile = $c; break }
}
$entry = $null
if ($manifestFile) {
KV 'Manifest' $manifestFile
try {
$manifest = Get-Content -LiteralPath $manifestFile -Raw | ConvertFrom-Json
$entry = $manifest.Applications | Where-Object { $_.Name -like 'eDNC*' } | Select-Object -First 1
} catch {
Write-Host (" Failed to parse manifest: {0}" -f $_) -ForegroundColor Red
}
if ($entry) {
KV 'Name' $entry.Name
KV 'Installer' $entry.Installer
KV 'Type' $entry.Type
KV 'InstallArgs' $entry.InstallArgs
KV 'DetectionMethod' $entry.DetectionMethod
KV 'DetectionPath' $entry.DetectionPath
KV 'DetectionName' $entry.DetectionName
KV 'DetectionValue' $entry.DetectionValue
} else {
Write-Host ' No eDNC* entry found in manifest Applications.' -ForegroundColor Yellow
}
} else {
Write-Host ' Manifest not reachable (no W: mount, UNC denied).' -ForegroundColor Yellow
Write-Host ' Re-run with -ManifestPath, or run while GE-Enforce has W: mounted.'
}
# ---------------------------------------------------------------------------
# 3. Replay Test-AppInstalled against the entry
# ---------------------------------------------------------------------------
Section '3. Detection replay'
$detected = $null
$missReason = ''
if ($entry) {
switch ($entry.DetectionMethod) {
'File' {
$detected = Test-Path $entry.DetectionPath
if (-not $detected) { $missReason = "DetectionPath does not exist: $($entry.DetectionPath)" }
}
'FileVersion' {
if (-not (Test-Path $entry.DetectionPath)) {
$detected = $false
$missReason = "DetectionPath does not exist: $($entry.DetectionPath)"
} elseif (-not $entry.DetectionValue) {
$detected = $false
$missReason = 'FileVersion detection with no DetectionValue always misses'
} else {
$actual = (Get-Item $entry.DetectionPath -ErrorAction SilentlyContinue).VersionInfo.FileVersion
$detected = ($actual -eq $entry.DetectionValue)
if (-not $detected) {
$missReason = "exact-match compare failed: actual '$actual' vs DetectionValue '$($entry.DetectionValue)'"
}
}
}
'Registry' {
if (-not (Test-Path $entry.DetectionPath)) {
$detected = $false
$missReason = "registry key does not exist: $($entry.DetectionPath) (eDNC uninstall key path varies across 6.x releases)"
} elseif ($entry.DetectionName) {
$v = Get-ItemProperty -Path $entry.DetectionPath -Name $entry.DetectionName -ErrorAction SilentlyContinue
if (-not $v) {
$detected = $false
$missReason = "value '$($entry.DetectionName)' missing under $($entry.DetectionPath)"
} elseif ($entry.DetectionValue) {
$actual = $v.$($entry.DetectionName)
$detected = ("$actual" -eq "$($entry.DetectionValue)")
if (-not $detected) {
$missReason = "exact-match compare failed: actual '$actual' vs DetectionValue '$($entry.DetectionValue)'"
}
} else {
$detected = $true
}
} else {
$detected = $true
}
}
default {
$missReason = "detection method '$($entry.DetectionMethod)' not replayed by this script"
}
}
if ($null -ne $detected) {
KV 'Test-AppInstalled' ($(if ($detected) { 'TRUE (would skip install)' } else { 'FALSE (re-runs MSI every cycle)' }))
if ($missReason) { KV 'Miss reason' $missReason }
}
} else {
Write-Host ' Skipped - no manifest entry available to replay.'
}
# ---------------------------------------------------------------------------
# 4. Verdict
# ---------------------------------------------------------------------------
Section '4. Verdict'
$edncInstalled = ($edncProducts.Count -gt 0) -or (Test-Path $dncMain)
if (-not $edncInstalled) {
Write-Host ' eDNC does NOT appear installed. The 1603 "same or newer" abort'
Write-Host ' then points at a stale MSI registration; check the upgrade-code'
Write-Host ' products under HKLM:\SOFTWARE\Classes\Installer\UpgradeCodes.'
} elseif ($detected -eq $true) {
Write-Host ' Detection passes now. If enforce still loops, the manifest on the'
Write-Host ' share differs from the one inspected here - confirm the path.'
} else {
Write-Host ' eDNC IS installed but the manifest entry does not detect it, so'
Write-Host ' GE-Enforce re-runs the MSI every cycle and the MSI aborts 1603'
Write-Host ' ("same or newer already installed"). Fix the manifest entry on'
Write-Host ' the share. Suggested replacement (File detection on NTLARS.exe,'
Write-Host ' same approach as the working 6.4.3 machineapps entry):'
Write-Host ''
$suggested = [ordered]@{
Name = 'eDNC (bundles NTLARS)'
Installer = 'apps\eDNC_6-4-5.msi'
Type = 'MSI'
InstallArgs = '/qn /norestart ALLUSERS=1 REBOOT=ReallySuppress SITESELECTED="West Jefferson"'
DetectionMethod = 'File'
DetectionPath = 'C:\Program Files (x86)\Dnc\Common\NTLARS.exe'
} | ConvertTo-Json
Write-Host $suggested
Write-Host ''
if ($edncProducts) {
$dv = ($edncProducts | Select-Object -First 1).DisplayVersion
Write-Host (" Alternative: keep Registry detection but set DetectionValue to the")
Write-Host (" actual installed DisplayVersion: '{0}'" -f $dv)
}
Write-Host ' Until the manifest is fixed, every enforce cycle also risks killing'
Write-Host ' a live DncMain session (InUseCheck force-kill before the doomed MSI).'
}
Write-Host ''