Runtime/Invoke-OSDStageRegistry.ps1

#Requires -Version 5.1
[CmdletBinding()]
param([string]$ConfigurationPath, [string]$ResultPath)
$ErrorActionPreference = 'Stop'
if (-not $ConfigurationPath) { $ConfigurationPath = Join-Path $PSScriptRoot 'config.json' }
. (Join-Path $PSScriptRoot 'OSDStageRegistry.ps1')
$started = (Get-Date).ToUniversalTime().ToString('o')
$rows = [System.Collections.Generic.List[object]]::new()
$fatal = $null
$config = $null
try {
    if ($env:OS -ne 'Windows_NT' -or (Test-Path -LiteralPath 'HKLM:\SYSTEM\CurrentControlSet\Control\MiniNT')) { throw 'Registry execution requires full Windows, not WinPE.' }
    $config = Get-Content -LiteralPath $ConfigurationPath -Raw -Encoding UTF8 | ConvertFrom-Json -ErrorAction Stop
    Assert-OSDStageRegistryConfiguration -Configuration $config
    if (-not $ResultPath) { $ResultPath = [Environment]::ExpandEnvironmentVariables([string]$config.ResultPath) }
    if ([string]::IsNullOrWhiteSpace($ResultPath) -or -not [IO.Path]::IsPathRooted($ResultPath)) { throw 'A rooted registry result path is required.' }
    # Validate the entire configuration before the first registry write.
    if (-not [Environment]::Is64BitOperatingSystem -and @($config.Operations | Where-Object View -eq 'Registry64').Count -gt 0) { throw 'Registry64 was requested on 32-bit Windows.' }
    foreach ($op in $config.Operations) {
        try { $rows.Add((Invoke-OSDStageRegistryOperation -Operation $op)) }
        catch { $rows.Add([pscustomobject]@{Action=$op.Action;Path=$op.Path;Name=$op.Name;Type=$op.Type;RequestedView=$op.View;ResolvedView=$null;Process64Bit=[Environment]::Is64BitProcess;Status='Failed';Outcome='Failed';Error=$_.Exception.Message}) }
    }
}
catch { $fatal = $_.Exception.Message }
$failed = @($rows | Where-Object Status -eq 'Failed').Count
$exitCode = if ($fatal -or $failed -gt 0 -or $rows.Count -eq 0) { 1 } else { 0 }
$report = [ordered]@{
    SchemaVersion=2; DeploymentId=$(if ($config) { $config.DeploymentId } else { $null })
    Status=$(if ($exitCode -eq 0) { 'Succeeded' } else { 'Failed' }); StartedAtUtc=$started
    CompletedAtUtc=(Get-Date).ToUniversalTime().ToString('o')
    Process64Bit=[Environment]::Is64BitProcess; OS64Bit=[Environment]::Is64BitOperatingSystem
    OperationCount=$rows.Count; SuccessfulOperationCount=@($rows | Where-Object Status -eq 'Succeeded').Count
    FailedOperationCount=$failed; Items=@($rows.ToArray()); Error=$fatal
}
try {
    if ($ResultPath) {
        New-Item -ItemType Directory -Path (Split-Path $ResultPath -Parent) -Force | Out-Null
        $tmp = $ResultPath + '.' + [guid]::NewGuid().ToString('N') + '.tmp'
        try { $report | ConvertTo-Json -Depth 15 | Set-Content -LiteralPath $tmp -Encoding UTF8; Move-Item -LiteralPath $tmp -Destination $ResultPath -Force }
        finally { if (Test-Path -LiteralPath $tmp) { Remove-Item -LiteralPath $tmp -Force } }
    }
} catch { Write-Error -Message $_.Exception.Message -ErrorAction Continue; $exitCode=1 }
$report | ConvertTo-Json -Depth 15 | Write-Output
exit $exitCode