Public/Set-ModuleSecret.ps1
|
<# .SYNOPSIS Saves a secret or credential for a tcs module, encrypted for the current user. .DESCRIPTION The Set-ModuleSecret function saves a SecureString (such as an API token) or a PSCredential under a name, for one module. The value is encrypted with Protect-ConfigValue (DPAPI on Windows, AES-256 + HMAC on Linux/macOS) and stored in the module's settings folder: <ApplicationData>/PowerShell/Config/<ModuleName>/Secrets/<Name>.json An existing secret with the same name is replaced. Read it back with Get-ModuleSecret, which returns the same type that was saved. .PARAMETER ModuleName The module the secret belongs to, for example 'tcs.jira'. .PARAMETER Name The name of the secret, for example 'ApiToken'. Letters, digits, '.', '_' and '-' only, starting with a letter or digit. .PARAMETER SecureString The secret to save. .PARAMETER Credential The credential to save. The user name is stored as it is; the password is encrypted. .PARAMETER Scope Who can decrypt the secret: 'CurrentUser' (default) or 'LocalMachine'. See Protect-ConfigValue. .INPUTS None This function does not accept pipeline input. .OUTPUTS None .EXAMPLE Set-ModuleSecret -ModuleName 'tcs.jira' -Name 'ApiToken' -SecureString (Read-Host -AsSecureString -Prompt 'Token') Saves an API token for tcs.jira. .EXAMPLE Set-ModuleSecret -ModuleName 'tcs.confluence' -Name 'Default' -Credential (Get-Credential) Saves a user name and password for tcs.confluence. .NOTES Author: Nigel Tatschner Company: TheCodeSaiyan .LINK Get-ModuleSecret .LINK Remove-ModuleSecret #> function Set-ModuleSecret { [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'SecureString')] [OutputType([void])] param( [Parameter(Mandatory, Position = 0, HelpMessage = 'The module the secret belongs to.')] [ValidatePattern('^[A-Za-z0-9][A-Za-z0-9._-]*$')] [string]$ModuleName, [Parameter(Mandatory, Position = 1, HelpMessage = 'The name of the secret.')] [ValidatePattern('^[A-Za-z0-9][A-Za-z0-9._-]*$')] [string]$Name, [Parameter(Mandatory, ParameterSetName = 'SecureString', HelpMessage = 'The secret to save.')] [ValidateNotNull()] [System.Security.SecureString]$SecureString, [Parameter(Mandatory, ParameterSetName = 'Credential', HelpMessage = 'The credential to save.')] [ValidateNotNull()] [System.Management.Automation.PSCredential]$Credential, [Parameter(HelpMessage = 'Who can decrypt the secret.')] [ValidateSet('CurrentUser', 'LocalMachine')] [string]$Scope = 'CurrentUser' ) $path = Get-ModuleSecretPath -ModuleName $ModuleName -Name $Name if (-not $PSCmdlet.ShouldProcess("$ModuleName secret '$Name'", 'Save secret')) { return } if ($PSCmdlet.ParameterSetName -eq 'Credential') { $type = 'PSCredential' $userName = $Credential.UserName $plainText = $Credential.GetNetworkCredential().Password } else { $type = 'SecureString' $userName = $null $plainText = (New-Object System.Net.NetworkCredential -ArgumentList '', $SecureString).Password } try { # An empty secret (for example a credential without a password) is stored as empty $protected = '' if (-not [string]::IsNullOrEmpty($plainText)) { $protected = Protect-ConfigValue -Value $plainText -Scope $Scope -ErrorAction Stop if ([string]::IsNullOrEmpty($protected)) { throw "The secret '$Name' could not be protected." } } } finally { $plainText = $null } $data = @{ Type = $type Value = $protected Updated = [datetime]::UtcNow.ToString('o', [System.Globalization.CultureInfo]::InvariantCulture) } if ($userName) { $data['UserName'] = $userName } Write-JsonFile -Path $path -Data $data } |