goCMM settings live in two places: 3 pointer values at HKLM\SOFTWARE\WOW6432Node\General Electric\goCMM, and the real content of all 7 Settings tabs (PC-DMIS, Quindos, Modus, Machine Definition, User Input, Notifications, Part Groups) in C:\geaofi\ApplicationSettings.xml. Capture-replay pair, mirroring the Wax/Trace Backup/Install scripts: - Backup-goCMMSettings.ps1/.bat: on a live legacy bay (admin), zips the registry key + the C:\geaofi tree (minus transient LocalProgramCopies/logs) to gocmm_backup_<PC>_<ts>.zip. - Install-goCMMSettings.ps1/.bat: restore at imaging (admin). Imports the key + lays down C:\geaofi, then grants BUILTIN\Users WriteKey on the reg key and Modify on C:\geaofi - goCMM's RegistrySettings.GetRegistryString opens the key with writable:true even to READ, so a locked-down operator throws a SecurityException without the grant (the post-lockdown 'registry access not allowed' error). Applies a built-in legacy->new FQDN rewrite (rd.ds.ge.com -> wjs.geaerospace.net) automatically across the registry values and ApplicationSettings.xml (incl PartGroup FullName); -NoDefaultRewrite skips it, /replace adds an extra pair, -SelectedPartGroup overrides per bay. - gocmm-debug.ps1/.bat: run as the operator to reproduce the SecurityException and dump the goCMM key ACL (confirms whether lockdown stripped the grant). All round-trip + FQDN-rewrite verified on the win11 VM. NOTE: covers goCMM only; PC-DMIS probe calibrations / custom tip angles / machine comp are owned by PC-DMIS (Hexagon) and not captured here. Not yet wired into 09-Setup-CMM auto-discovery. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
173 lines
8.0 KiB
PowerShell
173 lines
8.0 KiB
PowerShell
<#
|
|
Install-goCMMSettings.ps1
|
|
|
|
Restore a goCMM bay's settings from a zip produced by Backup-goCMMSettings.ps1.
|
|
Mirrors Install-FormtracepakSettings.ps1.
|
|
|
|
Lays back both halves:
|
|
1. Registry pointers -> HKLM\SOFTWARE\WOW6432Node\General Electric\goCMM
|
|
2. Shared Data Directory (C:\geaofi\, incl ApplicationSettings.xml = all 7
|
|
Settings tabs: PC-DMIS, Quindos, Modus, Machine Definition, User Input,
|
|
Notifications, Part Groups).
|
|
|
|
Then grants the access goCMM needs so it works under lockdown:
|
|
- BUILTIN\Users ReadKey+WriteKey on the goCMM reg key. goCMM's
|
|
RegistrySettings.GetRegistryString opens that key with writable:true even
|
|
to READ, so a read-only operator hits a SecurityException without this.
|
|
- BUILTIN\Users Modify on the Shared Data Directory (goCMM writes
|
|
ApplicationSettings.xml back there when settings are saved).
|
|
|
|
Run as administrator / SYSTEM (imaging context). If the lockdown pass strips
|
|
the registry ACE, re-run this (or just its ACL section) AFTER lockdown.
|
|
|
|
NOTE: this restores goCMM only. PC-DMIS probe calibrations / custom tip angles /
|
|
machine comp are owned by PC-DMIS (Hexagon) and are NOT in this backup.
|
|
#>
|
|
param(
|
|
[Parameter(Mandatory=$true)][string]$BackupPath, # zip or already-extracted dir
|
|
[string]$SelectedPartGroup, # optional per-bay override of the part-group UNC
|
|
[string]$ReplaceFrom, # optional EXTRA find/replace across reg + xml
|
|
[string]$ReplaceTo, # ... replacement. Case-insensitive.
|
|
[switch]$NoDefaultRewrite # skip the built-in legacy->new FQDN swaps below
|
|
)
|
|
|
|
# ============================================================================
|
|
# Built-in FQDN migration - applied AUTOMATICALLY on every restore (no flag).
|
|
# Add pairs here as more legacy domains retire. -NoDefaultRewrite disables them.
|
|
# ============================================================================
|
|
$DefaultRewrites = @(
|
|
@{ From = 'rd.ds.ge.com'; To = 'wjs.geaerospace.net' } # WJ legacy domain -> new domain
|
|
)
|
|
$ErrorActionPreference = 'Continue'
|
|
$ts = Get-Date -Format 'yyyyMMdd-HHmmss'
|
|
$logDir = 'C:\Logs\CMM'
|
|
New-Item -ItemType Directory -Path $logDir -Force -ErrorAction SilentlyContinue | Out-Null
|
|
$log = Join-Path $logDir "gocmm-restore-$ts.log"
|
|
function Log($m){ Write-Host $m; $m | Out-File -FilePath $log -Append }
|
|
|
|
$goCmmKey = 'HKLM:\SOFTWARE\WOW6432Node\General Electric\goCMM'
|
|
Log "==== goCMM restore on $env:COMPUTERNAME from $BackupPath ===="
|
|
|
|
# --- Resolve the backup to a directory ---
|
|
$src = $BackupPath
|
|
$extracted = $false
|
|
if ($BackupPath -match '\.zip$') {
|
|
$src = Join-Path $env:TEMP "gocmm-rs-$ts"
|
|
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
|
[System.IO.Compression.ZipFile]::ExtractToDirectory($BackupPath, $src)
|
|
$extracted = $true
|
|
}
|
|
if (-not (Test-Path $src)) { Log "ERROR: backup path not found: $src"; exit 1 }
|
|
|
|
# --- Read manifest for the shared-dir target ---
|
|
$sharedDir = 'C:\geaofi'
|
|
$mani = Join-Path $src 'manifest.json'
|
|
if (Test-Path $mani) {
|
|
try {
|
|
$m = Get-Content $mani -Raw | ConvertFrom-Json
|
|
if ($m.SharedDataDirectory) { $sharedDir = $m.SharedDataDirectory }
|
|
Log "manifest: shared=$sharedDir partGroup=$($m.SelectedPartGroup) ver=$($m.goCMMVersion)"
|
|
} catch { Log "WARN: could not parse manifest.json: $($_.Exception.Message)" }
|
|
}
|
|
|
|
# --- Import the registry pointers ---
|
|
$reg = Join-Path $src 'registry\goCMM.reg'
|
|
if (Test-Path $reg) {
|
|
reg import "$reg" 2>&1 | Out-Null
|
|
Log "Imported registry key from goCMM.reg"
|
|
} else {
|
|
Log "WARN: registry\goCMM.reg missing in backup - creating an empty key so the ACL still lands"
|
|
if (-not (Test-Path $goCmmKey)) { New-Item -Path $goCmmKey -Force | Out-Null }
|
|
}
|
|
|
|
# --- Optional per-bay Selected Part Group override (use when restoring to a different bay) ---
|
|
if ($SelectedPartGroup) {
|
|
if (-not (Test-Path $goCmmKey)) { New-Item -Path $goCmmKey -Force | Out-Null }
|
|
New-ItemProperty -Path $goCmmKey -Name 'Selected Part Group' -Value $SelectedPartGroup -PropertyType String -Force | Out-Null
|
|
Log "Override Selected Part Group = $SelectedPartGroup"
|
|
}
|
|
|
|
# --- Lay down the Shared Data Directory (ApplicationSettings.xml = all tabs) ---
|
|
$geaofiSrc = Join-Path $src 'geaofi'
|
|
if (Test-Path $geaofiSrc) {
|
|
New-Item -ItemType Directory -Path $sharedDir -Force -ErrorAction SilentlyContinue | Out-Null
|
|
robocopy $geaofiSrc $sharedDir /E /R:1 /W:1 /NFL /NDL /NJH /NJS | Out-Null
|
|
Log "Restored Shared Data Directory -> $sharedDir"
|
|
if (Test-Path (Join-Path $sharedDir 'ApplicationSettings.xml')) {
|
|
Log "ApplicationSettings.xml in place (PC-DMIS + all Settings tabs)"
|
|
} else {
|
|
Log "WARN: ApplicationSettings.xml not present after restore"
|
|
}
|
|
} else {
|
|
Log "WARN: geaofi payload missing in backup - settings tabs NOT restored"
|
|
}
|
|
|
|
# --- Find/replace across ALL restored data (registry values + every text file
|
|
# under the Shared Data Directory). The built-in legacy->new FQDN swaps run
|
|
# AUTOMATICALLY; -ReplaceFrom/-ReplaceTo adds one more; case-insensitive. ---
|
|
$rewrites = @()
|
|
if (-not $NoDefaultRewrite) { $rewrites += $DefaultRewrites }
|
|
if ($ReplaceFrom -and $ReplaceTo) { $rewrites += @{ From = $ReplaceFrom; To = $ReplaceTo } }
|
|
|
|
if ($rewrites.Count -gt 0) {
|
|
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
|
|
foreach ($rw in $rewrites) {
|
|
$from = $rw.From; $to = $rw.To
|
|
if (-not $from) { continue }
|
|
Log "Find/replace: '$from' -> '$to' (case-insensitive)"
|
|
$rxFrom = [regex]::Escape($from)
|
|
|
|
# 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, $rxFrom, 'IgnoreCase'))) {
|
|
$new = [regex]::Replace($p.Value, $rxFrom, $to, 'IgnoreCase')
|
|
Set-ItemProperty -Path $goCmmKey -Name $p.Name -Value $new
|
|
Log " reg [$($p.Name)] -> $new"
|
|
}
|
|
}
|
|
} catch { Log " WARN: registry rewrite failed: $($_.Exception.Message)" }
|
|
}
|
|
|
|
# files: every text file under the Shared Data Directory
|
|
if (Test-Path $sharedDir) {
|
|
Get-ChildItem -Path $sharedDir -Recurse -File -Include *.xml,*.txt,*.csv,*.config,*.ini -ErrorAction SilentlyContinue | ForEach-Object {
|
|
try {
|
|
$c = [System.IO.File]::ReadAllText($_.FullName)
|
|
if ([regex]::IsMatch($c, $rxFrom, 'IgnoreCase')) {
|
|
$nc = [regex]::Replace($c, $rxFrom, $to, 'IgnoreCase')
|
|
[System.IO.File]::WriteAllText($_.FullName, $nc, $utf8NoBom)
|
|
Log " file [$($_.Name)] rewritten"
|
|
}
|
|
} catch { Log " WARN: file rewrite $($_.FullName): $($_.Exception.Message)" }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# --- Grant BUILTIN\Users ReadKey+WriteKey on the reg key (goCMM opens it writable:true to read) ---
|
|
if (Test-Path $goCmmKey) {
|
|
try {
|
|
$acl = Get-Acl -Path $goCmmKey
|
|
$rule = New-Object System.Security.AccessControl.RegistryAccessRule(
|
|
'BUILTIN\Users','ReadKey,WriteKey','ContainerInherit','None','Allow')
|
|
$acl.AddAccessRule($rule)
|
|
Set-Acl -Path $goCmmKey -AclObject $acl -ErrorAction Stop
|
|
Log "Granted BUILTIN\Users ReadKey,WriteKey on $goCmmKey"
|
|
} catch { Log "WARN: registry ACL grant failed: $($_.Exception.Message)" }
|
|
}
|
|
|
|
# --- Grant BUILTIN\Users Modify on the Shared Data Directory (goCMM saves settings there) ---
|
|
if (Test-Path $sharedDir) {
|
|
& icacls $sharedDir /grant 'BUILTIN\Users:(OI)(CI)M' /T /C 2>&1 | Out-Null
|
|
Log "Granted BUILTIN\Users Modify on $sharedDir"
|
|
}
|
|
|
|
if ($extracted) { Remove-Item $src -Recurse -Force -ErrorAction SilentlyContinue }
|
|
Log "==== DONE ===="
|
|
Write-Host ""
|
|
Write-Host "goCMM restore complete. Log: $log" -ForegroundColor Green
|