Store.psm1
[CmdletBinding()] param() $scriptName = 'Store' Write-Verbose "[$scriptName] - Importing module" #region - From [functions] - [private] Write-Verbose "[$scriptName] - [functions] - [private] - Processing folder" #region - From [functions] - [private] - [Initialize-SecretVault] Write-Verbose "[$scriptName] - [functions] - [private] - [Initialize-SecretVault] - Importing" #Requires -Modules Microsoft.PowerShell.SecretManagement #Requires -Modules Microsoft.PowerShell.SecretStore function Initialize-SecretVault { <# .SYNOPSIS Initialize a SecretStore with open config. .DESCRIPTION Initialize a secret vault. If the vault does not exist, it will be created and registered. The SecretStore is created with the following parameters: - Authentication: None - PasswordTimeout: -1 (infinite) - Interaction: None - Scope: CurrentUser .EXAMPLE Initialize-SecretStore Initializes a secret vault named 'SecretStore' using the 'Microsoft.PowerShell.SecretStore' module. .NOTES For more information about secret vaults, see https://learn.microsoft.com/en-us/powershell/utility-modules/secretmanagement/overview?view=ps-modules #> [OutputType([Microsoft.PowerShell.SecretManagement.SecretVaultInfo])] [CmdletBinding()] param ( # The name of the secret vault. [Parameter()] [string] $Name = $script:Config.SecretVaultName, # The type of the secret vault. [Parameter()] [string] $Type = $script:Config.SecretVaultType ) $vault = Get-SecretVault | Where-Object { $_.ModuleName -eq $Type } if (-not $vault) { Write-Verbose "[$Type] - Configuring vault type" $vaultParameters = @{ Authentication = 'None' PasswordTimeout = -1 Interaction = 'None' Scope = 'CurrentUser' WarningAction = 'SilentlyContinue' Confirm = $false Force = $true } Reset-SecretStore @vaultParameters Write-Verbose "[$Type] - Done" Write-Verbose "[$Name] - Registering vault" $secretVault = @{ Name = $Name ModuleName = $Type DefaultVault = $true Description = 'SecretStore' } Register-SecretVault @secretVault Write-Verbose "[$Name] - Done" } else { Write-Verbose "[$Name] - Vault already registered" } Get-SecretVault | Where-Object { $_.ModuleName -eq $Type } } Write-Verbose "[$scriptName] - [functions] - [private] - [Initialize-SecretVault] - Done" #endregion - From [functions] - [private] - [Initialize-SecretVault] Write-Verbose "[$scriptName] - [functions] - [private] - Done" #endregion - From [functions] - [private] #region - From [functions] - [public] Write-Verbose "[$scriptName] - [functions] - [public] - Processing folder" #region - From [functions] - [public] - [Store] Write-Verbose "[$scriptName] - [functions] - [public] - [Store] - Processing folder" #region - From [functions] - [public] - [Store] - [Get-Store] Write-Verbose "[$scriptName] - [functions] - [public] - [Store] - [Get-Store] - Importing" function Get-Store { <# .SYNOPSIS Get a store from the vault. .DESCRIPTION Get a store from the vault. .EXAMPLE Get-Store -Name 'MySecret' Get the store called 'MySecret' from the vault. #> [OutputType([hashtable])] param ( # The name of the secret vault. [Parameter(Mandatory)] [string] $Name, # Set everything as plain text. [Parameter()] [switch] $AsPlainText ) $secretVault = Get-SecretVault | Where-Object { $_.Name -eq $script:Config.SecretVaultName } if (-not $secretVault) { return $null } $secretInfo = Get-SecretInfo -Vault $secretVault.Name | Where-Object { $_.Name -eq $Name } if (-not $secretInfo) { return $null } $metadata = $secretInfo | Select-Object -ExpandProperty Metadata $metadata + @{ Secret = Get-Secret -Name $Name -Vault $script:Config.SecretVaultName -AsPlainText:$AsPlainText } } Write-Verbose "[$scriptName] - [functions] - [public] - [Store] - [Get-Store] - Done" #endregion - From [functions] - [public] - [Store] - [Get-Store] #region - From [functions] - [public] - [Store] - [Remove-Store] Write-Verbose "[$scriptName] - [functions] - [public] - [Store] - [Remove-Store] - Importing" function Remove-Store { <# .SYNOPSIS Remove a store from the vault. .DESCRIPTION Remove a store from the vault. .EXAMPLE Remove-Store -Name 'MySecret' Removes the store called 'MySecret' from the vault. #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess)] param ( # The name of the secret vault. [Parameter(Mandatory)] [string] $Name ) if ($PSCmdlet.ShouldProcess('Remove-Secret', $Name)) { Get-SecretInfo | Where-Object { $_.Name -eq $Name } | ForEach-Object { Remove-Secret -Name $_.Name -Vault $script:Config.SecretVaultName } } } Write-Verbose "[$scriptName] - [functions] - [public] - [Store] - [Remove-Store] - Done" #endregion - From [functions] - [public] - [Store] - [Remove-Store] #region - From [functions] - [public] - [Store] - [Set-Store] Write-Verbose "[$scriptName] - [functions] - [public] - [Store] - [Set-Store] - Importing" function Set-Store { <# .SYNOPSIS Set a store in the vault. .DESCRIPTION If the store does not exist, it will be created. If it already exists, it will be updated. .EXAMPLE Set-Store -Name 'MySecret' Create a store called 'MySecret' in the vault. .EXAMPLE Set-Store -Name 'MySecret' -Secret 'MySecret' Creates a store called 'MySecret' in the vault with the secret. .EXAMPLE Set-Store -Name 'MySecret' -Secret 'MySecret' -Variables @{ 'Key' = 'Value' } Creates a store called 'MySecret' in the vault with the secret and variables. #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess)] param ( # The name of the store. [Parameter()] [string] $Name, # The secret of the store. [Parameter()] [object] $Secret = 'null', # The variables of the store. [Parameter()] [hashtable] $Variables ) $param = @{ Name = $Name Vault = $script:Config.SecretVaultName } #Map secret based on type, to Secret or SecureStringSecret if ($Secret -is [System.Security.SecureString]) { $param['SecureStringSecret'] = $Secret } elseif ($Secret -is [string]) { $param['Secret'] = $Secret } else { throw 'Invalid secret type' } if ($Variables) { $param.Metadata = $Variables } if ($PSCmdlet.ShouldProcess('Set-Secret', $param)) { Set-Secret @param } } Write-Verbose "[$scriptName] - [functions] - [public] - [Store] - [Set-Store] - Done" #endregion - From [functions] - [public] - [Store] - [Set-Store] Write-Verbose "[$scriptName] - [functions] - [public] - [Store] - Done" #endregion - From [functions] - [public] - [Store] #region - From [functions] - [public] - [StoreConfig] Write-Verbose "[$scriptName] - [functions] - [public] - [StoreConfig] - Processing folder" #region - From [functions] - [public] - [StoreConfig] - [Get-StoreConfig] Write-Verbose "[$scriptName] - [functions] - [public] - [StoreConfig] - [Get-StoreConfig] - Importing" #Requires -Modules Microsoft.PowerShell.SecretManagement function Get-StoreConfig { <# .SYNOPSIS Get a named value from the store. .DESCRIPTION Get a named value from the store. .EXAMPLE Get-StoreConfig -Name 'ApiBaseUri' -Store 'GitHub' Get the value of 'ApiBaseUri' config from the GitHub store. #> [OutputType([object])] [CmdletBinding()] param ( # Name of a value to get. [Parameter(Mandatory)] [string] $Name, # Return the value as plain text if it is a secret. [Parameter()] [switch] $AsPlainText, # The store to get the configuration from. [Parameter()] [string] $Store ) (Get-Store -Name $Store -AsPlainText:$AsPLainText).$Name } Write-Verbose "[$scriptName] - [functions] - [public] - [StoreConfig] - [Get-StoreConfig] - Done" #endregion - From [functions] - [public] - [StoreConfig] - [Get-StoreConfig] #region - From [functions] - [public] - [StoreConfig] - [Remove-StoreConfig] Write-Verbose "[$scriptName] - [functions] - [public] - [StoreConfig] - [Remove-StoreConfig] - Importing" function Remove-StoreConfig { <# .SYNOPSIS Remove a named value from the store. .DESCRIPTION Remove a named value from the store. .EXAMPLE Remove-StoreConfig -Name 'ApiBaseUri' -Store 'GitHub' Remove the ApiBaseUri value from the 'GitHub' store. #> [CmdletBinding(SupportsShouldProcess)] param ( # Name of a value to remove. [Parameter(Mandatory)] [string] $Name, # The store to remove the value from. [Parameter()] [string] $Store ) if ($PSCmdlet.ShouldProcess("config '$Name' from '$Store'", 'Remove')) { } Set-StoreConfig -Store $Store -Name $Name -Value $null } Write-Verbose "[$scriptName] - [functions] - [public] - [StoreConfig] - [Remove-StoreConfig] - Done" #endregion - From [functions] - [public] - [StoreConfig] - [Remove-StoreConfig] #region - From [functions] - [public] - [StoreConfig] - [Set-StoreConfig] Write-Verbose "[$scriptName] - [functions] - [public] - [StoreConfig] - [Set-StoreConfig] - Importing" #Requires -Modules Microsoft.PowerShell.SecretManagement function Set-StoreConfig { <# .SYNOPSIS Set a variables or secret. .DESCRIPTION Set a variable or secret in the store. To store a secret, set the name to 'Secret'. .EXAMPLE Set-StoreConfig -Name 'ApiBaseUri' -Value 'https://api.github.com' -Store 'GitHub' Sets a variable called 'ApiBaseUri' in the store called 'GitHub'. .EXAMPLE $secret = 'myAccessToken' | ConvertTo-SecureString -AsPlainText -Force Set-StoreConfig -Name 'Secret' -Value $secret -Store 'GitHub' Sets a secret called 'AccessToken' in the configuration store called 'GitHub'. #> [CmdletBinding(SupportsShouldProcess)] param ( # The name of a value to set. [Parameter(Mandatory)] [string] $Name, # The value to set. [Parameter(Mandatory)] [AllowNull()] [AllowEmptyString()] [object] $Value, # The name of the store. [Parameter(Mandatory)] [string] $Store ) $secretVault = Get-SecretVault | Where-Object { $_.Name -eq $script:Config.SecretVaultName } if (-not $secretVault) { throw "Vault '$($script:Config.SecretVaultName)' not found" } if ($PSCmdlet.ShouldProcess($Name, "Set value $Value]")) { if ($Name -eq 'Secret') { if ([string]::IsNullOrEmpty($Value)) { $Value = 'null' } if ($Value -is [SecureString]) { Set-Secret -Name $Store -SecureStringSecret $Value -Vault $script:Config.SecretVaultName } else { Set-Secret -Name $Store -Value $Value -Vault $script:Config.SecretVaultName } } else { $secretInfo = Get-SecretInfo -Vault $secretVault.Name | Where-Object { $_.Name -eq $Store } if (-not $secretInfo) { throw "Store '$Store' not found" } $metadata = ($secretInfo | Select-Object -ExpandProperty Metadata) + @{} if ([string]::IsNullOrEmpty($Value)) { $metadata.Remove($Name) } else { $metadata[$Name] = $Value } Set-SecretInfo -Name $Store -Metadata $metadata -Vault $script:Config.SecretVaultName } } } Write-Verbose "[$scriptName] - [functions] - [public] - [StoreConfig] - [Set-StoreConfig] - Done" #endregion - From [functions] - [public] - [StoreConfig] - [Set-StoreConfig] Write-Verbose "[$scriptName] - [functions] - [public] - [StoreConfig] - Done" #endregion - From [functions] - [public] - [StoreConfig] Write-Verbose "[$scriptName] - [functions] - [public] - Done" #endregion - From [functions] - [public] #region - From [variables] - [private] Write-Verbose "[$scriptName] - [variables] - [private] - Processing folder" #region - From [variables] - [private] - [Config] Write-Verbose "[$scriptName] - [variables] - [private] - [Config] - Importing" $script:Config = @{ Name = 'PSModule.Store' # $script:Config.Name SecretVaultName = 'SecretStore' # $script:Config.SecretVaultName SecretVaultType = 'Microsoft.PowerShell.SecretStore' # $script:Config.SecretVaultType } Write-Verbose "[$scriptName] - [variables] - [private] - [Config] - Done" #endregion - From [variables] - [private] - [Config] Write-Verbose "[$scriptName] - [variables] - [private] - Done" #endregion - From [variables] - [private] #region - From [loader] Write-Verbose "[$scriptName] - [loader] - Importing" ### This is the backend configuration for the functionality $initStoreParams = @{ Name = (Get-StoreConfig -Name SecretVaultName -Store $script:Config.Name) ?? $script:Config.SecretVaultName Type = (Get-StoreConfig -Name SecretVaultType -Store $script:Config.Name) ?? $script:Config.SecretVaultType } $vault = Initialize-SecretVault @initStoreParams $script:Config.SecretVaultName = $vault.Name $script:Config.SecretVaultType = $vault.ModuleName ### This is the store config for this module $storeParams = @{ Name = $script:Config.Name Variables = @{ SecretVaultName = $script:Config.SecretVaultName SecretVaultType = $script:Config.SecretVaultType } } Set-Store @storeParams Write-Verbose "[$scriptName] - [loader] - Done" #endregion - From [loader] $exports = @{ Alias = '*' Cmdlet = '' Function = @( 'Get-Store' 'Remove-Store' 'Set-Store' 'Get-StoreConfig' 'Remove-StoreConfig' 'Set-StoreConfig' ) } Export-ModuleMember @exports |