Standard-Machine: UDC data backup + restore scripts for PC swap workflow
Backup-UDCData.bat / Backup-UDCData.ps1: tech-runnable, UAC-self-elevating. Run on the OLD PC before retirement; reads bay number from udc_settings.json, copies CurrentData.json + ArchiveData/ to \\tsgwp00525\...\backup\udc\<bay>\, drops backup.manifest.json. Refuses the 9999 placeholder so backups never collide across PCs. Restore-UDCData.ps1: idempotent, designed for the manifest engine. 99% of cycles silent no-op (sub-second, zero side effects); 1% (cycle after a backup lands at this PC's bay) restores files locally, moves consumed backup to <bay>\migrated\<timestamp>\, writes restore.manifest.json, relaunches UDC. Round-trip + no-op fast path verified end-to-end on the win11 analyzer VM. Already wired into the Standard-Machine GE-Enforce manifest at standard-machine\manifest.json on the v2 share. Complementary to the placeholder-to-real branch in Update-MachineNumber.ps1: that branch covers the 9999 -> real flow, this one covers the pre-imaged-then-swapped flow where Update-MachineNumber already ran before any backup existed. Both safely no-op if the other consumed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
47
playbook/shopfloor-setup/Standard/Backup-UDCData.bat
Normal file
47
playbook/shopfloor-setup/Standard/Backup-UDCData.bat
Normal file
@@ -0,0 +1,47 @@
|
||||
@echo off
|
||||
REM Backup-UDCData.bat - Tech-runnable wrapper for Backup-UDCData.ps1.
|
||||
REM
|
||||
REM Self-elevates via UAC so the script can read C:\ProgramData\UDC\* and
|
||||
REM write to the SFLD share with the right cached creds. Forwards any
|
||||
REM extra args verbatim (e.g. -KeepPriorBackup, -MachineNumber 7605).
|
||||
REM
|
||||
REM Usage on the OLD PC, before retirement:
|
||||
REM 1. Double-click Backup-UDCData.bat (or right-click -> Run as admin)
|
||||
REM 2. Approve the UAC prompt
|
||||
REM 3. Watch the elevated PS window for the success summary
|
||||
REM 4. Confirm \\tsgwp00525.wjs.geaerospace.net\shared\dt\shopfloor\backup\udc\<machine>\
|
||||
REM now contains CurrentData.json + ArchiveData\ + backup.manifest.json
|
||||
|
||||
REM --- Self-elevate ---------------------------------------------------
|
||||
net session >nul 2>&1
|
||||
if %errorLevel% neq 0 (
|
||||
echo Requesting admin rights...
|
||||
powershell -Command "Start-Process '%~f0' -Verb RunAs -ArgumentList '%*'"
|
||||
exit /b
|
||||
)
|
||||
|
||||
set "SCRIPT=%~dp0Backup-UDCData.ps1"
|
||||
|
||||
if not exist "%SCRIPT%" (
|
||||
echo ERROR: %SCRIPT% not found.
|
||||
echo This .bat must be in the same folder as Backup-UDCData.ps1.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo ============================================================
|
||||
echo UDC Data Backup
|
||||
echo ============================================================
|
||||
echo Script: %SCRIPT%
|
||||
echo.
|
||||
|
||||
PowerShell.exe -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT%" %*
|
||||
set RC=%errorLevel%
|
||||
|
||||
echo.
|
||||
echo ============================================================
|
||||
echo Backup script exit code: %RC%
|
||||
echo ============================================================
|
||||
echo.
|
||||
pause
|
||||
exit /b %RC%
|
||||
212
playbook/shopfloor-setup/Standard/Backup-UDCData.ps1
Normal file
212
playbook/shopfloor-setup/Standard/Backup-UDCData.ps1
Normal file
@@ -0,0 +1,212 @@
|
||||
# Backup-UDCData.ps1 - Capture UDC's CurrentData.json + ArchiveData/ to the
|
||||
# SFLD share, keyed by the PC's current machine number. Runs LOCALLY on the
|
||||
# old PC before retirement; the new PC restores during its first
|
||||
# placeholder-to-real machine-number assignment via Update-MachineNumber.ps1.
|
||||
#
|
||||
# DESIGN: backup lives at <share>\<machine>\ root. Presence of CurrentData.json
|
||||
# at that root means "available to restore". Restore consumes by moving content
|
||||
# into <machine>\migrated\<timestamp>\, leaving the root empty so it can't
|
||||
# be replayed.
|
||||
#
|
||||
# USAGE:
|
||||
# - Direct: powershell -NoProfile -ExecutionPolicy Bypass -File Backup-UDCData.ps1
|
||||
# - Wrapper: Backup-UDCData.bat (handles the bypass + elevation prompts)
|
||||
# - Remote (WinRM): ../powershell/remote-execution/Backup-UDCData-Remote.ps1
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
# Override auto-detection for unusual cases. Defaults to whatever UDC
|
||||
# currently has in C:\ProgramData\UDC\udc_settings.json.
|
||||
[string]$MachineNumber,
|
||||
|
||||
# Override the share path. Defaults to the canonical SFLD location.
|
||||
[string]$BackupShareRoot = '\\tsgwp00525.wjs.geaerospace.net\shared\dt\shopfloor\backup\udc',
|
||||
|
||||
# Local UDC data root. Default matches the vendor install path.
|
||||
[string]$UdcDataDir = 'C:\ProgramData\UDC',
|
||||
|
||||
# Cred for the SFLD share. If omitted, the current session's identity
|
||||
# is used (works for SYSTEM-context runs and for local interactive
|
||||
# admin sessions where the user has cached SFLD creds via SFLD-Reg).
|
||||
[System.Management.Automation.PSCredential]$Credential,
|
||||
|
||||
# If the destination already has a backup for this machine, append a
|
||||
# timestamped suffix instead of overwriting. Default: overwrite (latest
|
||||
# backup wins; the prior one only matters if the new PC didn't restore
|
||||
# before this re-backup).
|
||||
[switch]$KeepPriorBackup
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$logDir = 'C:\Logs\UDC'
|
||||
if (-not (Test-Path $logDir)) {
|
||||
try { New-Item -Path $logDir -ItemType Directory -Force | Out-Null } catch { $logDir = $env:TEMP }
|
||||
}
|
||||
$logFile = Join-Path $logDir ("Backup-UDCData_$(Get-Date -Format 'yyyyMMdd-HHmmss').log")
|
||||
try { Start-Transcript -Path $logFile -Append -Force | Out-Null } catch {}
|
||||
|
||||
function Log {
|
||||
param([string]$Msg, [string]$Level = 'INFO')
|
||||
$ts = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
|
||||
Write-Host "[$ts][$Level] $Msg"
|
||||
}
|
||||
|
||||
Log "==============================================="
|
||||
Log "Backup-UDCData starting"
|
||||
Log "Hostname: $env:COMPUTERNAME"
|
||||
Log "User: $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)"
|
||||
Log "UDC data dir: $UdcDataDir"
|
||||
Log "Backup share: $BackupShareRoot"
|
||||
Log "Log file: $logFile"
|
||||
Log "==============================================="
|
||||
|
||||
# --- Resolve machine number ---
|
||||
if (-not $MachineNumber) {
|
||||
$settings = Join-Path $UdcDataDir 'udc_settings.json'
|
||||
if (-not (Test-Path $settings)) {
|
||||
Log "udc_settings.json not found at $settings - cannot auto-detect machine number" 'ERROR'
|
||||
Log "Re-run with -MachineNumber <num> to override"
|
||||
try { Stop-Transcript | Out-Null } catch {}
|
||||
exit 2
|
||||
}
|
||||
try {
|
||||
$json = Get-Content $settings -Raw | ConvertFrom-Json
|
||||
$MachineNumber = $json.GeneralSettings.MachineNumber
|
||||
} catch {
|
||||
Log "Failed to parse udc_settings.json: $_" 'ERROR'
|
||||
try { Stop-Transcript | Out-Null } catch {}
|
||||
exit 3
|
||||
}
|
||||
}
|
||||
if (-not $MachineNumber -or $MachineNumber -eq '9999' -or $MachineNumber -notmatch '^\d+$') {
|
||||
Log "Invalid or placeholder machine number: '$MachineNumber'. Aborting." 'ERROR'
|
||||
Log "(Backups for placeholder 9999 would collide across PCs and aren't useful.)"
|
||||
try { Stop-Transcript | Out-Null } catch {}
|
||||
exit 4
|
||||
}
|
||||
Log "Resolved machine number: $MachineNumber"
|
||||
|
||||
# --- Verify source files exist ---
|
||||
$srcCurrent = Join-Path $UdcDataDir 'CurrentData.json'
|
||||
$srcArchive = Join-Path $UdcDataDir 'ArchiveData'
|
||||
|
||||
$haveCurrent = Test-Path -LiteralPath $srcCurrent
|
||||
$haveArchive = Test-Path -LiteralPath $srcArchive
|
||||
|
||||
if (-not $haveCurrent -and -not $haveArchive) {
|
||||
Log "Neither CurrentData.json nor ArchiveData/ exists under $UdcDataDir. Nothing to back up." 'WARN'
|
||||
try { Stop-Transcript | Out-Null } catch {}
|
||||
exit 0
|
||||
}
|
||||
Log "CurrentData.json present: $haveCurrent"
|
||||
Log "ArchiveData/ present: $haveArchive"
|
||||
|
||||
# --- Mount share if credentials supplied (otherwise rely on ambient auth) ---
|
||||
$psDrive = $null
|
||||
$dest = Join-Path $BackupShareRoot $MachineNumber
|
||||
if ($Credential) {
|
||||
try {
|
||||
$psDrive = New-PSDrive -Name UDCBKP -PSProvider FileSystem -Root $BackupShareRoot -Credential $Credential -ErrorAction Stop
|
||||
Log "Share mounted as UDCBKP: with explicit credentials"
|
||||
$dest = "UDCBKP:\$MachineNumber"
|
||||
} catch {
|
||||
Log "Failed to mount $BackupShareRoot with supplied credentials: $_" 'ERROR'
|
||||
try { Stop-Transcript | Out-Null } catch {}
|
||||
exit 5
|
||||
}
|
||||
}
|
||||
|
||||
# --- Create destination dir ---
|
||||
try {
|
||||
if (-not (Test-Path -LiteralPath $dest)) {
|
||||
New-Item -ItemType Directory -Path $dest -Force | Out-Null
|
||||
Log "Created destination: $dest"
|
||||
}
|
||||
} catch {
|
||||
Log "Failed to create destination $dest : $_" 'ERROR'
|
||||
if ($psDrive) { Remove-PSDrive -Name UDCBKP -Force }
|
||||
try { Stop-Transcript | Out-Null } catch {}
|
||||
exit 6
|
||||
}
|
||||
|
||||
# --- Optional: rotate prior backup at this bay if -KeepPriorBackup ---
|
||||
if ($KeepPriorBackup) {
|
||||
$existingMarker = Join-Path $dest 'CurrentData.json'
|
||||
if (Test-Path -LiteralPath $existingMarker) {
|
||||
$rotateName = Join-Path $dest ('superseded-' + (Get-Date -Format 'yyyyMMdd-HHmmss'))
|
||||
New-Item -ItemType Directory -Path $rotateName -Force | Out-Null
|
||||
Get-ChildItem $dest -Force | Where-Object { $_.Name -ne 'migrated' -and $_.Name -ne (Split-Path $rotateName -Leaf) } | ForEach-Object {
|
||||
Move-Item -LiteralPath $_.FullName -Destination $rotateName -Force
|
||||
}
|
||||
Log "Prior backup rotated into $rotateName (KeepPriorBackup=true)"
|
||||
}
|
||||
}
|
||||
|
||||
# --- Copy CurrentData.json ---
|
||||
$copiedCurrent = $false
|
||||
$currentBytes = 0
|
||||
if ($haveCurrent) {
|
||||
try {
|
||||
Copy-Item -LiteralPath $srcCurrent -Destination (Join-Path $dest 'CurrentData.json') -Force -ErrorAction Stop
|
||||
$currentBytes = (Get-Item -LiteralPath $srcCurrent).Length
|
||||
$copiedCurrent = $true
|
||||
Log "Copied CurrentData.json ($currentBytes bytes)"
|
||||
} catch {
|
||||
Log "Failed to copy CurrentData.json: $_" 'ERROR'
|
||||
}
|
||||
}
|
||||
|
||||
# --- Copy ArchiveData/ recursively ---
|
||||
$copiedArchive = $false
|
||||
$archiveBytes = 0
|
||||
$archiveFiles = 0
|
||||
if ($haveArchive) {
|
||||
try {
|
||||
$destArchive = Join-Path $dest 'ArchiveData'
|
||||
if (Test-Path -LiteralPath $destArchive) {
|
||||
Remove-Item -LiteralPath $destArchive -Recurse -Force -ErrorAction Stop
|
||||
}
|
||||
Copy-Item -LiteralPath $srcArchive -Destination $destArchive -Recurse -Force -ErrorAction Stop
|
||||
$copiedArchive = $true
|
||||
$archiveItems = Get-ChildItem -LiteralPath $destArchive -Recurse -File -ErrorAction SilentlyContinue
|
||||
$archiveBytes = ($archiveItems | Measure-Object Length -Sum).Sum
|
||||
$archiveFiles = $archiveItems.Count
|
||||
Log "Copied ArchiveData/ ($archiveFiles files, $archiveBytes bytes)"
|
||||
} catch {
|
||||
Log "Failed to copy ArchiveData/: $_" 'ERROR'
|
||||
}
|
||||
}
|
||||
|
||||
# --- Drop a small backup.manifest.json next to the data for forensics ---
|
||||
try {
|
||||
$manifest = [ordered]@{
|
||||
BackedUpAt = (Get-Date -Format 'o')
|
||||
SourceHostname = $env:COMPUTERNAME
|
||||
SourceUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
|
||||
MachineNumber = $MachineNumber
|
||||
CurrentDataPresent = $copiedCurrent
|
||||
CurrentDataBytes = $currentBytes
|
||||
ArchiveDataPresent = $copiedArchive
|
||||
ArchiveDataFiles = $archiveFiles
|
||||
ArchiveDataBytes = $archiveBytes
|
||||
}
|
||||
$manifest | ConvertTo-Json | Set-Content -Path (Join-Path $dest 'backup.manifest.json') -Encoding UTF8
|
||||
Log "Wrote backup.manifest.json"
|
||||
} catch {
|
||||
Log "Manifest write failed (non-fatal): $_" 'WARN'
|
||||
}
|
||||
|
||||
# --- Tear down mount ---
|
||||
if ($psDrive) {
|
||||
try { Remove-PSDrive -Name UDCBKP -Force } catch {}
|
||||
}
|
||||
|
||||
Log "==============================================="
|
||||
Log "Backup complete:"
|
||||
Log " Bay: $MachineNumber"
|
||||
Log " Destination: $BackupShareRoot\$MachineNumber\"
|
||||
Log " CurrentData.json: $(if ($copiedCurrent) {'OK ('+$currentBytes+' bytes)'} else {'MISSING'})"
|
||||
Log " ArchiveData/: $(if ($copiedArchive) {'OK ('+$archiveFiles+' files, '+$archiveBytes+' bytes)'} else {'MISSING'})"
|
||||
Log "==============================================="
|
||||
try { Stop-Transcript | Out-Null } catch {}
|
||||
exit 0
|
||||
191
playbook/shopfloor-setup/Standard/Restore-UDCData.ps1
Normal file
191
playbook/shopfloor-setup/Standard/Restore-UDCData.ps1
Normal file
@@ -0,0 +1,191 @@
|
||||
# Restore-UDCData.ps1 - Idempotent UDC data restore for the manifest engine.
|
||||
#
|
||||
# Triggered by the GE Shopfloor Enforce scheduled task (runs as SYSTEM, every
|
||||
# user logon + every 5 min). Standard-machine manifest entry uses
|
||||
# DetectionMethod=Always so this fires every cycle; the script self-decides
|
||||
# whether there's actually any work to do.
|
||||
#
|
||||
# CONTRACT:
|
||||
# - 99% of cycles: no backup waiting -> exit 0 in ~1 second, no side effects
|
||||
# - 1 cycle (the one after Backup-UDCData lands a backup for this PC's bay):
|
||||
# stop UDC, copy CurrentData.json + ArchiveData/ to C:\ProgramData\UDC,
|
||||
# move consumed backup to <bay>\migrated\<timestamp>\, write
|
||||
# restore.manifest.json, restart UDC. After this, root is empty so the
|
||||
# check returns "no backup waiting" again on subsequent cycles.
|
||||
#
|
||||
# DESIGNED FOR THE SWAP WORKFLOW:
|
||||
# New PC gets pre-imaged with real machine number + locked down, sits in
|
||||
# storage. Days/weeks later, tech runs Backup-UDCData on old PC -> backup
|
||||
# lands on share. Tech swaps PCs. New PC powers on at the bay -> ShopFloor
|
||||
# autologon -> manifest engine fires this script -> backup detected ->
|
||||
# restored -> UDC opens with prior history intact.
|
||||
#
|
||||
# Replaces the placeholder->real trigger in Update-MachineNumber.ps1 for the
|
||||
# pre-imaged-then-swapped case (where the trigger fired at imaging time, before
|
||||
# the backup existed). Update-MachineNumber.ps1's branch still handles the
|
||||
# secondary case (tech used 9999 placeholder + sets number at bay) - both
|
||||
# triggers safely no-op if the other already consumed the backup.
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$BackupShareRoot = '\\tsgwp00525.wjs.geaerospace.net\shared\dt\shopfloor\backup\udc',
|
||||
[string]$UdcDataDir = 'C:\ProgramData\UDC',
|
||||
[string]$UdcExePath = 'C:\Program Files\UDC\UDC.exe',
|
||||
[string]$UdcSettingsPath = 'C:\ProgramData\UDC\udc_settings.json',
|
||||
[string]$Site = 'West Jefferson'
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$logDir = 'C:\Logs\UDC'
|
||||
if (-not (Test-Path $logDir)) {
|
||||
try { New-Item -Path $logDir -ItemType Directory -Force | Out-Null } catch { $logDir = $env:TEMP }
|
||||
}
|
||||
$logFile = Join-Path $logDir 'Restore-UDCData.log'
|
||||
|
||||
function Log {
|
||||
param([string]$Msg, [string]$Level = 'INFO')
|
||||
$ts = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
|
||||
$line = "[$ts][$Level] $Msg"
|
||||
Add-Content -LiteralPath $logFile -Value $line
|
||||
Write-Host $line
|
||||
}
|
||||
|
||||
# --- Resolve local machine number ---
|
||||
if (-not (Test-Path -LiteralPath $UdcSettingsPath)) {
|
||||
# No UDC installed yet. Manifest engine will catch up later if/when it lands.
|
||||
exit 0
|
||||
}
|
||||
try {
|
||||
$json = Get-Content -LiteralPath $UdcSettingsPath -Raw | ConvertFrom-Json
|
||||
$mn = $json.GeneralSettings.MachineNumber
|
||||
} catch {
|
||||
Log "Failed to parse $UdcSettingsPath : $_" 'ERROR'
|
||||
exit 0
|
||||
}
|
||||
if (-not $mn -or $mn -eq '9999' -or $mn -notmatch '^\d+$') {
|
||||
# Placeholder or invalid - the placeholder->real trigger in
|
||||
# Update-MachineNumber.ps1 will catch it when the tech sets a real number.
|
||||
exit 0
|
||||
}
|
||||
|
||||
# --- Probe for a waiting backup ---
|
||||
$bayDir = Join-Path $BackupShareRoot $mn
|
||||
$srcCur = Join-Path $bayDir 'CurrentData.json'
|
||||
$srcArc = Join-Path $bayDir 'ArchiveData'
|
||||
|
||||
if (-not (Test-Path -LiteralPath $srcCur)) {
|
||||
# Most-common path: no backup waiting. Exit silently to keep enforce-cycle
|
||||
# logs clean. (Manifest engine still records that the entry ran.)
|
||||
exit 0
|
||||
}
|
||||
|
||||
# We have a backup. From here on, log everything.
|
||||
Log "==============================================="
|
||||
Log "UDC data backup detected at $bayDir - restoring."
|
||||
Log "Hostname: $env:COMPUTERNAME"
|
||||
Log "Machine number: $mn"
|
||||
|
||||
# --- Stop UDC.exe so CurrentData.json isn't locked ---
|
||||
Get-Process UDC -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
try {
|
||||
$_.Kill()
|
||||
$_.WaitForExit(5000) | Out-Null
|
||||
Log "Stopped existing UDC.exe (PID $($_.Id))"
|
||||
} catch {
|
||||
Log "Could not stop UDC.exe (PID $($_.Id)): $_" 'WARN'
|
||||
}
|
||||
}
|
||||
Start-Sleep -Milliseconds 500
|
||||
|
||||
# --- Ensure local UDC data dir exists ---
|
||||
if (-not (Test-Path -LiteralPath $UdcDataDir)) {
|
||||
New-Item -ItemType Directory -Path $UdcDataDir -Force | Out-Null
|
||||
}
|
||||
$localCur = Join-Path $UdcDataDir 'CurrentData.json'
|
||||
$localArc = Join-Path $UdcDataDir 'ArchiveData'
|
||||
|
||||
# --- Copy CurrentData.json ---
|
||||
$copiedCur = $false
|
||||
try {
|
||||
Copy-Item -LiteralPath $srcCur -Destination $localCur -Force -ErrorAction Stop
|
||||
$copiedCur = $true
|
||||
Log "Copied CurrentData.json ($((Get-Item $localCur).Length) bytes)"
|
||||
} catch {
|
||||
Log "Copy CurrentData.json failed: $_" 'ERROR'
|
||||
}
|
||||
|
||||
# --- Copy ArchiveData/ ---
|
||||
$copiedArc = $false
|
||||
$arcFiles = 0
|
||||
$arcBytes = 0
|
||||
if (Test-Path -LiteralPath $srcArc) {
|
||||
try {
|
||||
if (Test-Path -LiteralPath $localArc) {
|
||||
Remove-Item -LiteralPath $localArc -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
Copy-Item -LiteralPath $srcArc -Destination $localArc -Recurse -Force -ErrorAction Stop
|
||||
$arcItems = Get-ChildItem -LiteralPath $localArc -Recurse -File -ErrorAction SilentlyContinue
|
||||
$arcFiles = $arcItems.Count
|
||||
$arcBytes = ($arcItems | Measure-Object Length -Sum).Sum
|
||||
$copiedArc = $true
|
||||
Log "Copied ArchiveData/ ($arcFiles files, $arcBytes bytes)"
|
||||
} catch {
|
||||
Log "Copy ArchiveData/ failed: $_" 'ERROR'
|
||||
}
|
||||
}
|
||||
|
||||
# --- One-shot consumption: only move backup -> migrated/ if BOTH copies succeeded ---
|
||||
# (If one failed, leave the live backup in place so the next cycle can retry.)
|
||||
$consumeOk = ($copiedCur -and ($copiedArc -or -not (Test-Path -LiteralPath $srcArc)))
|
||||
if ($consumeOk) {
|
||||
try {
|
||||
$stamp = Get-Date -Format 'yyyy-MM-ddTHH-mm-ssZ'
|
||||
$migDir = Join-Path $bayDir 'migrated'
|
||||
$migStamp = Join-Path $migDir $stamp
|
||||
if (-not (Test-Path -LiteralPath $migDir)) { New-Item -ItemType Directory -Path $migDir -Force | Out-Null }
|
||||
if (-not (Test-Path -LiteralPath $migStamp)) { New-Item -ItemType Directory -Path $migStamp -Force | Out-Null }
|
||||
|
||||
Move-Item -LiteralPath $srcCur -Destination (Join-Path $migStamp 'CurrentData.json') -Force -ErrorAction Stop
|
||||
if (Test-Path -LiteralPath $srcArc) {
|
||||
Move-Item -LiteralPath $srcArc -Destination (Join-Path $migStamp 'ArchiveData') -Force -ErrorAction Stop
|
||||
}
|
||||
$bakManifest = Join-Path $bayDir 'backup.manifest.json'
|
||||
if (Test-Path -LiteralPath $bakManifest) {
|
||||
Move-Item -LiteralPath $bakManifest -Destination (Join-Path $migStamp 'backup.manifest.json') -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
$restoreManifest = [ordered]@{
|
||||
RestoredAt = (Get-Date -Format 'o')
|
||||
DestinationHostname = $env:COMPUTERNAME
|
||||
DestinationUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
|
||||
MachineNumber = $mn
|
||||
CurrentDataBytes = (Get-Item $localCur).Length
|
||||
ArchiveDataFiles = $arcFiles
|
||||
ArchiveDataBytes = $arcBytes
|
||||
RestoredVia = 'Restore-UDCData.ps1 (manifest engine, on logon)'
|
||||
}
|
||||
$restoreManifest | ConvertTo-Json | Set-Content -Path (Join-Path $migStamp 'restore.manifest.json') -Encoding UTF8
|
||||
|
||||
Log "Backup consumed -> migrated\$stamp\"
|
||||
} catch {
|
||||
Log "Move-to-migrated failed (data IS restored locally, but live backup remains - next cycle will retry consumption): $_" 'ERROR'
|
||||
}
|
||||
} else {
|
||||
Log "Restore incomplete - leaving live backup at $bayDir for retry next cycle." 'WARN'
|
||||
}
|
||||
|
||||
# --- Relaunch UDC with the current machine number args so it picks up the
|
||||
# restored CurrentData.json. UDC's vendor autostart in HKLM\Run will also
|
||||
# fire on the next user logon, so this is belt-and-suspenders for the
|
||||
# same-session case (e.g. tech is at the keyboard during the restore). ---
|
||||
if ((Test-Path -LiteralPath $UdcExePath) -and $copiedCur) {
|
||||
try {
|
||||
Start-Process -FilePath $UdcExePath -ArgumentList @("`"$Site`"", "-$mn")
|
||||
Log "Relaunched UDC.exe with `"$Site`" -$mn"
|
||||
} catch {
|
||||
Log "UDC relaunch failed: $_" 'WARN'
|
||||
}
|
||||
}
|
||||
|
||||
Log "==============================================="
|
||||
exit 0
|
||||
Reference in New Issue
Block a user