Public/Save-NSPToolAnswers.ps1

function Save-NSPToolAnswers {
    <#
    .SYNOPSIS
        Saves a tool's answers to its work folder (Answers\Answers.json), creating the folder.
 
    .DESCRIPTION
        Writes to a temporary file first and then replaces Answers.json, so an interrupted save never
        leaves half a file behind. $null answers are not written.
 
        Answers that hold one of the tool's secret fields (NPS RADIUSSecret, PKI FortiGatePfxPassword)
        are only written when the Toolkit root is limited to Administrators and SYSTEM: an elevated
        session locks it down, otherwise the save fails and nothing is written. A NSP_TOOLKIT_ROOT
        override (tests, portable use) is not checked - protecting that folder is the caller's job.
 
    .PARAMETER Tool
        Tool key.
 
    .PARAMETER Answers
        Object or hashtable to save.
 
    .EXAMPLE
        Save-NSPToolAnswers -Tool AD -Answers $script:ADAnswers
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)][ValidatePattern('^[A-Za-z0-9]+$')][string]$Tool,
        [Parameter(Mandatory)][AllowNull()][object]$Answers
    )

    if ($null -eq $Answers) { return }
    $def = $script:NSPToolDefinitions | Where-Object { $_.Name -eq $Tool } | Select-Object -First 1
    $secrets = @(foreach ($field in @(if ($def) { $def.SecretFields })) {
        $value = if ($Answers -is [Collections.IDictionary]) { $Answers[$field] } elseif ($Answers.PSObject.Properties[$field]) { $Answers.$field } else { $null }
        if (-not [string]::IsNullOrWhiteSpace([string]$value)) { $field }
    })
    $dir = Get-NSPToolWorkPath -Tool $Tool -Kind Answers -Create
    if ($secrets.Count -and -not $env:NSP_TOOLKIT_ROOT) {
        Assert-NSPToolkitRootSecure -Root (Join-Path $env:ProgramData 'NSP\Toolkit') -Reason "$Tool answers holding $($secrets -join ', ')"
    }
    $file = Join-Path $dir 'Answers.json'
    if ($PSCmdlet.ShouldProcess($file, 'Save answers')) {
        $json = ConvertTo-Json -InputObject $Answers -Depth 20
        $tmp = "$file.tmp"
        [IO.File]::WriteAllText($tmp, $json, (New-Object Text.UTF8Encoding($false)))
        Move-Item -LiteralPath $tmp -Destination $file -Force
    }
}