Public/Get-NSPSecret.ps1
|
function Get-NSPSecret { <# .SYNOPSIS Retrieves a stored secret by name. Single call site for every NSP script that needs a credential, API key, or token. .DESCRIPTION Resolution order (first hit wins): 1. Environment variable NSP_SECRET_<NAME> (name upper-cased, non-alphanumerics -> _). For CI and one-off overrides. Plaintext by nature - only use where the environment itself is the secret store. 2. SecretManagement vault (default 'NSP'), if the module and vault are present. 3. DPAPI fallback file %LOCALAPPDATA%\NSP\Secrets\<name>.sec (current user + machine). If none match, throws with the exact command to fix it. Returns a [SecureString] by default. Pass -AsPlainText for a [string] (e.g. to put in an HTTP header). .PARAMETER Name The secret name, e.g. 'CW.Control.ApiKey'. .PARAMETER Vault SecretManagement vault name. Defaults to 'NSP'. .PARAMETER AsPlainText Return a plain [string] instead of a [SecureString]. .EXAMPLE $key = Get-NSPSecret -Name 'CW.Control.ApiKey' -AsPlainText $headers.Add('CTRLAuthHeader', $key) .EXAMPLE $cred = [pscredential]::new('api', (Get-NSPSecret -Name 'Some.Token')) #> [CmdletBinding()] [OutputType([System.Security.SecureString], [string])] param( [Parameter(Mandatory, Position = 0)][string]$Name, [string]$Vault = $script:NSPVaultName, [switch]$AsPlainText ) # --- 1. Environment override ------------------------------------------------- $envName = 'NSP_SECRET_' + (($Name -replace '[^A-Za-z0-9]', '_').ToUpperInvariant()) $envVal = [Environment]::GetEnvironmentVariable($envName) if (-not [string]::IsNullOrEmpty($envVal)) { Write-Verbose "Get-NSPSecret: '$Name' resolved from env var $envName." if ($AsPlainText) { return $envVal } return (ConvertTo-SecureString -String $envVal -AsPlainText -Force) } # --- 2. SecretManagement vault -------------------------------------------------- if (Get-Module -ListAvailable -Name 'Microsoft.PowerShell.SecretManagement') { try { Import-Module Microsoft.PowerShell.SecretManagement -ErrorAction Stop } catch { Write-Verbose "Get-NSPSecret: SecretManagement present but failed to import ($_)." } $vaultPresent = $false try { $vaultPresent = [bool](Get-SecretVault -Name $Vault -ErrorAction SilentlyContinue) } catch { $vaultPresent = $false } if ($vaultPresent -and (Get-SecretInfo -Name $Name -Vault $Vault -ErrorAction SilentlyContinue)) { Write-Verbose "Get-NSPSecret: '$Name' resolved from vault '$Vault'." $secure = Get-Secret -Name $Name -Vault $Vault -ErrorAction Stop if ($secure -is [string]) { $secure = ConvertTo-SecureString -String $secure -AsPlainText -Force } if ($AsPlainText) { return (ConvertFrom-NSPSecureString -SecureString $secure) } return $secure } } # --- 3. DPAPI fallback file ----------------------------------------------------- $path = Get-NSPSecretFallbackPath -Name $Name if (Test-Path -LiteralPath $path) { Write-Verbose "Get-NSPSecret: '$Name' resolved from DPAPI file $path." $secure = Get-Content -LiteralPath $path -Raw | ConvertTo-SecureString if ($AsPlainText) { return (ConvertFrom-NSPSecureString -SecureString $secure) } return $secure } throw ("Secret '{0}' not found. Store it with: Set-NSPSecret -Name '{0}' " -f $Name) + ("(or set env var {0}). See NSP-Bootstrap\SECRETS.md." -f $envName) } |