Context.psm1
[CmdletBinding()] param() $scriptName = 'Context' Write-Verbose "[$scriptName] - Importing module" #region - From [functions] - [private] Write-Verbose "[$scriptName] - [functions] - [private] - Processing folder" #region - From [functions] - [private] - [Get-ContextVault] Write-Verbose "[$scriptName] - [functions] - [private] - [Get-ContextVault] - Importing" #Requires -Modules @{ ModuleName = 'Microsoft.PowerShell.SecretManagement'; RequiredVersion = '1.1.2' } function Get-ContextVault { <# .SYNOPSIS Retrieves the context vault. .DESCRIPTION Connects to a context vault. If the vault name is not set in the configuration, it throws an error. If the specified vault is not found, it throws an error. Otherwise, it returns the secret vault object. .EXAMPLE Get-ContextVault This example retrieves the context vault. #> [CmdletBinding()] param() if (-not $script:Config.Context.VaultName) { throw 'Context vault name not set' } Write-Verbose "Connecting to context vault [$($script:Config.Context.VaultName)]" $secretVault = Get-SecretVault | Where-Object { $_.Name -eq $script:Config.Context.VaultName } if (-not $secretVault) { Write-Error $_ throw "Context vault [$($script:Config.Context.VaultName)] not found" } return $secretVault } Write-Verbose "[$scriptName] - [functions] - [private] - [Get-ContextVault] - Done" #endregion - From [functions] - [private] - [Get-ContextVault] #region - From [functions] - [private] - [Initialize-ContextVault] Write-Verbose "[$scriptName] - [functions] - [private] - [Initialize-ContextVault] - Importing" #Requires -Modules @{ ModuleName = 'Microsoft.PowerShell.SecretManagement'; RequiredVersion = '1.1.2' } #Requires -Modules @{ ModuleName = 'Microsoft.PowerShell.SecretStore'; RequiredVersion = '1.0.6' } function Initialize-ContextVault { <# .SYNOPSIS Initialize a context vault. .DESCRIPTION Initialize a context 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-ContextVault Initializes a context vault named 'ContextVault' using the 'Microsoft.PowerShell.SecretStore' module. #> [OutputType([Microsoft.PowerShell.SecretManagement.SecretVaultInfo])] [CmdletBinding()] param ( # The name of the secret vault. [Parameter()] [string] $Name = $script:Config.Context.VaultName, # The type of the secret vault. [Parameter()] [string] $Type = $script:Config.Context.VaultType ) $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-ContextVault] - Done" #endregion - From [functions] - [private] - [Initialize-ContextVault] 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] - [Context] Write-Verbose "[$scriptName] - [functions] - [public] - [Context] - Processing folder" #region - From [functions] - [public] - [Context] - [Get-Context] Write-Verbose "[$scriptName] - [functions] - [public] - [Context] - [Get-Context] - Importing" #Requires -Modules @{ ModuleName = 'Microsoft.PowerShell.SecretManagement'; RequiredVersion = '1.1.2' } function Get-Context { <# .SYNOPSIS Retrieves a context from the context vault. .DESCRIPTION Retrieves contexts from a specified context vault. You can specify the name of the context to retrieve or use a wildcard pattern to retrieve multiple contexts. If no name is specified, all contexts from the context vault will be retrieved. Optionally, you can choose to retrieve the contexts as plain text by providing the -AsPlainText switch. .EXAMPLE Get-Context Get all contexts from the context vault. .EXAMPLE Get-Context -Name 'MySecret' Get the context called 'MySecret' from the vault. .EXAMPLE Get-Context -Name 'My*' Get all contexts that match the pattern 'My*' from the vault. .EXAMPLE 'My*' | Get-Context Get all contexts that match the pattern 'My*' from the vault. #> [OutputType([hashtable])] [CmdletBinding()] param( # The name of the context to retrieve from the vault. Supports wildcard patterns. [Parameter( ValueFromPipeline, ValueFromPipelineByPropertyName )] [SupportsWildcards()] [Alias('Context', 'ContextName')] [string] $Name, # Switch to retrieve all the contexts secrets as plain text. [Parameter()] [switch] $AsPlainText ) begin { $filter = if ([string]::IsNullOrEmpty($PSBoundParameters.Name)) { '*' } else { $PSBoundParameters.Name } $Name = $($script:Config.Name) + $filter } process { $contextVault = Get-ContextVault Write-Verbose "Retrieving contexts from vault [$($contextVault.Name)] using pattern [$Name]" $contexts = Get-SecretInfo -Vault $contextVault.Name | Where-Object { $_.Name -like "$Name" } Write-Verbose "Found [$($contexts.Count)] contexts in context vault [$($contextVault.Name)]" foreach ($context in $contexts) { Get-Secret -Name $context.Name -Vault $contextVault.Name -AsPlainText:$AsPlainText } } } Register-ArgumentCompleter -CommandName Get-Context -ParameterName Name -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $null = $commandName, $parameterName, $commandAst, $fakeBoundParameter Get-SecretInfo -Vault $script:Config.Context.VaultName -Name "$($script:Config.Name)$wordToComplete*" -Verbose:$false | ForEach-Object { $contextName = $_.Name.Replace($script:Config.Name, '') [System.Management.Automation.CompletionResult]::new($contextName, $contextName, 'ParameterValue', $contextName) } } Write-Verbose "[$scriptName] - [functions] - [public] - [Context] - [Get-Context] - Done" #endregion - From [functions] - [public] - [Context] - [Get-Context] #region - From [functions] - [public] - [Context] - [Remove-Context] Write-Verbose "[$scriptName] - [functions] - [public] - [Context] - [Remove-Context] - Importing" #Requires -Modules @{ ModuleName = 'DynamicParams'; RequiredVersion = '1.1.8' } #Requires -Modules @{ ModuleName = 'Microsoft.PowerShell.SecretManagement'; RequiredVersion = '1.1.2' } filter Remove-Context { <# .SYNOPSIS Remove a context from the context vault. .DESCRIPTION This function removes a context from the vault. It supports removing a single context by name, multiple contexts using wildcard patterns, and can also accept input from the pipeline. If the specified context(s) exist, they will be removed from the vault. .EXAMPLE Remove-Context Removes all contexts from the vault. .EXAMPLE Remove-Context -Name 'MySecret' Removes the context called 'MySecret' from the vault. .EXAMPLE Remove-Context -Name 'MySe*' Removes the context called 'MySecret' from the vault. .EXAMPLE 'MySecret*' | Remove-Context Removes all contexts matching the pattern 'MySecret*' from the vault. .EXAMPLE Get-Context -Name 'MySecret*' | Remove-Context Retrieves all contexts matching the pattern 'MySecret*' and removes them from the vault. #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess)] param( # The name of the context to remove from the vault. Supports wildcard patterns. [Parameter( ValueFromPipeline, ValueFromPipelineByPropertyName )] [SupportsWildcards()] [Alias('Context', 'ContextName')] [string] $Name = '*' ) $contextVault = Get-ContextVault $contexts = Get-Context -Name $Name -AsPlainText Write-Verbose "Removing [$($contexts.count)] contexts from vault [$($contextVault.Name)]" foreach ($context in $contexts) { Write-Verbose "Removing context [$($context['Name'])]" $contextName = $($script:Config.Name) + $context['Name'] if ($PSCmdlet.ShouldProcess('Remove-Secret', $contextName)) { Write-Verbose "Removing secret [$contextName]" Remove-Secret -Name $contextName -Vault $contextVault.Name } } } Register-ArgumentCompleter -CommandName Remove-Context -ParameterName Name -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $null = $commandName, $parameterName, $commandAst, $fakeBoundParameter Get-Context -Name $wordToComplete | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) } } Write-Verbose "[$scriptName] - [functions] - [public] - [Context] - [Remove-Context] - Done" #endregion - From [functions] - [public] - [Context] - [Remove-Context] #region - From [functions] - [public] - [Context] - [Set-Context] Write-Verbose "[$scriptName] - [functions] - [public] - [Context] - [Set-Context] - Importing" #Requires -Modules @{ ModuleName = 'Microsoft.PowerShell.SecretManagement'; RequiredVersion = '1.1.2' } function Set-Context { <# .SYNOPSIS Set a context in the vault. .DESCRIPTION If the context does not exist, it will be created. If it already exists, it will be updated. .EXAMPLE Set-Context -Context @{ Name = 'MySecret' } Create a context called 'MySecret' in the vault. .EXAMPLE Set-Context -Context @{ Name = 'MySecret'; Key = 'Value' } Creates a context called 'MySecret' in the vault with the settings. #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess)] param( # The data of the context. [Parameter()] [hashtable] $Context = @{} ) if ([string]::IsNullOrEmpty($Context['Name'])) { throw 'The context must have a name.' } $contextVault = Get-ContextVault $param = @{ Name = $($script:Config.Name) + $Context['Name'] Secret = $Context Vault = $contextVault.Name } if ($PSCmdlet.ShouldProcess('Set-Secret', $param)) { Set-Secret @param } } Write-Verbose "[$scriptName] - [functions] - [public] - [Context] - [Set-Context] - Done" #endregion - From [functions] - [public] - [Context] - [Set-Context] Write-Verbose "[$scriptName] - [functions] - [public] - [Context] - Done" #endregion - From [functions] - [public] - [Context] #region - From [functions] - [public] - [ContextSetting] Write-Verbose "[$scriptName] - [functions] - [public] - [ContextSetting] - Processing folder" #region - From [functions] - [public] - [ContextSetting] - [Get-ContextSetting] Write-Verbose "[$scriptName] - [functions] - [public] - [ContextSetting] - [Get-ContextSetting] - Importing" #Requires -Modules @{ ModuleName = 'Microsoft.PowerShell.SecretManagement'; RequiredVersion = '1.1.2' } function Get-ContextSetting { <# .SYNOPSIS Retrieve a setting from a context. .DESCRIPTION This function retrieves a setting from a specified context. If the setting is a secret, it can be returned as plain text using the -AsPlainText switch. .EXAMPLE Get-ContextSetting -Context 'GitHub' -Name 'APIBaseUri' Get the value of the 'APIBaseUri' setting from the 'GitHub' context. #> [OutputType([object])] [CmdletBinding()] param ( # The context to get the configuration from. [Parameter(Mandatory)] [Alias('ContextName')] [string] $Context, # Name of a setting to get. [Parameter(Mandatory)] [string] $Name, # Return the setting as plain text if it is a secret. [Parameter()] [switch] $AsPlainText ) Write-Verbose "Getting settings for context: [$Context]" $contextObj = Get-Context -Name $Context -AsPlainText:$AsPlainText if (-not $contextObj) { throw "Context [$Context] not found" } Write-Verbose ($contextObj | Out-String) $contextObj[$Name] } Write-Verbose "[$scriptName] - [functions] - [public] - [ContextSetting] - [Get-ContextSetting] - Done" #endregion - From [functions] - [public] - [ContextSetting] - [Get-ContextSetting] #region - From [functions] - [public] - [ContextSetting] - [Remove-ContextSetting] Write-Verbose "[$scriptName] - [functions] - [public] - [ContextSetting] - [Remove-ContextSetting] - Importing" #Requires -Modules @{ ModuleName = 'DynamicParams'; RequiredVersion = '1.1.8' } filter Remove-ContextSetting { <# .SYNOPSIS Remove a setting from the context. .DESCRIPTION This function removes a setting from the specified context. It supports wildcard patterns for the name and does accept pipeline input. .PARAMETER Name Name of a setting to remove. .EXAMPLE Remove-ContextSetting -Name 'APIBaseUri' -Context 'GitHub' Remove the APIBaseUri setting from the 'GitHub' context. .EXAMPLE Get-ContextSetting -Context 'GitHub' | Remove-ContextSetting Remove all settings starting with 'API' from the 'GitHub' context. .EXAMPLE Remove-ContextSetting -Name 'API*' -Context 'GitHub' Remove all settings starting with 'API' from the 'GitHub' context. .EXAMPLE Get-ContextSetting -Context 'GitHub' | Where-Object { $_.Name -like 'API*' } | Remove-ContextSetting Remove all settings starting with 'API' from the 'GitHub' context using pipeline input. #> [CmdletBinding(SupportsShouldProcess)] param( # The name of the setting to remove. [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] [Alias('Setting')] [string] $Name, # The name of the context where the setting will be removed. [Parameter( Mandatory, ValueFromPipelineByPropertyName )] [Alias('ContextName')] [string] $Context ) if ($PSCmdlet.ShouldProcess('Target', "Remove value [$Name] from context [$($contextObj.Name)]")) { Set-ContextSetting -Name $Name -Value $null -Context $($Context) } } Write-Verbose "[$scriptName] - [functions] - [public] - [ContextSetting] - [Remove-ContextSetting] - Done" #endregion - From [functions] - [public] - [ContextSetting] - [Remove-ContextSetting] #region - From [functions] - [public] - [ContextSetting] - [Set-ContextSetting] Write-Verbose "[$scriptName] - [functions] - [public] - [ContextSetting] - [Set-ContextSetting] - Importing" #Requires -Modules @{ ModuleName = 'DynamicParams'; RequiredVersion = '1.1.8' } #Requires -Modules @{ ModuleName = 'Microsoft.PowerShell.SecretManagement'; RequiredVersion = '1.1.2' } function Set-ContextSetting { <# .SYNOPSIS Sets a setting in a context. .DESCRIPTION Sets a setting in the specified context. To store a secret, use the name 'Secret'. .EXAMPLE Set-ContextSetting -Name 'ApiBaseUri' -Value 'https://api.github.com' -Context 'GitHub' Sets a setting called 'ApiBaseUri' in the context called 'GitHub'. .EXAMPLE $secret = 'myAccessToken' | ConvertTo-SecureString -AsPlainText -Force Set-ContextSetting -Name 'Secret' -Value $secret -Context 'GitHub' Sets a secret in the configuration context called 'GitHub'. #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess)] param ( # The name of the setting to set. [Parameter(Mandatory)] [string] $Name, # The value to set for the specified setting. This can be a plain text string or a secure string. [Parameter(Mandatory)] [AllowNull()] [AllowEmptyString()] [object] $Value, # The name of the context where the setting will be set. [Parameter(Mandatory)] [Alias('ContextName')] [string] $Context ) $contextVault = Get-ContextVault $contextObj = Get-Context -Name $Context -AsPlainText if ($PSCmdlet.ShouldProcess($Name, "Set value [$Value]")) { Write-Verbose "Setting [$Name] to [$Value] in [$($contextObj.Name)]" if ([string]::IsNullOrEmpty($Value)) { Write-Verbose " - Removing [$Name] from metadata" $contextObj.Remove($Name) } else { Write-Verbose " - Setting [$Name] to [$Value] in metadata" $contextObj[$Name] = $Value } Write-Verbose "Updating context [$($contextObj.Name)] in vault [$($contextVault.Name)]" Set-Context -Context $contextObj } } Write-Verbose "[$scriptName] - [functions] - [public] - [ContextSetting] - [Set-ContextSetting] - Done" #endregion - From [functions] - [public] - [ContextSetting] - [Set-ContextSetting] Write-Verbose "[$scriptName] - [functions] - [public] - [ContextSetting] - Done" #endregion - From [functions] - [public] - [ContextSetting] 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 = [pscustomobject]@{ Name = 'Context:' # $script:Config.Name Context = [pscustomobject]@{ VaultName = 'SecretStore' # $script:Config.Context.VaultName VaultType = 'Microsoft.PowerShell.SecretStore' # $script:Config.Context.VaultType } } 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" try { $initContextParams = @{ Name = $script:Config.Context.VaultName Type = $script:Config.Context.VaultType } Initialize-ContextVault @initContextParams } catch { Write-Error $_ throw "Failed to initialize secret vault" } Write-Verbose "[$scriptName] - [loader] - Done" #endregion - From [loader] $exports = @{ Alias = '*' Cmdlet = '' Function = @( 'Get-Context' 'Remove-Context' 'Set-Context' 'Get-ContextSetting' 'Remove-ContextSetting' 'Set-ContextSetting' ) } Export-ModuleMember @exports |