CMM: add goCMM settings backup/restore + debug scripts
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>
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
@echo off
|
||||||
|
REM Backup-goCMMSettings.bat - capture this bay's goCMM settings.
|
||||||
|
REM
|
||||||
|
REM Run on a LIVE legacy goCMM device, AS ADMINISTRATOR (reg export of HKLM +
|
||||||
|
REM read of C:\geaofi needs admin). Captures the registry pointers + the whole
|
||||||
|
REM Shared Data Directory (ApplicationSettings.xml = all 7 Settings tabs) into
|
||||||
|
REM one zip under C:\Logs\CMM\gocmm-backup\.
|
||||||
|
REM
|
||||||
|
REM Backup-goCMMSettings.bat (default output dir)
|
||||||
|
REM Backup-goCMMSettings.bat -OutDir D:\path
|
||||||
|
|
||||||
|
setlocal
|
||||||
|
set "HERE=%~dp0"
|
||||||
|
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%HERE%Backup-goCMMSettings.ps1" %*
|
||||||
|
echo.
|
||||||
|
echo ---------------------------------------------------------------
|
||||||
|
echo Backup zip is under C:\Logs\CMM\gocmm-backup\ (gocmm_backup_*.zip)
|
||||||
|
echo ---------------------------------------------------------------
|
||||||
|
pause
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<#
|
||||||
|
Backup-goCMMSettings.ps1
|
||||||
|
|
||||||
|
Capture a goCMM bay's COMPLETE settings into one zip. Run on a LIVE legacy
|
||||||
|
goCMM device. Mirrors Backup-FormtracepakSettings.ps1.
|
||||||
|
|
||||||
|
goCMM stores settings in two places:
|
||||||
|
1. Registry pointers: HKLM\SOFTWARE\WOW6432Node\General Electric\goCMM
|
||||||
|
Shared Data Directory, Selected Part Group, Installation Directory
|
||||||
|
2. The Shared Data Directory (default C:\geaofi\) holds ApplicationSettings.xml
|
||||||
|
= the real content of ALL 7 Settings tabs: PC-DMIS, Quindos, Modus,
|
||||||
|
Machine Definition, User Input, Notifications, Part Groups.
|
||||||
|
|
||||||
|
This zip captures both so a re-imaged bay can be restored to identical settings.
|
||||||
|
|
||||||
|
Output: C:\Logs\CMM\gocmm-backup\gocmm_backup_<PC>_<timestamp>.zip
|
||||||
|
Run as administrator (reg export of HKLM + read of the data dir).
|
||||||
|
#>
|
||||||
|
param(
|
||||||
|
[string]$OutDir = 'C:\Logs\CMM\gocmm-backup'
|
||||||
|
)
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
$ts = Get-Date -Format 'yyyyMMdd-HHmmss'
|
||||||
|
New-Item -ItemType Directory -Path $OutDir -Force -ErrorAction SilentlyContinue | Out-Null
|
||||||
|
$log = Join-Path $OutDir "gocmm-backup-$ts.log"
|
||||||
|
function Log($m){ Write-Host $m; $m | Out-File -FilePath $log -Append }
|
||||||
|
|
||||||
|
$stage = Join-Path $env:TEMP "gocmm-bk-$ts"
|
||||||
|
New-Item -ItemType Directory -Path $stage, "$stage\registry", "$stage\geaofi" -Force | Out-Null
|
||||||
|
|
||||||
|
Log "==== goCMM backup on $env:COMPUTERNAME at $(Get-Date) ===="
|
||||||
|
|
||||||
|
# --- Read the 3 registry pointers (32-bit / WOW6432Node view, as the 32-bit app sees them) ---
|
||||||
|
$sharedDir = 'C:\geaofi'; $partGroup = ''; $installDir = ''
|
||||||
|
try {
|
||||||
|
$base32 = [Microsoft.Win32.RegistryKey]::OpenBaseKey('LocalMachine','Registry32')
|
||||||
|
$k = $base32.OpenSubKey('SOFTWARE\General Electric\goCMM', $false)
|
||||||
|
if ($k) {
|
||||||
|
$sharedDir = ([string]$k.GetValue('Shared Data Directory','C:\geaofi')).TrimEnd('\')
|
||||||
|
$partGroup = [string]$k.GetValue('Selected Part Group','')
|
||||||
|
$installDir = [string]$k.GetValue('Installation Directory','')
|
||||||
|
$k.Close()
|
||||||
|
Log "Shared Data Directory = $sharedDir"
|
||||||
|
Log "Selected Part Group = $partGroup"
|
||||||
|
Log "Installation Directory= $installDir"
|
||||||
|
} else { Log "WARN: goCMM registry key not found - defaulting Shared Data Directory to $sharedDir" }
|
||||||
|
} catch { Log "WARN: reading goCMM registry failed: $($_.Exception.Message)" }
|
||||||
|
|
||||||
|
# --- Export the registry key ---
|
||||||
|
reg export 'HKLM\SOFTWARE\WOW6432Node\General Electric\goCMM' "$stage\registry\goCMM.reg" /y 2>&1 | Out-Null
|
||||||
|
if (Test-Path "$stage\registry\goCMM.reg") { Log "Exported registry key" } else { Log "WARN: registry export produced no file" }
|
||||||
|
|
||||||
|
# --- Copy the Shared Data Directory (skip transient LocalProgramCopies + logs) ---
|
||||||
|
if (Test-Path $sharedDir) {
|
||||||
|
robocopy $sharedDir "$stage\geaofi" /E /XD LocalProgramCopies logs /R:1 /W:1 /NFL /NDL /NJH /NJS | Out-Null
|
||||||
|
Log "Copied $sharedDir (excluded LocalProgramCopies, logs)"
|
||||||
|
if (Test-Path "$stage\geaofi\ApplicationSettings.xml") {
|
||||||
|
Log "ApplicationSettings.xml captured (PC-DMIS + all Settings tabs)"
|
||||||
|
} else {
|
||||||
|
Log "WARN: ApplicationSettings.xml NOT found under $sharedDir - settings tabs may be unconfigured"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log "WARN: Shared Data Directory $sharedDir does not exist - only the registry key will be in this backup"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Manifest ---
|
||||||
|
$ver = ''
|
||||||
|
if ($installDir) {
|
||||||
|
$exe = Join-Path ($installDir.TrimEnd('\')) 'goCMM.exe'
|
||||||
|
if (Test-Path $exe) { $ver = (Get-Item $exe).VersionInfo.FileVersion }
|
||||||
|
}
|
||||||
|
[pscustomobject]@{
|
||||||
|
Computer = $env:COMPUTERNAME
|
||||||
|
Timestamp = (Get-Date -Format o)
|
||||||
|
SharedDataDirectory = $sharedDir
|
||||||
|
SelectedPartGroup = $partGroup
|
||||||
|
InstallationDirectory = $installDir
|
||||||
|
goCMMVersion = $ver
|
||||||
|
} | ConvertTo-Json | Out-File "$stage\manifest.json" -Encoding ascii
|
||||||
|
|
||||||
|
# --- Zip ---
|
||||||
|
$zip = Join-Path $OutDir "gocmm_backup_${env:COMPUTERNAME}_$ts.zip"
|
||||||
|
if (Test-Path $zip) { Remove-Item $zip -Force }
|
||||||
|
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||||
|
[System.IO.Compression.ZipFile]::CreateFromDirectory($stage, $zip)
|
||||||
|
Remove-Item $stage -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
Log "==== DONE: $zip ===="
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "goCMM backup written:" -ForegroundColor Green
|
||||||
|
Write-Host " $zip"
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
@echo off
|
||||||
|
REM Install-goCMMSettings.bat - restore goCMM settings from a backup zip.
|
||||||
|
REM
|
||||||
|
REM Run AS ADMINISTRATOR / at imaging. Imports the registry pointers + the
|
||||||
|
REM Shared Data Directory and grants the access goCMM needs under lockdown.
|
||||||
|
REM
|
||||||
|
REM Usage:
|
||||||
|
REM Install-goCMMSettings.bat "<zip>"
|
||||||
|
REM plain restore
|
||||||
|
REM Install-goCMMSettings.bat "<zip>" "\\server\SHARED\CMM\CMM7\Spool"
|
||||||
|
REM restore + override the Selected Part Group (per-bay)
|
||||||
|
REM Install-goCMMSettings.bat "<zip>" /replace <from> <to>
|
||||||
|
REM restore + find/replace <from> -> <to> across ALL registry values and
|
||||||
|
REM data files (e.g. legacy FQDN -> new FQDN). Swap the DOMAIN SUFFIX to
|
||||||
|
REM fix every host at once:
|
||||||
|
REM Install-goCMMSettings.bat "bk.zip" /replace rd.ds.ge.com wjs.geaerospace.net
|
||||||
|
|
||||||
|
setlocal
|
||||||
|
set "HERE=%~dp0"
|
||||||
|
if "%~1"=="" (
|
||||||
|
echo Usage:
|
||||||
|
echo %~nx0 "^<zip^>"
|
||||||
|
echo %~nx0 "^<zip^>" "^<SelectedPartGroup^>"
|
||||||
|
echo %~nx0 "^<zip^>" /replace ^<from^> ^<to^>
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
set "ZIP=%~1"
|
||||||
|
set "PS=powershell.exe -NoProfile -ExecutionPolicy Bypass -File ""%HERE%Install-goCMMSettings.ps1"" -BackupPath ""%ZIP%"""
|
||||||
|
|
||||||
|
if /i "%~2"=="/replace" (
|
||||||
|
if "%~4"=="" ( echo /replace needs ^<from^> ^<to^> & pause & exit /b 1 )
|
||||||
|
%PS% -ReplaceFrom "%~3" -ReplaceTo "%~4"
|
||||||
|
) else if not "%~2"=="" (
|
||||||
|
%PS% -SelectedPartGroup "%~2"
|
||||||
|
) else (
|
||||||
|
%PS%
|
||||||
|
)
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
<#
|
||||||
|
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
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
@echo off
|
||||||
|
REM gocmm-debug.bat - launcher for gocmm-debug.ps1
|
||||||
|
REM
|
||||||
|
REM *** RUN THIS AS THE OPERATOR (the locked-down shop-floor user). ***
|
||||||
|
REM *** DO NOT right-click "Run as administrator" - that hides the bug. ***
|
||||||
|
REM
|
||||||
|
REM Reproduces the goCMM "Requested registry access is not allowed" error and
|
||||||
|
REM dumps the goCMM registry key's ACL so we can confirm the lockdown stripped
|
||||||
|
REM the BUILTIN\Users write grant. Output lands in C:\Logs\CMM\.
|
||||||
|
|
||||||
|
setlocal
|
||||||
|
set "HERE=%~dp0"
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Running goCMM debug as %USERDOMAIN%\%USERNAME% ...
|
||||||
|
echo (If you see "Administrator: ..." in this window title, STOP - run as the operator instead.)
|
||||||
|
echo.
|
||||||
|
|
||||||
|
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%HERE%gocmm-debug.ps1"
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ---------------------------------------------------------------
|
||||||
|
echo Done. Collect everything under C:\Logs\CMM\ (the .txt and .reg).
|
||||||
|
echo ---------------------------------------------------------------
|
||||||
|
pause
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
# gocmm-debug.ps1 - diagnose goCMM "Requested registry access is not allowed" post-lockdown.
|
||||||
|
#
|
||||||
|
# RUN AS THE OPERATOR (the locked-down shop-floor user), NOT elevated / not "Run as administrator".
|
||||||
|
# Elevation would falsely succeed (admins have write) and hide the bug.
|
||||||
|
#
|
||||||
|
# Root cause being tested: goCMM's GEA_OFI_Common.RegistrySettings.GetRegistryString opens
|
||||||
|
# HKLM\Software\General Electric\goCMM with writable:TRUE even to READ a value. goCMM is
|
||||||
|
# 32-bit, so that redirects to HKLM\SOFTWARE\WOW6432Node\General Electric\goCMM. If the
|
||||||
|
# operator lacks WRITE on that key (lockdown stripped the BUILTIN\Users grant), the open throws.
|
||||||
|
#
|
||||||
|
# Output: C:\Logs\CMM\gocmm-debug-<PC>-<timestamp>.txt (pull this back for review)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
$ts = Get-Date -Format 'yyyyMMdd-HHmmss'
|
||||||
|
$dir = 'C:\Logs\CMM'
|
||||||
|
New-Item -ItemType Directory -Path $dir -Force -ErrorAction SilentlyContinue | Out-Null
|
||||||
|
$log = Join-Path $dir "gocmm-debug-$env:COMPUTERNAME-$ts.txt"
|
||||||
|
function W($m){ $m | Tee-Object -FilePath $log -Append }
|
||||||
|
|
||||||
|
$key32native = 'SOFTWARE\General Electric\goCMM' # path under the 32-bit (Registry32) base
|
||||||
|
$keyWow = 'HKLM:\SOFTWARE\WOW6432Node\General Electric\goCMM'
|
||||||
|
|
||||||
|
W "================ goCMM debug ================"
|
||||||
|
W "When : $(Get-Date)"
|
||||||
|
W "PC : $env:COMPUTERNAME"
|
||||||
|
W "User : $env:USERDOMAIN\$env:USERNAME"
|
||||||
|
W "PS : $($PSVersionTable.PSVersion) (process is $([IntPtr]::Size*8)-bit)"
|
||||||
|
W "Elevated: $((New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))"
|
||||||
|
W ""
|
||||||
|
|
||||||
|
W "================ whoami /all (identity + groups + privileges) ================"
|
||||||
|
(whoami /all 2>&1 | Out-String) | W
|
||||||
|
W ""
|
||||||
|
|
||||||
|
W "================ goCMM key values (32-bit view, read-only open) ================"
|
||||||
|
try {
|
||||||
|
$base32 = [Microsoft.Win32.RegistryKey]::OpenBaseKey('LocalMachine','Registry32')
|
||||||
|
$kr = $base32.OpenSubKey($key32native, $false)
|
||||||
|
if ($kr) {
|
||||||
|
foreach ($n in $kr.GetValueNames()) { W (" {0} = {1}" -f $n, $kr.GetValue($n)) }
|
||||||
|
$kr.Close()
|
||||||
|
} else { W " (key NOT found in 32-bit view)" }
|
||||||
|
} catch { W " ERROR reading values: $($_.Exception.Message)" }
|
||||||
|
W ""
|
||||||
|
|
||||||
|
W "================ PROBE 1: mimic goCMM GetRegistryString -> OpenSubKey(writable:TRUE), 32-bit view ================"
|
||||||
|
W "(this is the exact call that throws in the app)"
|
||||||
|
try {
|
||||||
|
$base32 = [Microsoft.Win32.RegistryKey]::OpenBaseKey('LocalMachine','Registry32')
|
||||||
|
$kw = $base32.OpenSubKey($key32native, $true)
|
||||||
|
if ($kw) { W " RESULT: SUCCESS - opened goCMM key WRITABLE. (operator HAS write; settings should work)"; $kw.Close() }
|
||||||
|
else { W " RESULT: NULL - key missing; app would attempt CreateSubKey (also needs write)" }
|
||||||
|
} catch [System.Security.SecurityException] {
|
||||||
|
W " RESULT: *** REPRODUCED *** SecurityException: $($_.Exception.Message)"
|
||||||
|
W " -> operator lacks WRITE on the goCMM key. Lockdown stripped the BUILTIN\Users grant, or a Deny applies."
|
||||||
|
} catch {
|
||||||
|
W " RESULT: OTHER $($_.Exception.GetType().FullName): $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
W ""
|
||||||
|
|
||||||
|
W "================ PROBE 2: read-only open (writable:FALSE) - does the operator at least READ? ================"
|
||||||
|
try {
|
||||||
|
$base32 = [Microsoft.Win32.RegistryKey]::OpenBaseKey('LocalMachine','Registry32')
|
||||||
|
$kr2 = $base32.OpenSubKey($key32native, $false)
|
||||||
|
if ($kr2) { W " read-only open OK (read works; only WRITE-open is denied -> confirms the writable:true bug)"; $kr2.Close() }
|
||||||
|
else { W " read-only open returned NULL (key missing)" }
|
||||||
|
} catch { W " read-only open FAILED: $($_.Exception.Message)" }
|
||||||
|
W ""
|
||||||
|
|
||||||
|
W "================ ACL on goCMM key (the smoking gun) ================"
|
||||||
|
try {
|
||||||
|
$acl = Get-Acl -Path $keyWow
|
||||||
|
W (" Owner : " + $acl.Owner)
|
||||||
|
W (" SDDL : " + $acl.Sddl)
|
||||||
|
W " Access rules:"
|
||||||
|
$acl.Access | ForEach-Object {
|
||||||
|
W (" {0,-30} {1,-22} {2,-6} Inherited={3}" -f $_.IdentityReference, $_.RegistryRights, $_.AccessControlType, $_.IsInherited)
|
||||||
|
}
|
||||||
|
$usersWrite = $acl.Access | Where-Object {
|
||||||
|
$_.AccessControlType -eq 'Allow' -and
|
||||||
|
"$($_.IdentityReference)" -match 'Users' -and
|
||||||
|
("$($_.RegistryRights)" -match 'WriteKey|SetValue|FullControl')
|
||||||
|
}
|
||||||
|
if ($usersWrite) { W " >> BUILTIN\Users WRITE ACE present (grant survived). Failure is elsewhere - check for a Deny ACE or wrong view." }
|
||||||
|
else { W " >> NO BUILTIN\Users WRITE ACE. Confirms lockdown removed the grant. <<" }
|
||||||
|
} catch { W " Get-Acl failed: $($_.Exception.Message)" }
|
||||||
|
W ""
|
||||||
|
|
||||||
|
W "================ raw reg export of the key (for record) ================"
|
||||||
|
$regOut = Join-Path $dir "gocmm-key-$env:COMPUTERNAME-$ts.reg"
|
||||||
|
reg export "HKLM\SOFTWARE\WOW6432Node\General Electric\goCMM" "$regOut" /y 2>&1 | Out-String | W
|
||||||
|
W " exported -> $regOut"
|
||||||
|
W ""
|
||||||
|
|
||||||
|
W "================ goCMM version + install ================"
|
||||||
|
foreach ($p in @(
|
||||||
|
'C:\Program Files (x86)\General Electric\goCMM\GEAOperatorFriendlyInterface.exe',
|
||||||
|
'C:\Program Files (x86)\General Electric\goCMM\GEA_OFI_Common.dll')) {
|
||||||
|
if (Test-Path $p) { $vi = (Get-Item $p).VersionInfo; W (" {0} FileVer={1} ProductVer={2}" -f (Split-Path $p -Leaf), $vi.FileVersion, $vi.ProductVersion) }
|
||||||
|
else { W " MISSING: $p" }
|
||||||
|
}
|
||||||
|
W ""
|
||||||
|
|
||||||
|
W "================ UAC / registry virtualization ================"
|
||||||
|
(reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v EnableLUA 2>&1 | Out-String) | W
|
||||||
|
(reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v EnableVirtualization 2>&1 | Out-String) | W
|
||||||
|
W ""
|
||||||
|
|
||||||
|
W "================ DONE ================"
|
||||||
|
W "Log : $log"
|
||||||
|
W "Reg : $regOut"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Done. Collected:" -ForegroundColor Green
|
||||||
|
Write-Host " $log"
|
||||||
|
Write-Host " $regOut"
|
||||||
Reference in New Issue
Block a user