Public/Secrets.ps1
<#
.SYNOPSIS Register a Secret Vault. .DESCRIPTION Register a Secret Vault, which is defined by either custom logic or using the SecretManagement module. .PARAMETER Name The unique friendly Name of the Secret Vault within Pode. .PARAMETER VaultParameters A hashtable of extra parameters that should be supplied to either the SecretManagement module, or custom scriptblocks. .PARAMETER UnlockSecret An optional Secret to be used to unlock the Secret Vault if need. .PARAMETER UnlockSecureSecret An optional Secret, as a SecureString, to be used to unlock the Secret Vault if need. .PARAMETER UnlockInterval An optional number of minutes that Pode will periodically check/unlock the Secret Vault. (Default: 0) .PARAMETER NoUnlock If supplied, the Secret Vault will not be unlocked after registration. To unlock you'll need to call Unlock-PodeSecretVault. .PARAMETER CacheTtl An optional number of minutes that Secrets should be cached for. (Default: 0) .PARAMETER InitScriptBlock An optional scriptblock to run before the Secret Vault is registered, letting you initialise any connection, contexts, etc. .PARAMETER VaultName For SecretManagement module Secret Vaults, you can use thie parameter to specify the actual Vault name, and use the above Name parameter as a more friendly name if required. .PARAMETER ModuleName For SecretManagement module Secret Vaults, this is the name/path of the extension module to be used. .PARAMETER ScriptBlock For custom Secret Vaults, this is a scriptblock used to read the Secret from the Vault. .PARAMETER UnlockScriptBlock For custom Secret Vaults, this is an optional scriptblock used to unlock the Secret Vault. .PARAMETER RemoveScriptBlock For custom Secret Vaults, this is an optional scriptblock used to remove a Secret from the Vault. .PARAMETER SetScriptBlock For custom Secret Vaults, this is an optional scriptblock used to create/update a Secret in the Vault. .PARAMETER UnregisterScriptBlock For custom Secret Vaults, this is an optional scriptblock used unregister the Secret Vault with any custom clean-up logic. .EXAMPLE Register-PodeSecretVault -Name 'VaultName' -ModuleName 'Az.KeyVault' -VaultParameters @{ AZKVaultName = $name; SubscriptionId = $subId } .EXAMPLE Register-PodeSecretVault -Name 'VaultName' -VaultParameters @{ Address = 'http://127.0.0.1:8200' } -ScriptBlock { ... } #> function Register-PodeSecretVault { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string] $Name, [Parameter()] [hashtable] $VaultParameters, [Parameter()] [string] $UnlockSecret, [Parameter()] [securestring] $UnlockSecureSecret, [Parameter()] [int] $UnlockInterval = 0, [switch] $NoUnlock, [Parameter()] [int] $CacheTtl = 0, # in minutes [Parameter()] [scriptblock] $InitScriptBlock, [Parameter(ParameterSetName = 'SecretManagement')] [string] $VaultName, [Parameter(Mandatory = $true, ParameterSetName = 'SecretManagement')] [Alias('Module')] [string] $ModuleName, [Parameter(Mandatory = $true, ParameterSetName = 'Custom')] [scriptblock] $ScriptBlock, # Read a secret [Parameter(ParameterSetName = 'Custom')] [Alias('Unlock')] [scriptblock] $UnlockScriptBlock, [Parameter(ParameterSetName = 'Custom')] [Alias('Remove')] [scriptblock] $RemoveScriptBlock, [Parameter(ParameterSetName = 'Custom')] [Alias('Set')] [scriptblock] $SetScriptBlock, [Parameter(ParameterSetName = 'Custom')] [Alias('Unregister')] [scriptblock] $UnregisterScriptBlock ) # has the vault already been registered? if (Test-PodeSecretVault -Name $Name) { $autoImported = [string]::Empty if ($PodeContext.Server.Secrets.Vaults[$Name].AutoImported) { $autoImported = ' from auto-importing' } # A Secret Vault with the name {0} has already been registered{1} throw ($PodeLocale.secretVaultAlreadyRegisteredAutoImportExceptionMessage -f $Name, $autoImported) } # base vault config if (![string]::IsNullOrEmpty($UnlockSecret)) { $UnlockSecureSecret = $UnlockSecret | ConvertTo-SecureString -AsPlainText -Force } $vault = @{ Name = $Name Type = $PSCmdlet.ParameterSetName.ToLowerInvariant() Parameters = $VaultParameters AutoImported = $false LockableName = "__Pode_SecretVault_$($Name)__" Unlock = @{ Secret = $UnlockSecureSecret Expiry = $null Interval = $UnlockInterval Enabled = (!(Test-PodeIsEmpty $UnlockSecureSecret)) } Cache = @{ Ttl = $CacheTtl Enabled = ($CacheTtl -gt 0) } } # initialise the secret vault if ($null -ne $InitScriptBlock) { $vault | Initialize-PodeSecretVault -ScriptBlock $InitScriptBlock } # set vault config depending on vault type switch ($vault.Type) { 'custom' { $vault | Register-PodeSecretCustomVault ` -ScriptBlock $ScriptBlock ` -UnlockScriptBlock $UnlockScriptBlock ` -RemoveScriptBlock $RemoveScriptBlock ` -SetScriptBlock $SetScriptBlock ` -UnregisterScriptBlock $UnregisterScriptBlock } 'secretmanagement' { $vault | Register-PodeSecretManagementVault ` -VaultName $VaultName ` -ModuleName $ModuleName } } # create timer to clear cached secrets every minute Start-PodeSecretCacheHousekeeper # create a lockable so secrets are thread safe New-PodeLockable -Name $vault.LockableName # add vault config to context $PodeContext.Server.Secrets.Vaults[$Name] = $vault # unlock the vault? if (!$NoUnlock -and $vault.Unlock.Enabled) { Unlock-PodeSecretVault -Name $Name } } <# .SYNOPSIS Unregister a Secret Vault. .DESCRIPTION Unregister a Secret Vault. If the Vault was via the SecretManagement module it will also be unregistered there as well. .PARAMETER Name The Name of the Secret Vault in Pode to unregister. .EXAMPLE Unregister-PodeSecretVault -Name 'VaultName' #> function Unregister-PodeSecretVault { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string] $Name ) # has the vault been registered? if (!(Test-PodeSecretVault -Name $Name)) { return } # get vault $vault = $PodeContext.Server.Secrets.Vaults[$Name] # unlock depending on vault type, and set expiry switch ($vault.Type) { 'custom' { $vault | Unregister-PodeSecretCustomVault } 'secretmanagement' { $vault | Unregister-PodeSecretManagementVault } } # unregister from Pode $null = $PodeContext.Server.Secrets.Vaults.Remove($Name) } <# .SYNOPSIS Unlock the Secret Vault. .DESCRIPTION Unlock the Secret Vault. .PARAMETER Name The Name of the Secret Vault in Pode to be unlocked. .EXAMPLE Unlock-PodeSecretVault -Name 'VaultName' #> function Unlock-PodeSecretVault { param( [Parameter(Mandatory = $true)] [string] $Name ) # has the vault been registered? if (!(Test-PodeSecretVault -Name $Name)) { # No Secret Vault with the name has been registered throw ($PodeLocale.noSecretVaultRegisteredExceptionMessage -f $Vault) } # get vault $vault = $PodeContext.Server.Secrets.Vaults[$Name] $expiry = $null # is unlocking even enabled? if (!$vault.Unlock.Enabled) { return } # unlock depending on vault type, and set expiry $expiry = Lock-PodeObject -Name $vault.LockableName -Return -ScriptBlock { switch ($vault.Type) { 'custom' { return ($vault | Unlock-PodeSecretCustomVault) } 'secretmanagement' { return ($vault | Unlock-PodeSecretManagementVault) } } } # if we have an expiry returned, set to UTC and configure unlock schedule if ($null -ne $expiry) { $expiry = ([datetime]$expiry).ToUniversalTime() if ($expiry -le [datetime]::UtcNow) { # Secret Vault unlock expiry date is in the past (UTC) throw ($PodeLocale.secretVaultUnlockExpiryDateInPastExceptionMessage -f $expiry) } $vault.Unlock.Expiry = $expiry Start-PodeSecretVaultUnlocker } } <# .SYNOPSIS Fetches and returns information of a Secret Vault. .DESCRIPTION Fetches and returns information of a Secret Vault. .PARAMETER Name The Name(s) of a Secret Vault to retrieve. .EXAMPLE $vault = Get-PodeSecretVault -Name 'VaultName' .EXAMPLE $vaults = Get-PodeSecretVault -Name 'VaultName1', 'VaultName2' #> function Get-PodeSecretVault { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string[]] $Name ) $vaults = $PodeContext.Server.Secrets.Vaults.Values # further filter by vault names if (($null -ne $Name) -and ($Name.Length -gt 0)) { $vaults = @(foreach ($_name in $Name) { foreach ($vault in $vaults) { if ($vault.Name -ine $_name) { continue } $vault } }) } # return return $vaults } <# .SYNOPSIS Tests if a Secret Vault has been registered. .DESCRIPTION Tests if a Secret Vault has been registered. .PARAMETER Name The Name of the Secret Vault to test. .EXAMPLE if (Test-PodeSecretVault -Name 'VaultName') { ... } #> function Test-PodeSecretVault { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string] $Name ) return (($null -ne $PodeContext.Server.Secrets.Vaults) -and $PodeContext.Server.Secrets.Vaults.ContainsKey($Name)) } <# .SYNOPSIS Mount a Secret from a Secret Vault. .DESCRIPTION Mount a Secret from a Secret Vault, so it can be more easily referenced and support caching. .PARAMETER Name A unique friendly Name for the Secret. .PARAMETER Vault The friendly name of the Secret Vault this Secret can be found in. .PARAMETER Property An optional array of Properties to be returned if the Secret contains multiple properties. .PARAMETER ExpandProperty An optional Property to be expanded from the Secret and return if it contains multiple properties. .PARAMETER Key The Key/Path of the Secret within the Secret Vault. .PARAMETER ArgumentList An optional array of Arguments to be supplied to a custom Secret Vault's scriptblocks. .PARAMETER CacheTtl An optional number of minutes to Cache the Secret's value for. You can use this parameter to override the Secret Vault's value. (Default: -1) If the value is -1 it uses the Secret Vault's CacheTtl. A value of 0 is to disable caching for this Secret. A value >0 overrides the Secret Vault. .EXAMPLE Mount-PodeSecret -Name 'SecretName' -Vault 'VaultName' -Key 'path/to/secret' -ExpandProperty 'foo' .EXAMPLE Mount-PodeSecret -Name 'SecretName' -Vault 'VaultName' -Key 'key_of_secret' -CacheTtl 5 #> function Mount-PodeSecret { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string] $Name, [Parameter(Mandatory = $true)] [string] $Vault, [Parameter()] [string[]] $Property, [Parameter()] [string] $ExpandProperty, [Parameter(Mandatory = $true)] [string] $Key, [Parameter()] [object[]] $ArgumentList, # in minutes (-1 means use the vault default, 0 is off, anything higher than 0 is an override) [Parameter()] [int] $CacheTtl = -1 ) # has the secret been mounted already? if (Test-PodeSecret -Name $Name) { # A Secret with the name has already been mounted throw ($PodeLocale.secretAlreadyMountedExceptionMessage -f $Name) } # does the vault exist? if (!(Test-PodeSecretVault -Name $Vault)) { # No Secret Vault with the name has been registered throw ($PodeLocale.noSecretVaultRegisteredExceptionMessage -f $Vault) } # check properties if (!(Test-PodeIsEmpty $Property) -and !(Test-PodeIsEmpty $ExpandProperty)) { # Parameters 'Property' and 'ExpandPropery' are mutually exclusive throw ($PodeLocale.parametersMutuallyExclusiveExceptionMessage -f 'Property' , 'ExpandPropery') } # which cache value? if ($CacheTtl -lt 0) { $CacheTtl = [int]$PodeContext.Server.Secrets.Vaults[$Vault].Cache.Ttl } # mount secret reference $props = $Property if (![string]::IsNullOrWhiteSpace($ExpandProperty)) { $props = $ExpandProperty } $PodeContext.Server.Secrets.Keys[$Name] = @{ Key = $Key Properties = @{ Fields = $props Expand = (![string]::IsNullOrWhiteSpace($ExpandProperty)) Enabled = (!(Test-PodeIsEmpty $props)) } Vault = $Vault Arguments = $ArgumentList Cache = @{ Ttl = $CacheTtl Enabled = ($CacheTtl -gt 0) } } } <# .SYNOPSIS Dismount a previously mounted Secret. .DESCRIPTION Dismount a previously mounted Secret. .PARAMETER Name The friendly Name of the Secret. .PARAMETER Remove If supplied, the Secret will also be removed from the Secret Vault as well. .EXAMPLE Dismount-PodeSecret -Name 'SecretName' .EXAMPLE Dismount-PodeSecret -Name 'SecretName' -Remove #> function Dismount-PodeSecret { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string] $Name, [switch] $Remove ) # do nothing if the secret hasn't been mounted, unless Remove is specified if (!(Test-PodeSecret -Name $Name)) { if ($Remove) { # No Secret named has been mounted throw ($PodeLocale.noSecretNamedMountedExceptionMessage -f $Name) } return } # if "remove" switch passed, remove the secret from the vault as well if ($Remove) { $secret = $PodeContext.Server.Secrets.Keys[$Name] Remove-PodeSecret -Key $secret.Key -Vault $secret.Vault -ArgumentList $secret.Arguments } # remove reference $null = $PodeContext.Server.Secrets.Keys.Remove($Name) } <# .SYNOPSIS Retrieve the value of a mounted Secret. .DESCRIPTION Retrieve the value of a mounted Secret from a Secret Vault. You can also use "$value = $secret:<NAME>" syntax in certain places. .PARAMETER Name The friendly Name of a Secret. .EXAMPLE $value = Get-PodeSecret -Name 'SecretName' .EXAMPLE $value = $secret:SecretName #> function Get-PodeSecret { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string] $Name ) # has the secret been mounted? if (!(Test-PodeSecret -Name $Name)) { # No Secret named has been mounted throw ($PodeLocale.noSecretNamedMountedExceptionMessage -f $Name) } # get the secret and vault $secret = $PodeContext.Server.Secrets.Keys[$Name] # is the value cached? if ($secret.Cache.Enabled -and ($null -ne $secret.Cache.Expiry) -and ($secret.Cache.Expiry -gt [datetime]::UtcNow)) { return $secret.Cache.Value } # fetch the secret depending on vault type $vault = $PodeContext.Server.Secrets.Vaults[$secret.Vault] $value = Lock-PodeObject -Name $vault.LockableName -Return -ScriptBlock { switch ($vault.Type) { 'custom' { return Get-PodeSecretCustomKey -Vault $secret.Vault -Key $secret.Key -ArgumentList $secret.Arguments } 'secretmanagement' { return Get-PodeSecretManagementKey -Vault $secret.Vault -Key $secret.Key } } } # filter the value by any properties if ($secret.Properties.Enabled) { if ($secret.Properties.Expand) { $value = Select-Object -InputObject $value -ExpandProperty $secret.Properties.Fields } else { $value = Select-Object -InputObject $value -Property $secret.Properties.Fields } } # cache the value if needed if ($secret.Cache.Enabled) { $secret.Cache.Value = $value $secret.Cache.Expiry = [datetime]::UtcNow.AddMinutes($secret.Cache.Ttl) } # return value return $value } <# .SYNOPSIS Test if a Secret has been mounted. .DESCRIPTION Test if a Secret has been mounted. .PARAMETER Name The friendly Name of a Secret. .EXAMPLE if (Test-PodeSecret -Name 'SecretName') { ... } #> function Test-PodeSecret { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string] $Name ) return (($null -ne $PodeContext.Server.Secrets.Keys) -and $PodeContext.Server.Secrets.Keys.ContainsKey($Name)) } <# .SYNOPSIS Update the value of a mounted Secret. .DESCRIPTION Update the value of a mounted Secret in a Secret Vault. You can also use "$secret:<NAME> = $value" syntax in certain places. .PARAMETER Name The friendly Name of a Secret. .PARAMETER InputObject The value to use when updating the Secret. Only the following object types are supported: byte[], string, securestring, pscredential, hashtable. .PARAMETER Metadata An optional Metadata hashtable. .EXAMPLE Update-PodeSecret -Name 'SecretName' -InputObject @{ key = value } .EXAMPLE Update-PodeSecret -Name 'SecretName' -InputObject 'value' .EXAMPLE $secret:SecretName = 'value' #> function Update-PodeSecret { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string] $Name, #> byte[], string, securestring, pscredential, hashtable [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true )] [object] $InputObject, [Parameter()] [hashtable] $Metadata ) begin { # has the secret been mounted? if (!(Test-PodeSecret -Name $Name)) { # No Secret named has been mounted throw ($PodeLocale.noSecretNamedMountedExceptionMessage -f $Name) } $pipelineItemCount = 0 # Initialize counter to track items in the pipeline. } process { $pipelineItemCount++ # Increment the counter for each item in the pipeline. } end { # Throw an error if more than one item is passed in the pipeline. if ($pipelineItemCount -gt 1) { throw ($PodeLocale.fnDoesNotAcceptArrayAsPipelineInputExceptionMessage -f $($MyInvocation.MyCommand.Name)) } # make sure the value type is correct $InputObject = Protect-PodeSecretValueType -Value $InputObject # get the secret and vault $secret = $PodeContext.Server.Secrets.Keys[$Name] # reset the cache if enabled if ($secret.Cache.Enabled) { $secret.Cache.Value = $InputObject $secret.Cache.Expiry = [datetime]::UtcNow.AddMinutes($secret.Cache.Ttl) } # if we're expanding a property, convert this to a hashtable if ($secret.Properties.Enabled -and $secret.Properties.Expand) { $InputObject = @{ "$($secret.Properties.Fields)" = $InputObject } } # set the secret depending on vault type $vault = $PodeContext.Server.Secrets.Vaults[$secret.Vault] Lock-PodeObject -Name $vault.LockableName -ScriptBlock { switch ($vault.Type) { 'custom' { Set-PodeSecretCustomKey -Vault $secret.Vault -Key $secret.Key -Value $InputObject -Metadata $Metadata -ArgumentList $secret.Arguments } 'secretmanagement' { Set-PodeSecretManagementKey -Vault $secret.Vault -Key $secret.Key -Value $InputObject -Metadata $Metadata } } } } } <# .SYNOPSIS Remove a Secret from a Secret Vault. .DESCRIPTION Remove a Secret from a Secret Vault. To remove a mounted Secret, you can pass the Remove switch to Dismount-PodeSecret. .PARAMETER Key The Key/Path of the Secret within the Secret Vault. .PARAMETER Vault The friendly name of the Secret Vault this Secret can be found in. .PARAMETER ArgumentList An optional array of Arguments to be supplied to a custom Secret Vault's scriptblocks. .EXAMPLE Remove-PodeSecret -Key 'path/to/secret' -Vault 'VaultName' #> function Remove-PodeSecret { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string] $Key, [Parameter(Mandatory = $true)] [string] $Vault, [Parameter()] [object[]] $ArgumentList ) # has the vault been registered? if (!(Test-PodeSecretVault -Name $Vault)) { # No Secret Vault with the name has been registered throw ($PodeLocale.noSecretVaultRegisteredExceptionMessage -f $Vault) } # remove the secret depending on vault type $_vault = $PodeContext.Server.Secrets.Vaults[$Vault] Lock-PodeObject -Name $_vault.LockableName -ScriptBlock { switch ($_vault.Type) { 'custom' { Remove-PodeSecretCustomKey -Vault $Vault -Key $Key -ArgumentList $ArgumentList } 'secretmanagement' { Remove-PodeSecretManagementKey -Vault $Vault -Key $Key } } } } <# .SYNOPSIS Read a Secret from a Secret Vault. .DESCRIPTION Read a Secret from a Secret Vault. .PARAMETER Key The Key/Path of the Secret within the Secret Vault. .PARAMETER Vault The friendly name of the Secret Vault this Secret can be found in. .PARAMETER Property An optional array of Properties to be returned if the Secret contains multiple properties. .PARAMETER ExpandProperty An optional Property to be expanded from the Secret and return if it contains multiple properties. .PARAMETER ArgumentList An optional array of Arguments to be supplied to a custom Secret Vault's scriptblocks. .EXAMPLE $value = Read-PodeSecret -Key 'path/to/secret' -Vault 'VaultName' .EXAMPLE $value = Read-PodeSecret -Key 'key_of_secret' -Vault 'VaultName' -Property prop1, prop2 #> function Read-PodeSecret { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string] $Key, [Parameter(Mandatory = $true)] [string] $Vault, [Parameter()] [string[]] $Property, [Parameter()] [string] $ExpandProperty, [Parameter()] [object[]] $ArgumentList ) # has the vault been registered? if (!(Test-PodeSecretVault -Name $Vault)) { # No Secret Vault with the name has been registered throw ($PodeLocale.noSecretVaultRegisteredExceptionMessage -f $Vault) } # fetch the secret depending on vault type $_vault = $PodeContext.Server.Secrets.Vaults[$Vault] $value = Lock-PodeObject -Name $_vault.LockableName -Return -ScriptBlock { switch ($_vault.Type) { 'custom' { return Get-PodeSecretCustomKey -Vault $Vault -Key $Key -ArgumentList $ArgumentList } 'secretmanagement' { return Get-PodeSecretManagementKey -Vault $Vault -Key $Key } } } # filter the value by any properties if (![string]::IsNullOrWhiteSpace($ExpandProperty)) { $value = Select-Object -InputObject $value -ExpandProperty $ExpandProperty } elseif (![string]::IsNullOrEmpty($Property)) { $value = Select-Object -InputObject $value -Property $Property } # return value return $value } <# .SYNOPSIS Create/update a Secret in a Secret Vault. .DESCRIPTION Create/update a Secret in a Secret Vault. .PARAMETER Key The Key/Path of the Secret within the Secret Vault. .PARAMETER Vault The friendly name of the Secret Vault this Secret should be created in. .PARAMETER InputObject The value to use when updating the Secret. Only the following object types are supported: byte[], string, securestring, pscredential, hashtable. .PARAMETER Metadata An optional Metadata hashtable. .PARAMETER ArgumentList An optional array of Arguments to be supplied to a custom Secret Vault's scriptblocks. .EXAMPLE Set-PodeSecret -Key 'path/to/secret' -Vault 'VaultName' -InputObject 'value' .EXAMPLE Set-PodeSecret -Key 'key_of_secret' -Vault 'VaultName' -InputObject @{ key = value } #> function Set-PodeSecret { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string] $Key, [Parameter(Mandatory = $true)] [string] $Vault, #> byte[], string, securestring, pscredential, hashtable [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [object] $InputObject, [Parameter()] [hashtable] $Metadata, [Parameter()] [object[]] $ArgumentList ) begin { # has the vault been registered? if (!(Test-PodeSecretVault -Name $Vault)) { # No Secret Vault with the name has been registered throw ($PodeLocale.noSecretVaultRegisteredExceptionMessage -f $Vault) } $pipelineItemCount = 0 # Initialize counter to track items in the pipeline. } process { $pipelineItemCount++ # Increment the counter for each item in the pipeline. } end { # Throw an error if more than one item is passed in the pipeline. if ($pipelineItemCount -gt 1) { throw ($PodeLocale.fnDoesNotAcceptArrayAsPipelineInputExceptionMessage -f $($MyInvocation.MyCommand.Name)) } # make sure the value type is correct $InputObject = Protect-PodeSecretValueType -Value $InputObject # set the secret depending on vault type $_vault = $PodeContext.Server.Secrets.Vaults[$Vault] Lock-PodeObject -Name $_vault.LockableName -ScriptBlock { switch ($_vault.Type) { 'custom' { Set-PodeSecretCustomKey -Vault $Vault -Key $Key -Value $InputObject -Metadata $Metadata -ArgumentList $ArgumentList } 'secretmanagement' { Set-PodeSecretManagementKey -Vault $Vault -Key $Key -Value $InputObject -Metadata $Metadata } } } } } |