Actions/Credential/SecretManagement.ps1

<#
.SYNOPSIS
Credential action: resolve a secret from a Microsoft.PowerShell.SecretManagement vault (#57 §5/§6).
 
.DESCRIPTION
Soft dependency on the Microsoft.PowerShell.SecretManagement module, following the same
pattern as Actions/Connect/AzureDevOps.ps1's AzureDevOpsDsc.Common dependency: the runner does
not require SecretManagement (or any particular vault extension) to be installed unless a
configuration actually asks for this Credential action, and a clear, actionable error is
thrown when it is missing rather than an opaque "command not found".
 
This is the runner's secret-vault backend for #57 §6: any vault registered with
Register-SecretVault (a local vault, Azure Key Vault, HashiCorp Vault, etc. via their
respective SecretManagement extension modules) works here unchanged, since SecretManagement
itself is the abstraction over the vault backend - this action does not talk to a specific
vault product directly. Only unit-tested with Get-Secret mocked (#57 scope note) - this
environment has no live vault to validate a real secret lookup against.
 
.PARAMETER Context
Hashtable with:
  Name [string] - required, the secret name to look up.
  Vault [string] - optional, the registered vault name (Get-Secret's -Vault).
  UserName [string] - optional; when the secret is a bare SecureString, used as the
                       PSCredential's username (defaults to Name).
 
.OUTPUTS
[PSCredential]
#>

param(
    [hashtable]$Context = @{}
)

if ([string]::IsNullOrWhiteSpace([string]$Context.Name)) {
    throw "[Actions/Credential/SecretManagement] 'Name' (the secret name) is required in the Credential context."
}

if (Get-Module -ListAvailable -Name Microsoft.PowerShell.SecretManagement) {
    Import-Module -Name Microsoft.PowerShell.SecretManagement -ErrorAction Stop
}
else {
    throw "[Actions/Credential/SecretManagement] Secret vault support requires the 'Microsoft.PowerShell.SecretManagement' module (plus a registered vault extension). Install it with: Install-Module Microsoft.PowerShell.SecretManagement"
}

$secretParams = @{ Name = [string]$Context.Name; ErrorAction = 'Stop' }
if (-not [string]::IsNullOrWhiteSpace([string]$Context.Vault)) {
    $secretParams.Vault = [string]$Context.Vault
}

$secret = Get-Secret @secretParams

if ($null -eq $secret) {
    throw "[Actions/Credential/SecretManagement] Secret '$($Context.Name)' was not found in vault '$($Context.Vault)'."
}

if ($secret -is [System.Management.Automation.PSCredential]) {
    return $secret
}

if ($secret -is [securestring]) {
    $userName = if (-not [string]::IsNullOrWhiteSpace([string]$Context.UserName)) { [string]$Context.UserName } else { [string]$Context.Name }
    return [System.Management.Automation.PSCredential]::new($userName, $secret)
}

throw "[Actions/Credential/SecretManagement] Secret '$($Context.Name)' is a '$($secret.GetType().Name)', not a PSCredential or SecureString - cannot resolve it to a credential."