CMM: wire per-bay settings restore into the imaging flow

Restore-CMM.ps1 (new) restores a CMM's PC-DMIS + goCMM settings at imaging from
its staged backup. Self-gating: reads C:\Enrollment\cmm\{cmmid,version,doda,
partgroup}.txt, skips DODA bays and bays with no staged backup, and restores
ONLY the config-version PC-DMIS zip via the existing Install-*Settings scripts.
Same-bay restore (cmmid match) so the backed-up controller CommPort is this
bay's own value - no cross-bay clobber.

Version selection matches the VERSION FIELD of the zip name, anchored on the
trailing timestamp, so version=2026 does not false-match a 2019/2016 zip whose
backup timestamp (20260612...) merely contains "2026".

09-Setup-CMM.ps1: new Step 2.8 calls Restore-CMM after app install + first-run
init (so a restored config is not clobbered by PC-DMIS defaults) and before the
C:\CMM-Install cleanup (the backup set lives under <stagingRoot>\backups\<cmmid>).
Best-effort: Restore-CMM always exits 0, imaging never fails on a restore.

startnet.cmd: stage ONLY the picked bay's backup into C:\CMM-Install\backups\
%CMMID% (the bulk robocopy now /XD-excludes the backups tree, which holds every
bay's backup - some 240 MB each - to avoid copying GBs to every imaged CMM).
Also bump the PPKG to v4.16 (the live boot.wim was already v4.16; the repo had
drifted to v4.14).

sync-cmm-backups.sh: source the backups from pxe-images/cmm/backups (where
Backup-CMM writes via the pulled-down copies), not the old cmm-bk path.

Smoke tested on the win11 VM against CMM3's real backup: version=2019 restored
the 2019 R2 zip (not 2016.0), imported HKLM+HKCU reg, converted the part-group
S:\ path to the tsgwp00525 UNC, created C:\geaofi, exit 0; version=2026 correctly
found no matching zip (anchor works).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-06-12 15:34:10 -04:00
parent 59deaea714
commit c2538a05c5
4 changed files with 145 additions and 5 deletions

View File

@@ -321,6 +321,28 @@ if (Test-Path $goCmmKey) {
}
}
# ============================================================================
# Step 2.8: Restore this bay's PC-DMIS + goCMM settings from its backup
# ============================================================================
# Runs AFTER app install + first-run-init (so a restored config is not clobbered
# by PC-DMIS's first-launch defaults) and BEFORE the Step 3 cleanup (the backup
# set lives under $stagingRoot\backups\<cmmid>, deleted by cleanup). Restore-CMM
# self-gates: it skips DODA bays and bays with no staged backup, and restores
# ONLY the config-version PC-DMIS zip. Same-bay restore -> no CommPort clobber.
# Best-effort: Restore-CMM always exits 0, so imaging never fails on a restore.
$restoreScript = Join-Path $PSScriptRoot 'scripts\Restore-CMM.ps1'
if (Test-Path -LiteralPath $restoreScript) {
Write-CMMLog "Running per-bay settings restore (Restore-CMM.ps1)"
try {
& $restoreScript -BackupRoot (Join-Path $stagingRoot 'backups') *>&1 | ForEach-Object { Write-CMMLog " $_" }
Write-CMMLog "Restore-CMM returned $LASTEXITCODE"
} catch {
Write-CMMLog "Restore-CMM threw (non-fatal): $_" 'WARN'
}
} else {
Write-CMMLog "Restore-CMM.ps1 not found at $restoreScript - skipping settings restore" 'WARN'
}
# ============================================================================
# Step 3: Conditional cleanup of the bootstrap staging dir
# ============================================================================

View File

@@ -0,0 +1,107 @@
<#
Restore-CMM.ps1
Imaging-time per-bay settings restore. Called by 09-Setup-CMM after app install,
before the C:\CMM-Install cleanup (it reads the staged backup from there).
Reads the resolved bay identity from C:\Enrollment\cmm\ (written by
resolve-cmm-bay-config.ps1 at the WinPE picker):
cmmid.txt - which staged backup set to use (e.g. CMM3)
version.txt - which PC-DMIS version to restore. A bay's backup holds every
version it ever had (2016.0 + 2019 R2); we restore ONLY the
one the bay-config pins, matched by substring (2019 -> the
'2019 R2' zip, 2016 -> the '2016.0' zip).
doda.txt - 'no' to proceed; anything else SKIPS restore (DODA bays are
handled separately and must not get a settings overlay).
partgroup.txt - optional goCMM Selected Part Group (friendly S:\ form), used
as the authoritative per-bay override even if the backup is stale.
Finds the backup set at <BackupRoot>\<cmmid>\ (default C:\CMM-Install\backups,
robocopied there from the PXE share by startnet) and restores via the sibling
Install-PCDMISSettings.ps1 + Install-goCMMSettings.ps1.
SAME-BAY restore by design (cmmid match), so the backed-up controller CommPort
is this bay's own value - no cross-bay CommPort clobber. (The clobber problem is
only when one bay's settings are pushed onto a different bay.)
Best-effort: always exits 0. Imaging must never fail because a restore step
could not find a backup or hit a non-fatal error - those are logged.
#>
param(
[string]$EnrollDir = 'C:\Enrollment\cmm',
[string]$BackupRoot = 'C:\CMM-Install\backups',
[string]$TargetUser = 'ShopFloor',
[string]$PartGroupShareRoot = '\\tsgwp00525.wjs.geaerospace.net\SHARED',
[string]$OutDir = 'C:\Logs\CMM'
)
$ErrorActionPreference = 'Continue'
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
New-Item -ItemType Directory -Path $OutDir -Force -EA SilentlyContinue | Out-Null
$ts = Get-Date -Format 'yyyyMMdd-HHmmss'
$log = Join-Path $OutDir "restore-cmm-$ts.log"
function Log($m){ $line = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $m"; Write-Host $line; $line | Out-File -FilePath $log -Append -Encoding ascii }
function ReadTxt($n){ $p = Join-Path $EnrollDir $n; if (Test-Path -LiteralPath $p) { (Get-Content -LiteralPath $p -First 1 -EA 0).Trim() } else { '' } }
Log "==== CMM settings restore on $env:COMPUTERNAME ===="
$cmmid = ReadTxt 'cmmid.txt'
$ver = ReadTxt 'version.txt'
$doda = (ReadTxt 'doda.txt').ToLower()
$pgRaw = ReadTxt 'partgroup.txt'
if (-not $cmmid) { Log "no cmmid.txt (manual CMM id, or not a bay-config bay) - nothing to restore. Skipping."; exit 0 }
if ($doda -eq 'yes') { Log "DODA bay ($cmmid) - skipping settings restore by policy."; exit 0 }
$bdir = Join-Path $BackupRoot $cmmid
if (-not (Test-Path -LiteralPath $bdir)) { Log "no staged backup at $bdir - skipping (stage it with sync-cmm-backups.sh)."; exit 0 }
Log "bay=$cmmid version=$ver doda=$doda backupDir=$bdir"
# --- PC-DMIS: restore ONLY the config-version zip ---
# A bay's folder holds every version it ever had (e.g. 2016.0 + 2019 R2). Match
# the VERSION FIELD of the name, not a loose substring: filenames are
# pcdmis_backup_<PC>_<VER>_<YYYYMMDD-HHMMSS>.zip
# A loose "*2026*" would wrongly match a 2019 zip whose TIMESTAMP starts 2026
# (e.g. 20260612). Anchor on the trailing timestamp so only the real version
# field (2019 -> '2019 R2', 2016 -> '2016.0', 2026 -> '2026.1') matches.
$pcZip = $null
if ($ver) {
$verRx = '^pcdmis_backup_.+_' + [regex]::Escape($ver) + '[^_]*_\d{8}-\d{6}\.zip$'
$pcZip = Get-ChildItem -LiteralPath $bdir -Filter 'pcdmis_backup_*.zip' -File -EA 0 |
Where-Object { $_.Name -match $verRx } | Sort-Object LastWriteTime | Select-Object -Last 1
}
$pcScript = Join-Path $here 'Install-PCDMISSettings.ps1'
if (-not $pcZip) {
Log "WARN: no PC-DMIS backup zip matching version '$ver' in $bdir - PC-DMIS settings NOT restored."
} elseif (-not (Test-Path -LiteralPath $pcScript)) {
Log "WARN: Install-PCDMISSettings.ps1 not found at $pcScript - PC-DMIS settings NOT restored."
} else {
Log "restoring PC-DMIS $ver from $($pcZip.Name) (TargetUser=$TargetUser)"
try { & $pcScript -BackupPath $pcZip.FullName -TargetUser $TargetUser *>&1 | ForEach-Object { Log " $_" } }
catch { Log " ERROR in Install-PCDMISSettings: $($_.Exception.Message)" }
}
# --- goCMM: restore the bay's settings; force the authoritative part group ---
$goZip = Get-ChildItem -LiteralPath $bdir -Filter 'gocmm_backup_*.zip' -File -EA 0 | Sort-Object LastWriteTime | Select-Object -Last 1
$goScript = Join-Path $here 'Install-goCMMSettings.ps1'
if (-not $goZip) {
Log "WARN: no goCMM backup zip in $bdir - goCMM settings NOT restored."
} elseif (-not (Test-Path -LiteralPath $goScript)) {
Log "WARN: Install-goCMMSettings.ps1 not found at $goScript - goCMM settings NOT restored."
} else {
# partgroup.txt is the friendly S:\... form; convert the S: prefix to the UNC
# share root the same way 09-Setup-CMM does, and pass it so the restored
# Selected Part Group matches the CURRENT bay-config even if the backup is stale.
$goArgs = @{ BackupPath = $goZip.FullName }
if ($pgRaw) {
$pg = $pgRaw -replace '(?i)^S:\\', "$PartGroupShareRoot\"
$goArgs.SelectedPartGroup = $pg
Log "restoring goCMM from $($goZip.Name) (SelectedPartGroup=$pg)"
} else {
Log "restoring goCMM from $($goZip.Name) (no partgroup override - using backup's own value)"
}
try { & $goScript @goArgs *>&1 | ForEach-Object { Log " $_" } }
catch { Log " ERROR in Install-goCMMSettings: $($_.Exception.Message)" }
}
Log "==== restore complete for $cmmid ===="
exit 0