shopfloor: CMM PC-DMIS version gate, ShopDB reporter fixes, staging self-heal
- lib Install-FromManifest 2.5->2.6: add _CmmVersion per-entry filter (reads C:\Enrollment\cmm\version.txt). Lifted the version gate out of 09-Setup-CMM into the shared lib so imaging and GE-Enforce apply it identically and cannot drift (root cause of PC-DMIS 2016 installing on every CMM). - Install-goCMMSettings: canonicalize the part-group share host to the FQDN in both the registry and ApplicationSettings.xml. Handles bare \\tsgwp00525\ and the legacy rd.ds.ge.com domain; idempotent. VM-tested. - Report-AssetToShopDB: resolve the machine number eDNC registry first, then fall back to C:\Enrollment\machine-number.txt (matches the lib resolution order) so a freshly imaged PC still reports its number for the PC-machine relationship. - Add Update-CMMEnforcer.ps1/.bat: update one CMM's local lib to the gated version and self-heal its PC-DMIS version. - Add Debug-ShopDBReporting.ps1/.bat: one-shot reporter triage (preconditions, client log, live test POST, verdict). - Add Verify-And-Heal-Staging.ps1/.bat: post-boot check that every imaging payload arrived and re-pull anything missing from the share, including the CMM bundle and the selected bay's backup (the payload that times out in WinPE). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
27
playbook/shopfloor-setup/Verify-And-Heal-Staging.bat
Normal file
27
playbook/shopfloor-setup/Verify-And-Heal-Staging.bat
Normal file
@@ -0,0 +1,27 @@
|
||||
@echo off
|
||||
REM ==========================================================================
|
||||
REM Verify-And-Heal-Staging.bat - check every imaging payload arrived on this PC
|
||||
REM and re-pull whatever is missing from the enrollment share.
|
||||
REM
|
||||
REM Usage (run on the PC):
|
||||
REM Verify-And-Heal-Staging.bat verify + heal anything missing
|
||||
REM Verify-And-Heal-Staging.bat /verifyonly report only, do not pull
|
||||
REM ==========================================================================
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
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=%~dp0Verify-And-Heal-Staging.ps1"
|
||||
set "ARGS="
|
||||
if /I "%~1"=="/verifyonly" set "ARGS=-VerifyOnly"
|
||||
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%PS%" %ARGS%
|
||||
echo.
|
||||
echo Exit code: %errorlevel% (0=all present/healed, 1=still missing)
|
||||
pause
|
||||
endlocal
|
||||
155
playbook/shopfloor-setup/Verify-And-Heal-Staging.ps1
Normal file
155
playbook/shopfloor-setup/Verify-And-Heal-Staging.ps1
Normal file
@@ -0,0 +1,155 @@
|
||||
<#
|
||||
Verify-And-Heal-Staging.ps1
|
||||
|
||||
Post-boot check that every payload the imaging flow is supposed to stage onto a
|
||||
shopfloor PC actually arrived - and re-pull (heal) anything missing from the
|
||||
enrollment share. Runs in full Windows (reliable network), so it is immune to the
|
||||
WinPE samba-idle-drop that loses copies during the WIM apply.
|
||||
|
||||
Covers the generic Fetch payload (shopfloor-setup tree + preinstall bundle) AND
|
||||
the heavy per-type payload that Fetch-StagingPayload does NOT pull today: the CMM
|
||||
bundle (C:\CMM-Install) and the selected bay's backup set
|
||||
(C:\CMM-Install\backups\<cmmid>). That is the one that silently goes missing when
|
||||
WinPE staging runs out of time before reboot.
|
||||
|
||||
Designed to be:
|
||||
- run manually on a problem PC (Verify-And-Heal-Staging.bat), or
|
||||
- called from the pre-install phase before 00-PreInstall-MachineApps so a bay is
|
||||
never left under-provisioned.
|
||||
|
||||
Idempotent. Verifies first; only heals what is actually missing/empty. Heals use
|
||||
/R:3 /W:5 (resilient), not the WinPE fail-fast /R:1 /W:1.
|
||||
|
||||
Share + creds: read from C:\Enrollment\fetch-source.txt (line1=UNC, line2=user,
|
||||
line3=pass) - same file Fetch-StagingPayload uses - else the defaults below.
|
||||
|
||||
Run as administrator. Exit 0 = everything present or healed; 1 = something still
|
||||
missing after heal attempts (read the table).
|
||||
#>
|
||||
param(
|
||||
[string]$ShareUnc,
|
||||
[string]$ShareUser,
|
||||
[string]$SharePass,
|
||||
[switch]$VerifyOnly # report only, do not heal
|
||||
)
|
||||
$ErrorActionPreference = 'Continue'
|
||||
|
||||
$ts = Get-Date -Format 'yyyyMMdd-HHmmss'
|
||||
$logDir = 'C:\Logs\Fetch'
|
||||
New-Item -ItemType Directory -Path $logDir -Force -ErrorAction SilentlyContinue | Out-Null
|
||||
$log = Join-Path $logDir "verify-heal-$ts.log"
|
||||
function Log($m,$lvl='INFO'){ $line="[$(Get-Date -Format 'HH:mm:ss')] [$lvl] $m"; Write-Host $line; Add-Content -Path $log -Value $line -EA SilentlyContinue }
|
||||
|
||||
# --- share + creds (mirror Fetch-StagingPayload) ---
|
||||
$defUnc='\\172.16.9.1\enrollment'; $defUser='pxe-upload'; $defPass='pxe'
|
||||
$srcFile='C:\Enrollment\fetch-source.txt'
|
||||
if ((-not $ShareUnc) -and (Test-Path -LiteralPath $srcFile)) {
|
||||
$l=@(Get-Content -LiteralPath $srcFile -EA SilentlyContinue)
|
||||
if ($l.Count -ge 1 -and $l[0].Trim()) { $ShareUnc=$l[0].Trim() }
|
||||
if ($l.Count -ge 2 -and $l[1].Trim()) { $ShareUser=$l[1].Trim() }
|
||||
if ($l.Count -ge 3 -and $l[2].Trim()) { $SharePass=$l[2].Trim() }
|
||||
}
|
||||
if (-not $ShareUnc) { $ShareUnc=$defUnc }
|
||||
if (-not $ShareUser) { $ShareUser=$defUser }
|
||||
if (-not $SharePass) { $SharePass=$defPass }
|
||||
|
||||
# --- identity ---
|
||||
function ReadTxt($p){ if (Test-Path -LiteralPath $p) { (Get-Content -LiteralPath $p -First 1 -EA 0).Trim() } else { '' } }
|
||||
$pcType = ReadTxt 'C:\Enrollment\pc-type.txt'
|
||||
$cmmid = ReadTxt 'C:\Enrollment\cmm\cmmid.txt'
|
||||
|
||||
Log "=== Verify-And-Heal-Staging ==="
|
||||
Log "share=$ShareUnc user=$ShareUser pcType=$(if($pcType){$pcType}else{'(none)'}) cmmid=$(if($cmmid){$cmmid}else{'(none)'}) verifyOnly=$VerifyOnly"
|
||||
|
||||
# --- expected payload manifest -------------------------------------------------
|
||||
# Each: Label, Src (under share), Dst, Mode (File|Dir), Verify (path that must
|
||||
# exist to count as present), Optional (missing-and-no-source is not a failure),
|
||||
# Files (for Mode=File), Xd (robocopy /XD dirs to exclude on heal).
|
||||
$items = New-Object System.Collections.Generic.List[object]
|
||||
function Add-Item($Label,$Src,$Dst,$Mode,$Verify,$Files=$null,$Optional=$false,$Xd=$null){
|
||||
$items.Add([pscustomobject]@{Label=$Label;Src=$Src;Dst=$Dst;Mode=$Mode;Verify=$Verify;Files=$Files;Optional=$Optional;Xd=$Xd})
|
||||
}
|
||||
$ENR='C:\Enrollment'; $SFD='C:\Enrollment\shopfloor-setup'; $PIN='C:\PreInstall'
|
||||
Add-Item 'Run-ShopfloorSetup.ps1' 'shopfloor-setup' $ENR 'File' (Join-Path $ENR 'Run-ShopfloorSetup.ps1') @('Run-ShopfloorSetup.ps1')
|
||||
Add-Item 'Shopfloor baseline' 'shopfloor-setup\Shopfloor' (Join-Path $SFD 'Shopfloor') 'Dir' (Join-Path $SFD 'Shopfloor')
|
||||
Add-Item 'common' 'shopfloor-setup\common' (Join-Path $SFD 'common') 'Dir' (Join-Path $SFD 'common')
|
||||
Add-Item '_ntlars-backups' 'shopfloor-setup\_ntlars-backups' (Join-Path $SFD '_ntlars-backups') 'Dir' (Join-Path $SFD '_ntlars-backups') $null $true
|
||||
if ($pcType) {
|
||||
Add-Item "type:$pcType" "shopfloor-setup\$pcType" (Join-Path $SFD $pcType) 'Dir' (Join-Path $SFD $pcType)
|
||||
}
|
||||
Add-Item 'preinstall.json' 'pre-install' $PIN 'File' (Join-Path $PIN 'preinstall.json') @('preinstall.json')
|
||||
Add-Item 'preinstall installers' 'pre-install\installers' (Join-Path $PIN 'installers') 'Dir' (Join-Path $PIN 'installers')
|
||||
Add-Item 'udc-backups' 'pre-install\udc-backups' (Join-Path $PIN 'udc-backups') 'Dir' (Join-Path $PIN 'udc-backups') $null $true
|
||||
# --- heavy CMM payload (the gap) ---
|
||||
if ($pcType -eq 'gea-shopfloor-cmm') {
|
||||
Add-Item 'CMM bundle' 'installers-post\cmm' 'C:\CMM-Install' 'Dir' 'C:\CMM-Install\cmm-manifest.json' $null $false 'backups'
|
||||
if ($cmmid) {
|
||||
Add-Item "CMM backup ($cmmid)" "installers-post\cmm\backups\$cmmid" "C:\CMM-Install\backups\$cmmid" 'Dir' "C:\CMM-Install\backups\$cmmid" $null $true
|
||||
}
|
||||
}
|
||||
|
||||
# --- present check -------------------------------------------------------------
|
||||
function Test-Present($it){
|
||||
if (-not (Test-Path -LiteralPath $it.Verify)) { return $false }
|
||||
if ($it.Mode -eq 'Dir') {
|
||||
$n = @(Get-ChildItem -LiteralPath $it.Verify -Recurse -File -EA SilentlyContinue).Count
|
||||
return ($n -gt 0)
|
||||
}
|
||||
return $true
|
||||
}
|
||||
|
||||
# --- mount share fresh (only if we will heal) ---------------------------------
|
||||
$drive='Z:'; $mounted=$false
|
||||
function Mount-Share { & net use $drive /delete /y 2>$null | Out-Null; & net use $drive $ShareUnc /user:$ShareUser $SharePass /persistent:no 2>&1 | Out-Null; return ($LASTEXITCODE -eq 0) }
|
||||
|
||||
# --- first pass: verify --------------------------------------------------------
|
||||
$report = New-Object System.Collections.Generic.List[object]
|
||||
$missing = New-Object System.Collections.Generic.List[object]
|
||||
foreach ($it in $items) {
|
||||
if (Test-Present $it) { $report.Add([pscustomobject]@{Item=$it.Label;Status='PRESENT'}) }
|
||||
else { $missing.Add($it); $report.Add([pscustomobject]@{Item=$it.Label;Status=$(if($it.Optional){'MISSING(opt)'}else{'MISSING'})}) }
|
||||
}
|
||||
|
||||
# --- heal missing --------------------------------------------------------------
|
||||
if ($missing.Count -gt 0 -and -not $VerifyOnly) {
|
||||
Log "$($missing.Count) item(s) missing - mounting share to heal"
|
||||
for ($a=1;$a -le 5 -and -not $mounted;$a++){ if (Mount-Share){$mounted=$true;Log "Mounted $ShareUnc as $drive"} else {Log "mount attempt $a failed - 10s" 'WARN'; Start-Sleep 10} }
|
||||
if (-not $mounted) { Log "Could not mount share - cannot heal. $($missing.Count) item(s) remain missing." 'ERROR' }
|
||||
else {
|
||||
foreach ($it in $missing) {
|
||||
$src = Join-Path $drive $it.Src
|
||||
if (-not (Test-Path -LiteralPath $src)) {
|
||||
Log "[heal SKIP] $($it.Label): not on share ($src)$(if($it.Optional){' - optional'})" $(if($it.Optional){'INFO'}else{'WARN'})
|
||||
($report | Where-Object { $_.Item -eq $it.Label })[0].Status = $(if($it.Optional){'ABSENT(opt)'}else{'NO-SOURCE'})
|
||||
continue
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $it.Dst)) { New-Item -ItemType Directory -Path $it.Dst -Force | Out-Null }
|
||||
$args=@($src,$it.Dst)
|
||||
if ($it.Mode -eq 'Dir') { $args+='/E' } else { $args+=$it.Files }
|
||||
if ($it.Xd) { $args+=@('/XD',(Join-Path $src $it.Xd)) }
|
||||
$args+=@('/R:3','/W:5','/NFL','/NDL','/NP')
|
||||
Log "[heal] $($it.Label): robocopy $src -> $($it.Dst)"
|
||||
& robocopy @args | Out-Null
|
||||
$rc=$LASTEXITCODE
|
||||
Start-Sleep 1
|
||||
$ok = Test-Present $it
|
||||
($report | Where-Object { $_.Item -eq $it.Label })[0].Status = $(if($ok){'HEALED'}elseif($rc -ge 8){'HEAL-FAIL'}else{'STILL-MISSING'})
|
||||
Log "[heal $(if($ok){'OK'}else{'FAIL'})] $($it.Label) robocopy exit=$rc present=$ok"
|
||||
}
|
||||
& net use $drive /delete /y 2>$null | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
# --- report --------------------------------------------------------------------
|
||||
Log '================ STAGING VERIFY/HEAL REPORT ================'
|
||||
foreach ($r in $report) { Log (" {0,-26} {1}" -f $r.Item, $r.Status) }
|
||||
$bad = @($report | Where-Object { $_.Status -in @('MISSING','NO-SOURCE','HEAL-FAIL','STILL-MISSING') })
|
||||
if ($bad.Count -gt 0) {
|
||||
Log "RESULT: $($bad.Count) REQUIRED item(s) still missing: $(($bad|ForEach-Object{$_.Item}) -join ', ')" 'ERROR'
|
||||
Log "Log: $log"
|
||||
exit 1
|
||||
} else {
|
||||
Log 'RESULT: all required payloads present (or healed).'
|
||||
Log "Log: $log"
|
||||
exit 0
|
||||
}
|
||||
@@ -38,6 +38,12 @@ $ErrorActionPreference = 'Continue'
|
||||
# logged; manifests tagged with a newer MINOR are fine.
|
||||
#
|
||||
# Changelog:
|
||||
# 2.6 - added _CmmVersion filter. Entry tagged _CmmVersion only applies when
|
||||
# it equals C:\Enrollment\cmm\version.txt (the bay's resolved PC-DMIS
|
||||
# version, written at imaging from cmm-bay-config.csv). Untagged entries
|
||||
# always pass; missing/empty version file is a no-op (legacy install-all
|
||||
# + non-CMM scopes unaffected). Lifted out of 09-Setup-CMM so the gate
|
||||
# lives in one place both the imaging and enforce paths share.
|
||||
# 2.5 - Type=EXE handler honors optional WaitTimeoutSec on the manifest
|
||||
# entry. WiX Burn bootstrappers (UDC_Setup.exe) install the MSI
|
||||
# successfully but the wrapper process never exits (waits on a
|
||||
@@ -58,7 +64,7 @@ $ErrorActionPreference = 'Continue'
|
||||
# 2.0 - initial Stage 2a: PS1/BAT/File/Registry/INF action types,
|
||||
# Always/MarkerFile/ValueMatches/pnputil detection, PCTypes filter
|
||||
$LIB_MANIFEST_MAJOR = 2
|
||||
$LIB_MANIFEST_MINOR = 5
|
||||
$LIB_MANIFEST_MINOR = 6
|
||||
|
||||
$logDir = Split-Path -Parent $LogFile
|
||||
if (-not (Test-Path $logDir)) {
|
||||
@@ -575,6 +581,42 @@ function Test-MachineNumberMatches {
|
||||
return $false
|
||||
}
|
||||
|
||||
# CMM PC-DMIS version filter. The bay's PC-DMIS version (2016/2019/2026) is
|
||||
# resolved at imaging by resolve-cmm-bay-config.ps1 from cmm-bay-config.csv (the
|
||||
# single bay -> version map) and persisted to C:\Enrollment\cmm\version.txt. An
|
||||
# entry tagged _CmmVersion applies only when it equals that file; untagged
|
||||
# entries (CLM, goCMM, Protect Viewer, DODA, the PDF converter) always pass.
|
||||
# When the file is absent/empty - a bay imaged before the picker, or any
|
||||
# non-CMM PC running a different scope - the filter is a no-op so every tagged
|
||||
# entry passes. That preserves the legacy "install all versions" behavior for
|
||||
# pre-picker bays and leaves non-CMM scopes untouched.
|
||||
#
|
||||
# This is the SINGLE place the version gate lives. Both the imaging path
|
||||
# (09-Setup-CMM) and the runtime path (GE-Enforce) call this lib, so the gate
|
||||
# cannot apply in one path and not the other. The 2016-installed-on-a-2019-bay
|
||||
# bug was exactly that drift: the imaging path filtered by _CmmVersion but the
|
||||
# enforce path did not, so enforce reinstalled every version it did not detect.
|
||||
$script:_cachedCmmVersion = $null
|
||||
$script:_cmmVersionRead = $false
|
||||
function Get-CurrentCmmVersion {
|
||||
if ($script:_cmmVersionRead) { return $script:_cachedCmmVersion }
|
||||
$script:_cmmVersionRead = $true
|
||||
$f = 'C:\Enrollment\cmm\version.txt'
|
||||
if (Test-Path -LiteralPath $f) {
|
||||
$v = (Get-Content -LiteralPath $f -First 1 -ErrorAction SilentlyContinue)
|
||||
if ($v) { $script:_cachedCmmVersion = $v.Trim() }
|
||||
}
|
||||
return $script:_cachedCmmVersion
|
||||
}
|
||||
|
||||
function Test-CmmVersionMatches {
|
||||
param($App)
|
||||
if (-not $App._CmmVersion) { return $true } # untagged entry always applies
|
||||
$myVer = Get-CurrentCmmVersion
|
||||
if (-not $myVer) { return $true } # no resolved version -> legacy install-all
|
||||
return ([string]$App._CmmVersion -ieq $myVer)
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main loop
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -614,6 +656,13 @@ foreach ($app in $config.Applications) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (-not (Test-CmmVersionMatches -App $app)) {
|
||||
$myVer = Get-CurrentCmmVersion
|
||||
Write-InstallLog " _CmmVersion filter: entry targets $($app._CmmVersion) but bay version is $(if ($myVer) { $myVer } else { '(none)' }) - skipping"
|
||||
$pcFiltered++
|
||||
continue
|
||||
}
|
||||
|
||||
if (Test-AppInstalled -App $app) {
|
||||
Write-InstallLog ' Already installed at expected version - skipping'
|
||||
$skipped++
|
||||
|
||||
@@ -124,42 +124,13 @@ else {
|
||||
if (Test-Path 'C:\Enrollment\pc-type.txt') { $pcType = (Get-Content 'C:\Enrollment\pc-type.txt' -First 1 -EA 0).Trim() }
|
||||
if (Test-Path 'C:\Enrollment\pc-subtype.txt') { $pcSubType = (Get-Content 'C:\Enrollment\pc-subtype.txt' -First 1 -EA 0).Trim() }
|
||||
|
||||
# Read resolved PC-DMIS version from bay-config (written by
|
||||
# resolve-cmm-bay-config.ps1 via startnet.cmd). If missing, install all
|
||||
# PC-DMIS versions (legacy behavior for bays imaged before the picker).
|
||||
$cmmVersion = ''
|
||||
$cmmVersionFile = 'C:\Enrollment\cmm\version.txt'
|
||||
if (Test-Path -LiteralPath $cmmVersionFile) {
|
||||
$cmmVersion = (Get-Content -LiteralPath $cmmVersionFile -First 1 -EA 0).Trim()
|
||||
}
|
||||
Write-CMMLog "Resolved CMM version: $(if ($cmmVersion) { $cmmVersion } else { '(none - installing all)' })"
|
||||
|
||||
# Filter manifest: drop entries whose _CmmVersion doesn't match the
|
||||
# resolved version. Entries without _CmmVersion always pass (CLM, goCMM,
|
||||
# Protect Viewer, DODA). Write a temp filtered manifest for the lib.
|
||||
if ($cmmVersion) {
|
||||
try {
|
||||
$cfg = Get-Content $stagingMani -Raw | ConvertFrom-Json
|
||||
$filtered = @($cfg.Applications | Where-Object {
|
||||
if (-not $_._CmmVersion) { return $true }
|
||||
return ($_._CmmVersion -ieq $cmmVersion)
|
||||
})
|
||||
$skipped = @($cfg.Applications | Where-Object {
|
||||
$_._CmmVersion -and ($_._CmmVersion -ine $cmmVersion)
|
||||
})
|
||||
foreach ($s in $skipped) {
|
||||
Write-CMMLog " Skipping $($s.Name) (_CmmVersion=$($_._CmmVersion) != $cmmVersion)"
|
||||
}
|
||||
$cfg.Applications = $filtered
|
||||
$filteredMani = Join-Path $stagingRoot 'cmm-manifest-filtered.json'
|
||||
$cfg | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $filteredMani -Encoding UTF8
|
||||
Write-CMMLog "Filtered manifest: $($filtered.Count) entries (from $($filtered.Count + $skipped.Count))"
|
||||
$stagingMani = $filteredMani
|
||||
} catch {
|
||||
Write-CMMLog "Version filter failed: $_ - using unfiltered manifest" 'WARN'
|
||||
}
|
||||
}
|
||||
|
||||
# PC-DMIS version gating (drop entries whose _CmmVersion does not match the
|
||||
# bay's C:\Enrollment\cmm\version.txt) is now owned by the shared lib
|
||||
# Install-FromManifest.ps1 (>= 2.6). Both this imaging path and the runtime
|
||||
# GE-Enforce path call that lib, so the gate is applied identically in one
|
||||
# place and the two paths cannot drift. We pass the full manifest unfiltered
|
||||
# and let the lib filter per entry. The bug this prevents: enforce lacked
|
||||
# the gate and reinstalled the wrong PC-DMIS version on an already-imaged bay.
|
||||
Write-CMMLog "Running Install-FromManifest against $stagingRoot (PCType=$pcType, PCSubType=$pcSubType)"
|
||||
& $libSource -ManifestPath $stagingMani -InstallerRoot $stagingRoot -LogFile $logFile -PCType $pcType -PCSubType $pcSubType
|
||||
$rc = $LASTEXITCODE
|
||||
|
||||
@@ -38,6 +38,25 @@ param(
|
||||
$DefaultRewrites = @(
|
||||
@{ From = 'rd.ds.ge.com'; To = 'wjs.geaerospace.net' } # WJ legacy domain -> new domain
|
||||
)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Part-group share host canonicalization. Bays were captured with three
|
||||
# inconsistent forms of the share host in the goCMM 'Selected Part Group' /
|
||||
# ApplicationSettings.xml <PartGroup FullName>:
|
||||
# \\tsgwp00525\... (bare hostname - DNS-suffix dependent)
|
||||
# \\tsgwp00525.rd.ds.ge.com\... (legacy GE corp domain - dead on the
|
||||
# air-gapped shopfloor net)
|
||||
# \\tsgwp00525.wjs.geaerospace.net\... (correct)
|
||||
# The DefaultRewrites above only fix the middle form. goCMM DISPLAYS the part
|
||||
# group from ApplicationSettings.xml, so the bare form survived into the UI.
|
||||
# Pin every form to the FQDN in BOTH the registry and the XML. The regex
|
||||
# matches the UNC host with an optional domain suffix and is idempotent (an
|
||||
# already-correct FQDN maps to itself). Disabled by -NoDefaultRewrite.
|
||||
$PartGroupHostShort = 'tsgwp00525'
|
||||
$PartGroupHostFqdn = 'tsgwp00525.wjs.geaerospace.net'
|
||||
# \\HOST or \\HOST.any.domain (followed by the next path backslash) -> \\FQDN
|
||||
$PartGroupHostRx = '(?i)\\\\' + [regex]::Escape($PartGroupHostShort) + '(?:\.[A-Za-z0-9.\-]+)?(?=\\)'
|
||||
$PartGroupHostTo = '\\' + $PartGroupHostFqdn
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$ts = Get-Date -Format 'yyyyMMdd-HHmmss'
|
||||
$logDir = 'C:\Logs\CMM'
|
||||
@@ -148,6 +167,45 @@ if ($rewrites.Count -gt 0) {
|
||||
}
|
||||
}
|
||||
|
||||
# --- Canonicalize the part-group share host to the FQDN in reg + XML ---
|
||||
# Runs AFTER the geaofi robocopy (so ApplicationSettings.xml is in place)
|
||||
# and AFTER the literal rewrites above. Idempotent. This is what makes
|
||||
# goCMM show the FQDN regardless of which form the bay was captured with.
|
||||
if (-not $NoDefaultRewrite) {
|
||||
# registry: every string value under the goCMM key
|
||||
if (Test-Path $goCmmKey) {
|
||||
try {
|
||||
$props = Get-ItemProperty -Path $goCmmKey
|
||||
foreach ($p in $props.PSObject.Properties) {
|
||||
if ($p.Name -like 'PS*') { continue }
|
||||
if (($p.Value -is [string]) -and ([regex]::IsMatch($p.Value, $PartGroupHostRx))) {
|
||||
$new = [regex]::Replace($p.Value, $PartGroupHostRx, $PartGroupHostTo)
|
||||
if ($new -ne $p.Value) {
|
||||
Set-ItemProperty -Path $goCmmKey -Name $p.Name -Value $new
|
||||
Log " host-canon reg [$($p.Name)] -> $new"
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch { Log " WARN: host canonicalize (reg) failed: $($_.Exception.Message)" }
|
||||
}
|
||||
# files: every text file under the Shared Data Directory (incl ApplicationSettings.xml)
|
||||
if (Test-Path $sharedDir) {
|
||||
$utf8NoBomHost = New-Object System.Text.UTF8Encoding($false)
|
||||
Get-ChildItem -Path $sharedDir -Recurse -File -Include *.xml,*.txt,*.csv,*.config,*.ini,*.bas -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
try {
|
||||
$c = [System.IO.File]::ReadAllText($_.FullName)
|
||||
if ([regex]::IsMatch($c, $PartGroupHostRx)) {
|
||||
$nc = [regex]::Replace($c, $PartGroupHostRx, $PartGroupHostTo)
|
||||
if ($nc -ne $c) {
|
||||
[System.IO.File]::WriteAllText($_.FullName, $nc, $utf8NoBomHost)
|
||||
Log " host-canon file [$($_.Name)] rewritten"
|
||||
}
|
||||
}
|
||||
} catch { Log " WARN: host canonicalize $($_.FullName): $($_.Exception.Message)" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Grant BUILTIN\Users ReadKey+WriteKey on the reg key (goCMM opens it writable:true to read) ---
|
||||
if (Test-Path $goCmmKey) {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
@echo off
|
||||
REM ==========================================================================
|
||||
REM Update-CMMEnforcer.bat - update ONE CMM's local GE-Enforce lib to the
|
||||
REM version that honors the PC-DMIS _CmmVersion gate, then self-heal.
|
||||
REM
|
||||
REM Usage (run on the CMM PC):
|
||||
REM Update-CMMEnforcer.bat update lib + report wrong versions + self-heal
|
||||
REM Update-CMMEnforcer.bat /remove also uninstall the wrong PC-DMIS version(s)
|
||||
REM
|
||||
REM Pulls the lib from the tsgwp00525 share by default. To use a USB copy:
|
||||
REM Update-CMMEnforcer.bat /src "D:\Install-FromManifest.ps1"
|
||||
REM Update-CMMEnforcer.bat /remove /src "D:\Install-FromManifest.ps1"
|
||||
REM ==========================================================================
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
REM --- self-elevate to administrator ---
|
||||
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=%~dp0Update-CMMEnforcer.ps1"
|
||||
set "ARGS="
|
||||
|
||||
:parse
|
||||
if "%~1"=="" goto run
|
||||
if /I "%~1"=="/remove" set "ARGS=!ARGS! -RemoveWrongVersions" & shift & goto parse
|
||||
if /I "%~1"=="/src" set "ARGS=!ARGS! -SourceLib '%~2'" & shift & shift & goto parse
|
||||
shift
|
||||
goto parse
|
||||
|
||||
:run
|
||||
echo Running: powershell -File "%PS%" !ARGS!
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%PS%" !ARGS!
|
||||
set "RC=%errorlevel%"
|
||||
echo.
|
||||
echo Exit code: %RC%
|
||||
pause
|
||||
endlocal
|
||||
@@ -0,0 +1,131 @@
|
||||
<#
|
||||
Update-CMMEnforcer.ps1
|
||||
|
||||
Bring ONE CMM shopfloor PC's local GE-Enforce engine up to the lib version
|
||||
that understands the PC-DMIS _CmmVersion gate (lib MINOR >= 6), then run a
|
||||
single enforce cycle so it self-heals to this bay's correct PC-DMIS version.
|
||||
|
||||
Why this exists: GE-Enforce runs its lib from LOCAL
|
||||
C:\Program Files\GE\Shopfloor\lib\ and does NOT auto-update from the share.
|
||||
A bay running an older lib ignores the _CmmVersion tags in the synced CMM
|
||||
manifest and would install every version it does not detect. This script
|
||||
pushes the new lib locally so the gate takes effect.
|
||||
|
||||
Run as administrator on the CMM PC.
|
||||
|
||||
Params:
|
||||
-SourceLib Explicit path to the 2.6+ lib (e.g. a USB copy). If omitted,
|
||||
pulls from the tsgwp00525 share (-ShareRoot).
|
||||
-ShareRoot Share root holding common\lib\Install-FromManifest.ps1.
|
||||
-RemoveWrongVersions Uninstall any installed PC-DMIS version that does not
|
||||
match this bay's C:\Enrollment\cmm\version.txt.
|
||||
-NoEnforceRun Skip the post-update enforce cycle (just update + report).
|
||||
#>
|
||||
param(
|
||||
[string]$SourceLib,
|
||||
[string]$ShareRoot = '\\tsgwp00525.wjs.geaerospace.net\shared\dt\shopfloor',
|
||||
[string]$InstallRoot = 'C:\Program Files\GE\Shopfloor',
|
||||
[int]$MinLibMinor = 6,
|
||||
[switch]$RemoveWrongVersions,
|
||||
[switch]$NoEnforceRun
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
function Log($m){ Write-Host ("[Update-CMMEnforcer] {0}" -f $m) }
|
||||
|
||||
function Get-LibMinor($path){
|
||||
if (-not (Test-Path -LiteralPath $path)) { return -1 }
|
||||
$m = Select-String -Path $path -Pattern 'LIB_MANIFEST_MINOR\s*=\s*(\d+)' -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
if ($m) { return [int]$m.Matches[0].Groups[1].Value } else { return -1 }
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 1. Resolve the source lib (USB override or share) and validate its version
|
||||
# ---------------------------------------------------------------------------
|
||||
if (-not $SourceLib) { $SourceLib = Join-Path $ShareRoot 'common\lib\Install-FromManifest.ps1' }
|
||||
if (-not (Test-Path -LiteralPath $SourceLib)) {
|
||||
throw "Source lib not found: $SourceLib (if using the share, confirm you are authenticated to it; or pass -SourceLib <path to a USB copy>)"
|
||||
}
|
||||
$srcMinor = Get-LibMinor $SourceLib
|
||||
if ($srcMinor -lt $MinLibMinor) {
|
||||
throw "Source lib is MINOR=$srcMinor but >= $MinLibMinor is required. Point -SourceLib at the updated lib (or sync the share first)."
|
||||
}
|
||||
|
||||
$dstLib = Join-Path $InstallRoot 'lib\Install-FromManifest.ps1'
|
||||
$oldMinor = Get-LibMinor $dstLib
|
||||
Log "Source lib MINOR=$srcMinor ($SourceLib)"
|
||||
Log "Local lib MINOR=$oldMinor ($dstLib)"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 2. Back up + copy the lib into place, verify
|
||||
# ---------------------------------------------------------------------------
|
||||
$libDir = Split-Path -Parent $dstLib
|
||||
New-Item -ItemType Directory -Path $libDir -Force | Out-Null
|
||||
if (Test-Path -LiteralPath $dstLib) {
|
||||
$bak = "$dstLib.bak-{0}" -f (Get-Date -Format 'yyyyMMdd-HHmmss')
|
||||
Copy-Item -LiteralPath $dstLib -Destination $bak -Force
|
||||
Log "Backed up local lib -> $bak"
|
||||
}
|
||||
Copy-Item -LiteralPath $SourceLib -Destination $dstLib -Force
|
||||
$newMinor = Get-LibMinor $dstLib
|
||||
if ($newMinor -lt $MinLibMinor) { throw "Copy did not take - local lib still MINOR=$newMinor" }
|
||||
Log "Local lib updated: MINOR $oldMinor -> $newMinor (OK - gate is now active)"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 3. Report bay version + which PC-DMIS versions are installed
|
||||
# ---------------------------------------------------------------------------
|
||||
$verFile = 'C:\Enrollment\cmm\version.txt'
|
||||
$bayVer = if (Test-Path -LiteralPath $verFile) { (Get-Content -LiteralPath $verFile -First 1).Trim() } else { '' }
|
||||
Log "Bay PC-DMIS version (version.txt): $(if($bayVer){$bayVer}else{'(none - bay not resolved; gate will install-all)'})"
|
||||
|
||||
$pcd = @(
|
||||
[pscustomobject]@{ Ver='2016'; Guid='{5389B196-81F0-44A9-A073-4C1D72041F09}'; Name='PC-DMIS 2016.0' },
|
||||
[pscustomobject]@{ Ver='2019'; Guid='{49DBE7F9-228A-4E66-8BB5-DB5A446DCAE7}'; Name='PC-DMIS 2019 R2' },
|
||||
[pscustomobject]@{ Ver='2026'; Guid='{81BACE1B-FB08-4DCF-8100-79911AD3EC1E}'; Name='PC-DMIS 2026.1' }
|
||||
)
|
||||
function Test-MsiInstalled($guid){
|
||||
foreach($r in @(
|
||||
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$guid",
|
||||
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\$guid")){
|
||||
if (Test-Path -LiteralPath $r) { return $true }
|
||||
}
|
||||
return $false
|
||||
}
|
||||
$installed = @($pcd | Where-Object { Test-MsiInstalled $_.Guid } | ForEach-Object { $_.Ver })
|
||||
Log "PC-DMIS installed: $(if($installed){$installed -join ', '}else{'none'})"
|
||||
$wrong = @($installed | Where-Object { $bayVer -and $_ -ne $bayVer })
|
||||
if ($wrong.Count) { Log "WRONG for this bay (expected $bayVer): $($wrong -join ', ')" }
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 4. Optionally uninstall wrong versions
|
||||
# ---------------------------------------------------------------------------
|
||||
if ($RemoveWrongVersions -and $bayVer -and $wrong.Count) {
|
||||
foreach($p in ($pcd | Where-Object { $wrong -contains $_.Ver })){
|
||||
Log "Uninstalling $($p.Name) $($p.Guid) ..."
|
||||
$proc = Start-Process msiexec.exe -ArgumentList "/x $($p.Guid) /qn /norestart REBOOT=ReallySuppress" -Wait -PassThru -WindowStyle Hidden
|
||||
Log " msiexec exit $($proc.ExitCode) $(if($proc.ExitCode -in 0,1605,3010){'(OK)'}else{'(check)'})"
|
||||
}
|
||||
} elseif ($wrong.Count) {
|
||||
Log "Re-run with -RemoveWrongVersions to uninstall the wrong version(s)."
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 5. Kick one enforce cycle so it self-heals against the gated manifest
|
||||
# ---------------------------------------------------------------------------
|
||||
if (-not $NoEnforceRun) {
|
||||
$task = Get-ScheduledTask -ErrorAction SilentlyContinue | Where-Object { $_.TaskName -match 'Enforce' } | Select-Object -First 1
|
||||
$enf = Join-Path $InstallRoot 'GE-Enforce.ps1'
|
||||
if ($task) {
|
||||
Log "Triggering scheduled task '$($task.TaskName)' ..."
|
||||
Start-ScheduledTask -TaskName $task.TaskName -TaskPath $task.TaskPath
|
||||
Log "Triggered. Watch C:\Logs\Shopfloor\enforce-*.log"
|
||||
} elseif (Test-Path -LiteralPath $enf) {
|
||||
Log "No enforce task found - running GE-Enforce.ps1 directly ..."
|
||||
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $enf | Out-Null
|
||||
Log "Enforce cycle done. See C:\Logs\Shopfloor\enforce-*.log"
|
||||
} else {
|
||||
Log "Could not find an enforce task or $enf - trigger enforcement manually."
|
||||
}
|
||||
}
|
||||
|
||||
Log "DONE. lib MINOR=$newMinor | bay=$(if($bayVer){$bayVer}else{'?'}) | installed=$($installed -join ',')"
|
||||
Log "Verify next enforce log shows the 2016/2026 entries skipped with '_CmmVersion filter'."
|
||||
@@ -0,0 +1,37 @@
|
||||
@echo off
|
||||
REM ==========================================================================
|
||||
REM Debug-ShopDBReporting.bat - diagnose a PC not updating its ShopDB entry.
|
||||
REM
|
||||
REM Usage (run on the problem PC):
|
||||
REM Debug-ShopDBReporting.bat full triage incl a live test POST
|
||||
REM Debug-ShopDBReporting.bat /nopost inspect log + registry only (no POST)
|
||||
REM Debug-ShopDBReporting.bat /mn 2001 force the machine number sent
|
||||
REM
|
||||
REM The live POST writes this PC's row to ShopDB (idempotent upsert, same as the
|
||||
REM scheduled reporter). Use /nopost to avoid writing.
|
||||
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=%~dp0Debug-ShopDBReporting.ps1"
|
||||
set "ARGS="
|
||||
|
||||
:parse
|
||||
if "%~1"=="" goto run
|
||||
if /I "%~1"=="/nopost" set "ARGS=!ARGS! -NoPost" & shift & goto parse
|
||||
if /I "%~1"=="/mn" set "ARGS=!ARGS! -MachineNo '%~2'" & shift & shift & goto parse
|
||||
shift
|
||||
goto parse
|
||||
|
||||
:run
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%PS%" !ARGS!
|
||||
echo.
|
||||
pause
|
||||
endlocal
|
||||
@@ -0,0 +1,199 @@
|
||||
<#
|
||||
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.'
|
||||
@@ -83,16 +83,38 @@ if (-not $serialNumber) {
|
||||
exit 0
|
||||
}
|
||||
|
||||
# DNC machine number from eDNC reg (2001, 2002, ...). optional - sent only if present.
|
||||
# DNC machine number (2001, 2002, ...). optional - sent only if found.
|
||||
# Resolution order mirrors the GE-Enforce lib Get-CurrentMachineNumber so the
|
||||
# reporter and manifest gating agree:
|
||||
# 1. eDNC registry (WOW6432Node, then native) - follows bay reassignment, which
|
||||
# Set-MachineNumber rewrites here.
|
||||
# 2. C:\Enrollment\machine-number.txt - the imaging-time value written once by
|
||||
# startnet.cmd. Used when eDNC has not populated the registry yet (fresh
|
||||
# image, or a bay where eDNC has not run), so the PC still reports its number
|
||||
# and api.asp can build the relationship.
|
||||
$machineNo = ''
|
||||
$edncRegPath = 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\General'
|
||||
foreach ($regPath in @(
|
||||
'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\General',
|
||||
'HKLM:\SOFTWARE\GE Aircraft Engines\DNC\General'
|
||||
)) {
|
||||
if ($machineNo) { break }
|
||||
try {
|
||||
if (Test-Path $edncRegPath) {
|
||||
$machineNo = [string](Get-ItemProperty -Path $edncRegPath -Name MachineNo -ErrorAction Stop).MachineNo
|
||||
$machineNo = $machineNo.Trim()
|
||||
if (Test-Path $regPath) {
|
||||
$v = [string](Get-ItemProperty -Path $regPath -Name MachineNo -ErrorAction Stop).MachineNo
|
||||
if ($v) { $machineNo = $v.Trim() }
|
||||
}
|
||||
} catch {
|
||||
Log "WARN could not read eDNC MachineNo: $($_.Exception.Message)"
|
||||
Log "WARN could not read MachineNo from ${regPath}: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
if (-not $machineNo) {
|
||||
$mnFile = 'C:\Enrollment\machine-number.txt'
|
||||
if (Test-Path -LiteralPath $mnFile) {
|
||||
try {
|
||||
$v = Get-Content -LiteralPath $mnFile -First 1 -ErrorAction Stop
|
||||
if ($v) { $machineNo = ([string]$v).Trim(); Log "machineNo from $mnFile (eDNC registry empty): $machineNo" }
|
||||
} catch { Log "WARN could not read ${mnFile}: $($_.Exception.Message)" }
|
||||
}
|
||||
}
|
||||
|
||||
# OS caption for the operatingsystems lookup.
|
||||
|
||||
Reference in New Issue
Block a user