<# 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 ''