Public/Backup-PWSHPUKMGT-Data.ps1

<#
.SYNOPSIS
    Backs up all PWSHPUKMGT records to a CMS-encrypted external file.
.DESCRIPTION
    Reads every record from the currently configured hosting backend (flat file or Active
    Directory), and writes them to a single CMS-encrypted backup file, tagged with the source
    hosting model so Restore-PWSHPUKMGT-Data can refuse a cross-backend restore.
.PARAMETER Path
    Destination path for the encrypted backup file.
.PARAMETER CertificateThumbprint
    Certificate thumbprint to encrypt the backup with. Defaults to the configured module
    certificate.
.PARAMETER Server
    The domain controller to run the underlying LDAP requests against. Defaults to the value
    configured in hosting.activeDirectory.server, or, if that is not set either, to the primary
    domain controller (PDC emulator) of the current domain.
.EXAMPLE
    Backup-PWSHPUKMGT-Data -Path 'D:\Backups\pukdata-2026-07-07.json.cms'
#>

function Backup-PWSHPUKMGT-Data {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory, Position = 0)]
        [string]$Path,

        [string]$CertificateThumbprint,

        [string]$Server
    )

    $config = Get-PukModuleConfig
    $thumbprint = if ($CertificateThumbprint) { $CertificateThumbprint } else { $config.certificate.thumbprint }
    $certificate = Test-PukCertificate -Thumbprint $thumbprint -StoreLocation $config.certificate.storeLocation -RequiredEkuOids $config.certificate.requiredEkuOids
    $effectiveServer = Resolve-PukServer -Server $Server -Config $config

    $records = Get-PukAllRecords -Config $config -Certificate $certificate -Server $effectiveServer

    if (-not $PSCmdlet.ShouldProcess($Path, "Write PWSHPUKMGT encrypted backup ($($records.Count) record(s))")) {
        return
    }

    $backup = [PSCustomObject]@{
        schemaVersion = '1.0'
        hostingModel  = $config.hosting.model
        exportedAt    = (Get-Date).ToUniversalTime().ToString('o')
        records       = @($records | ForEach-Object {
                [PSCustomObject]@{ DistinguishedName = $_.DistinguishedName; SerialNumber = $_.SerialNumber; Puk = $_.Puk }
            })
    }

    $encrypted = Protect-PukFlatFileData -InputObject $backup -Certificate $certificate

    $directory = Split-Path -Path $Path -Parent
    if ($directory -and -not (Test-Path -LiteralPath $directory)) {
        New-Item -ItemType Directory -Path $directory -Force | Out-Null
    }

    Set-Content -LiteralPath $Path -Value $encrypted -Encoding utf8 -Force

    Write-PukLog -Level Info -Config $config -Message "Backup-PWSHPUKMGT-Data: wrote $($records.Count) record(s) to '$Path'."
}