<# Debug-ShopDBReporting.ps1 One-shot diagnosis of why a shopfloor PC is not updating its ShopDB entry through api.asp (action=updateCompleteAsset). Runs the whole triage and prints a verdict: 1. Preconditions - hostname, BIOS serial (api.asp requires it), eDNC MachineNo (the PC->machine relationship needs it). 2. Client log - the latest C:\Logs\Shopfloor\report-asset-*.log POST/RESPONSE. 3. Live test POST - calls api.asp updateCompleteAsset and parses the JSON. 4. Verdict - likely cause + fix. NOTE: the live POST WRITES to ShopDB (it is the real reporter action - an idempotent upsert of this PC's row, same as the scheduled reporter does). Use -NoPost to skip it and only inspect the local log + registry. Run as administrator on the problem PC. Params: -ApiUrl override the api.asp endpoint -MachineNo override the machine number sent (default: read from eDNC registry) -NoPost do not send the live test POST #> param( [string]$ApiUrl = 'https://tsgwp00525.wjs.geaerospace.net/shopdb/api.asp', [string]$MachineNo, [int]$TimeoutSec = 30, [switch]$NoPost ) $ErrorActionPreference = 'Continue' function Section($t){ Write-Host ''; Write-Host ("==== {0} ====" -f $t) -ForegroundColor Cyan } function KV($k,$v){ Write-Host (" {0,-20}: {1}" -f $k, $v) } Write-Host '########################################################' Write-Host '# ShopDB Reporting Debug' Write-Host ("# {0}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss')) Write-Host '########################################################' # --------------------------------------------------------------------------- # 1. Preconditions # --------------------------------------------------------------------------- Section '1. Preconditions' $hostname = $env:COMPUTERNAME $serial = '' try { $serial = (Get-CimInstance Win32_BIOS -ErrorAction Stop).SerialNumber } catch { try { $serial = (Get-WmiObject Win32_BIOS -ErrorAction Stop).SerialNumber } catch {} } $serial = ("" + $serial).Trim() # Resolve machine number the same way the reporter / GE-Enforce lib do: # eDNC registry (WOW6432Node then native) first, then C:\Enrollment\machine-number.txt. $regMachineNo = '' foreach ($rp in @('HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\General', 'HKLM:\SOFTWARE\GE Aircraft Engines\DNC\General')) { if ($regMachineNo) { break } if (Test-Path $rp) { try { $regMachineNo = ("" + (Get-ItemProperty -Path $rp -Name MachineNo -ErrorAction Stop).MachineNo).Trim() } catch {} } } $txtMachineNo = '' $mnFile = 'C:\Enrollment\machine-number.txt' if (Test-Path -LiteralPath $mnFile) { try { $txtMachineNo = ("" + (Get-Content -LiteralPath $mnFile -First 1 -ErrorAction Stop)).Trim() } catch {} } $machineNoSource = '' if (-not $MachineNo) { if ($regMachineNo) { $MachineNo = $regMachineNo; $machineNoSource = 'eDNC registry' } elseif ($txtMachineNo) { $MachineNo = $txtMachineNo; $machineNoSource = 'machine-number.txt (fallback)' } } else { $machineNoSource = 'override param' } $corpIp = '' try { $corpIp = (Get-NetIPAddress -AddressFamily IPv4 -ErrorAction Stop | Where-Object { $_.IPAddress -notmatch '^(127\.|169\.254\.)' } | Select-Object -First 1).IPAddress } catch {} KV 'Hostname' $hostname KV 'BIOS serial' ($(if ($serial) { $serial } else { '(MISSING - api.asp will reject)' })) KV 'eDNC MachineNo (reg)' ($(if ($regMachineNo) { $regMachineNo } else { '(none)' })) KV 'machine-number.txt' ($(if ($txtMachineNo) { $txtMachineNo } else { '(none)' })) KV 'MachineNo to send' ($(if ($MachineNo) { "$MachineNo [from $machineNoSource]" } else { '(none - relationship will be skipped)' })) KV 'Corp IPv4' ($(if ($corpIp) { $corpIp } else { '(none found)' })) KV 'API endpoint' $ApiUrl # --------------------------------------------------------------------------- # 2. Latest client reporter log # --------------------------------------------------------------------------- Section '2. Latest client reporter log' $logDir = 'C:\Logs\Shopfloor' $log = $null if (Test-Path $logDir) { $log = Get-ChildItem -Path $logDir -Filter 'report-asset-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime | Select-Object -Last 1 } if ($log) { KV 'Log file' $log.FullName KV 'Last written' $log.LastWriteTime Write-Host ' --- last POST / RESPONSE / ERROR lines ---' Get-Content $log.FullName -ErrorAction SilentlyContinue | Select-String -Pattern 'POST |RESPONSE |ERROR ' | Select-Object -Last 6 | ForEach-Object { Write-Host (" " + $_.Line) } } else { Write-Host ' No report-asset-*.log found. The reporter may never have run on this PC.' Write-Host ' -> check the scheduled task / GE-Enforce entry that invokes Report-AssetToShopDB.ps1' } # --------------------------------------------------------------------------- # 3. Live test POST # --------------------------------------------------------------------------- Section '3. Live test POST to api.asp' $resp = $null; $postErr = $null; $rawText = $null if ($NoPost) { Write-Host ' -NoPost set: skipping the live POST.' } elseif (-not $serial) { Write-Host ' SKIPPED: no BIOS serial, api.asp requires serialNumber. Fix the hardware/WMI read first.' } else { # Accept the internal IIS certificate for this run (PS 5.1-safe). try { if (-not ([System.Management.Automation.PSTypeName]'TrustAllCerts').Type) { Add-Type @" using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCerts : ICertificatePolicy { public bool CheckValidationResult(ServicePoint sp, X509Certificate c, WebRequest r, int p) { return true; } } "@ } [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCerts [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 } catch {} $body = @{ action = 'updateCompleteAsset' hostname = $hostname serialNumber = $serial pcType = 'Shopfloor' } if ($MachineNo) { $body['machineNo'] = $MachineNo } Write-Host (" POST host={0} serial={1} machineNo={2}" -f $hostname, $serial, $(if ($MachineNo) { $MachineNo } else { '(none)' })) try { $resp = Invoke-RestMethod -Uri $ApiUrl -Method Post -Body $body -TimeoutSec $TimeoutSec -ErrorAction Stop Write-Host ' --- response JSON ---' Write-Host (" " + ($resp | ConvertTo-Json -Compress -Depth 5)) } catch { $postErr = $_.Exception.Message Write-Host (" POST FAILED: {0}" -f $postErr) -ForegroundColor Red try { $rawText = $_.Exception.Response } catch {} } } # --------------------------------------------------------------------------- # 4. Verdict # --------------------------------------------------------------------------- Section '4. VERDICT' function Truthy($v){ return ($v -eq $true -or "$v" -eq 'True' -or "$v" -eq '1') } if ($NoPost) { Write-Host ' (live POST skipped) Inspect section 1-2 above.' if (-not $MachineNo) { Write-Host ' NOTE: no machine number (eDNC registry and machine-number.txt both empty) -> no relationship until set.' -ForegroundColor Yellow } } elseif ($postErr) { Write-Host ' CANNOT REACH api.asp - network, TLS, DNS, or IIS error.' -ForegroundColor Red Write-Host (" Detail: {0}" -f $postErr) Write-Host ' -> ping/curl the host, confirm IIS is up, check the cert and the /shopdb/ path.' } elseif (-not $serial) { Write-Host ' BIOS SERIAL MISSING - api.asp rejects the POST (serialNumber required).' -ForegroundColor Red Write-Host ' -> fix WMI/Win32_BIOS on this PC; it is a hardware/OS read issue, not ShopDB.' } elseif (-not (Truthy $resp.success)) { Write-Host ' SERVER RETURNED success=false - it aborted at a DB step.' -ForegroundColor Red Write-Host (" Breadcrumb/message: {0}" -f $resp.message) Write-Host ' -> the LAST "N-OK," token in that message is where it stopped. Match N to api.asp.' } elseif (-not (Truthy $resp.relationshipCreated)) { if (-not $MachineNo) { Write-Host ' ASSET OK, but NO RELATIONSHIP because this PC has no machine number.' -ForegroundColor Yellow Write-Host (" machineid={0}. Neither the eDNC registry nor C:\Enrollment\machine-number.txt" -f $resp.machineid) Write-Host ' has a value, so api.asp skips the relationship by design.' Write-Host ' -> set the bay machine number (Set-MachineNumber writes the eDNC registry), then' Write-Host ' re-run. CMM/keyence/wax-trace bays have no DNC number and register asset-only.' } else { Write-Host ' ASSET OK, but RELATIONSHIP NOT CREATED even though MachineNo was sent.' -ForegroundColor Red Write-Host (" machineid={0}, machineNo={1}." -f $resp.machineid, $MachineNo) Write-Host ' -> this is the server-side silent abort (LogToFile Err-leak). Deploy the api.asp fix' Write-Host ' (commit a4051e3) to prod IIS, then re-run. Also confirm the IIS logs dir is' Write-Host ' writable by the app-pool identity (that is the trigger).' } } else { Write-Host ' HEALTHY.' -ForegroundColor Green Write-Host (" Asset updated (machineid={0}) and PC->machine relationship created." -f $resp.machineid) Write-Host ' If ShopDB still looks wrong, the issue is data/display, not the reporter path.' } Write-Host '' Write-Host 'Done.'