From b240cfea12fa032de3bd8952ffbfcd2586484494 Mon Sep 17 00:00:00 2001 From: cproudlock Date: Thu, 23 Jul 2026 09:21:49 -0400 Subject: [PATCH] collections: add eDNC detection diagnostic scripts --- .../Diagnose-eDNCDetection.bat | 35 +++ .../Diagnose-eDNCDetection.ps1 | 232 ++++++++++++++++++ 2 files changed, 267 insertions(+) create mode 100644 playbook/shopfloor-setup/gea-shopfloor-collections/Diagnose-eDNCDetection.bat create mode 100644 playbook/shopfloor-setup/gea-shopfloor-collections/Diagnose-eDNCDetection.ps1 diff --git a/playbook/shopfloor-setup/gea-shopfloor-collections/Diagnose-eDNCDetection.bat b/playbook/shopfloor-setup/gea-shopfloor-collections/Diagnose-eDNCDetection.bat new file mode 100644 index 0000000..1d306f7 --- /dev/null +++ b/playbook/shopfloor-setup/gea-shopfloor-collections/Diagnose-eDNCDetection.bat @@ -0,0 +1,35 @@ +@echo off +REM ========================================================================== +REM Diagnose-eDNCDetection.bat - diagnose the eDNC 6.4.5 enforce loop (MSI +REM 1603 "same or newer already installed" every 5 minutes). +REM +REM Usage (run on the problem PC): +REM Diagnose-eDNCDetection.bat auto-find manifest (W: or UNC) +REM Diagnose-eDNCDetection.bat /manifest explicit manifest.json path +REM +REM Read-only: no installs, no share writes. +REM ========================================================================== +setlocal EnableDelayedExpansion + +REM --- self-elevate --- +net session >nul 2>&1 +if %errorlevel% neq 0 ( + echo Requesting administrator elevation... + powershell -NoProfile -Command "Start-Process -Verb RunAs -FilePath '%~f0' -ArgumentList '%*'" + exit /b +) + +set "PS=%~dp0Diagnose-eDNCDetection.ps1" +set "ARGS=" + +:parse +if "%~1"=="" goto run +if /I "%~1"=="/manifest" set "ARGS=!ARGS! -ManifestPath '%~2'" & shift & shift & goto parse +shift +goto parse + +:run +powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%PS%" !ARGS! +echo. +pause +endlocal diff --git a/playbook/shopfloor-setup/gea-shopfloor-collections/Diagnose-eDNCDetection.ps1 b/playbook/shopfloor-setup/gea-shopfloor-collections/Diagnose-eDNCDetection.ps1 new file mode 100644 index 0000000..aa920bd --- /dev/null +++ b/playbook/shopfloor-setup/gea-shopfloor-collections/Diagnose-eDNCDetection.ps1 @@ -0,0 +1,232 @@ +<# +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 ''