<# 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,*.bas -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