Context.psm1
[CmdletBinding()] param() $baseName = [System.IO.Path]::GetFileNameWithoutExtension($PSCommandPath) $script:PSModuleInfo = Test-ModuleManifest -Path "$PSScriptRoot\$baseName.psd1" $script:PSModuleInfo | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } $scriptName = $script:PSModuleInfo.Name Write-Debug "[$scriptName] - Importing module" #region [functions] - [private] Write-Debug "[$scriptName] - [functions] - [private] - Processing folder" #region [functions] - [private] - [Import-Context] Write-Debug "[$scriptName] - [functions] - [private] - [Import-Context] - Importing" #Requires -Modules @{ ModuleName = 'Microsoft.PowerShell.SecretManagement'; RequiredVersion = '1.1.2' } filter Import-Context { <# .SYNOPSIS Imports the context vault into memory. .DESCRIPTION Imports the context vault into memory. .EXAMPLE Import-Context Imports all contexts from the context vault into memory. #> [OutputType([object])] [CmdletBinding()] param() begin { $stackPath = Get-PSCallStackPath Write-Debug "[$stackPath] - Start" if (-not $script:Config.Initialized) { Set-ContextVault } } process { try { Write-Verbose "Importing contexts: [$($script:Config.SecretPrefix)*] from vault: $($script:Config.VaultName)" $secretInfos = Get-SecretInfo -Name "$($script:Config.SecretPrefix)*" -Vault $script:Config.VaultName -Verbose:$false Write-Verbose "Found [$($secretInfos.Count)] secrets" $secretInfos | ForEach-Object { Write-Verbose "- [$($_.Name)]" $secretJson = $_ | Get-Secret -AsPlainText -Verbose:$false $script:Contexts[$_.Name] = ConvertFrom-ContextJson -JsonString $secretJson } } catch { Write-Error $_ throw 'Failed to get context' } } end { Write-Debug "[$stackPath] - End" } } Write-Debug "[$scriptName] - [functions] - [private] - [Import-Context] - Done" #endregion [functions] - [private] - [Import-Context] #region [functions] - [private] - [Set-ContextVault] Write-Debug "[$scriptName] - [functions] - [private] - [Set-ContextVault] - Importing" #Requires -Modules @{ ModuleName = 'Microsoft.PowerShell.SecretManagement'; RequiredVersion = '1.1.2' } #Requires -Modules @{ ModuleName = 'Microsoft.PowerShell.SecretStore'; RequiredVersion = '1.0.6' } function Set-ContextVault { <# .SYNOPSIS Sets the context vault. .DESCRIPTION Sets the 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 Set-ContextVault Sets a context vault named 'ContextVault' using the 'Microsoft.PowerShell.SecretStore' module. .EXAMPLE Set-ContextVault -Name 'MyVault' -Type 'MyModule' Sets a context vault named 'MyVault' using the 'MyModule' module. .EXAMPLE Set-ContextVault -PassThru Sets a context vault using the default values and returns the secret vault object. #> [OutputType([Microsoft.PowerShell.SecretManagement.SecretVaultInfo])] [CmdletBinding(SupportsShouldProcess)] param ( # The name of the context vault. [Parameter()] [string] $Name = $script:Config.VaultName, # The type of the context vault. [Parameter()] [string] $Type = $script:Config.VaultType, # Pass the vault through the pipeline. [Parameter()] [switch] $PassThru ) begin { $stackPath = Get-PSCallStackPath Write-Debug "[$stackPath] - Start" } process { try { $vault = Get-SecretVault -Verbose:$false | Where-Object { $_.ModuleName -eq $Type } if (-not $vault) { Write-Debug "[$Type] - Configuring vault type" $vaultParameters = @{ Authentication = 'None' PasswordTimeout = -1 Interaction = 'None' Scope = 'CurrentUser' WarningAction = 'SilentlyContinue' Confirm = $false Force = $true Verbose = $false } if ($PSCmdlet.ShouldProcess('SecretStore', 'Reset')) { Reset-SecretStore @vaultParameters } Write-Debug "[$Type] - Done" Write-Debug "[$Name] - Registering vault" $secretVault = @{ Name = $Name ModuleName = $Type DefaultVault = $true Description = 'SecretStore' Verbose = $false } if ($PSCmdlet.ShouldProcess('SecretVault', 'Register')) { $vault = Register-SecretVault @secretVault -PassThru } Write-Debug "[$Name] - Done" } $script:Config.VaultName = $vault.Name Write-Debug "Connected to context vault [$($script:Config.VaultName)]" $script:Config.Initialized = $true } catch { Write-Error $_ throw 'Failed to initialize context vault' } if ($PassThru) { $vault } } end { Write-Debug "[$stackPath] - End" Import-Context } } Write-Debug "[$scriptName] - [functions] - [private] - [Set-ContextVault] - Done" #endregion [functions] - [private] - [Set-ContextVault] #region [functions] - [private] - [JsonToObject] Write-Debug "[$scriptName] - [functions] - [private] - [JsonToObject] - Processing folder" #region [functions] - [private] - [JsonToObject] - [Convert-ContextHashtableToObjectRecursive] Write-Debug "[$scriptName] - [functions] - [private] - [JsonToObject] - [Convert-ContextHashtableToObjectRecursive] - Importing" function Convert-ContextHashtableToObjectRecursive { <# .SYNOPSIS Converts a hashtable to a context object. .DESCRIPTION This function is used to convert a hashtable to a context object. String values that are prefixed with '[SECURESTRING]', are converted back to SecureString objects. Other values are converted to their original types, like ints, booleans, string, arrays, and nested objects. .EXAMPLE Convert-ContextHashtableToObjectRecursive -Hashtable @{ Name = 'Test' Token = '[SECURESTRING]TestToken' Nested = @{ Name = 'Nested' Token = '[SECURESTRING]NestedToken' } } This example converts a hashtable to a context object, where the 'Token' and 'Nested.Token' values are SecureString objects. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'The securestring is read from the object this function reads.' )] [OutputType([pscustomobject])] [CmdletBinding()] param ( # Hashtable to convert to context object [object] $Hashtable ) begin { $stackPath = Get-PSCallStackPath Write-Debug "[$stackPath] - Start" } process { try { $result = [pscustomobject]@{} foreach ($key in $Hashtable.Keys) { $value = $Hashtable[$key] Write-Debug "Processing [$key]" Write-Debug "Value: $value" Write-Debug "Type: $($value.GetType().Name)" if ($value -is [string] -and $value -like '`[SECURESTRING`]*') { Write-Debug "Converting [$key] as [SecureString]" $secureValue = $value -replace '^\[SECURESTRING\]', '' $result | Add-Member -NotePropertyName $key -NotePropertyValue ($secureValue | ConvertTo-SecureString -AsPlainText -Force) } elseif ($value -is [hashtable]) { Write-Debug "Converting [$key] as [hashtable]" $result | Add-Member -NotePropertyName $key -NotePropertyValue (Convert-ContextHashtableToObjectRecursive $value) } elseif ($value -is [array]) { Write-Debug "Converting [$key] as [IEnumerable], including arrays and hashtables" $result | Add-Member -NotePropertyName $key -NotePropertyValue @( $value | ForEach-Object { if ($_ -is [hashtable]) { Convert-ContextHashtableToObjectRecursive $_ } else { $_ } } ) } else { Write-Debug "Converting [$key] as regular value" $result | Add-Member -NotePropertyName $key -NotePropertyValue $value } } return $result } catch { Write-Error $_ throw 'Failed to convert hashtable to object' } } end { Write-Debug "[$stackPath] - End" } } Write-Debug "[$scriptName] - [functions] - [private] - [JsonToObject] - [Convert-ContextHashtableToObjectRecursive] - Done" #endregion [functions] - [private] - [JsonToObject] - [Convert-ContextHashtableToObjectRecursive] #region [functions] - [private] - [JsonToObject] - [ConvertFrom-ContextJson] Write-Debug "[$scriptName] - [functions] - [private] - [JsonToObject] - [ConvertFrom-ContextJson] - Importing" function ConvertFrom-ContextJson { <# .SYNOPSIS Converts a JSON string to a context object. .DESCRIPTION Converts a JSON string to a context object. [SECURESTRING] prefixed text is converted to SecureString objects. Other values are converted to their original types, like ints, booleans, string, arrays, and nested objects. .EXAMPLE ConvertFrom-ContextJson -JsonString '{ "Name": "Test", "Token": "[SECURESTRING]TestToken", "Nested": { "Name": "Nested", "Token": "[SECURESTRING]NestedToken" } }' This example converts a JSON string to a context object, where the 'Token' and 'Nested.Token' values are SecureString objects. #> [OutputType([pscustomobject])] [CmdletBinding()] param ( # JSON string to convert to context object [Parameter()] [string] $JsonString = '{}' ) begin { $stackPath = Get-PSCallStackPath Write-Debug "[$stackPath] - Start" } process { try { $hashtableObject = $JsonString | ConvertFrom-Json -Depth 100 -AsHashtable return Convert-ContextHashtableToObjectRecursive $hashtableObject } catch { Write-Error $_ throw 'Failed to convert JSON to object' } } end { Write-Debug "[$stackPath] - End" } } Write-Debug "[$scriptName] - [functions] - [private] - [JsonToObject] - [ConvertFrom-ContextJson] - Done" #endregion [functions] - [private] - [JsonToObject] - [ConvertFrom-ContextJson] Write-Debug "[$scriptName] - [functions] - [private] - [JsonToObject] - Done" #endregion [functions] - [private] - [JsonToObject] #region [functions] - [private] - [ObjectToJson] Write-Debug "[$scriptName] - [functions] - [private] - [ObjectToJson] - Processing folder" #region [functions] - [private] - [ObjectToJson] - [Convert-ContextObjectToHashtableRecursive] Write-Debug "[$scriptName] - [functions] - [private] - [ObjectToJson] - [Convert-ContextObjectToHashtableRecursive] - Importing" function Convert-ContextObjectToHashtableRecursive { <# .SYNOPSIS Converts a context object to a hashtable. .DESCRIPTION This function converts a context object to a hashtable. Secure strings are converted to a string representation, prefixed with '[SECURESTRING]'. Datetime objects are converted to a string representation using the 'o' format specifier. Nested context objects are recursively converted to hashtables. .EXAMPLE Convert-ContextObjectToHashtableRecursive -Object ([PSCustomObject]@{ Name = 'MySecret' AccessToken = '123123123' | ConvertTo-SecureString -AsPlainText -Force Nested = @{ Name = 'MyNestedSecret' NestedAccessToken = '123123123' | ConvertTo-SecureString -AsPlainText -Force } }) Converts the context object to a hashtable. Converts the AccessToken and NestedAccessToken secure strings to a string representation. #> [OutputType([hashtable])] [CmdletBinding()] param ( # The object to convert. [Parameter()] [object] $Object = @{} ) begin { $stackPath = Get-PSCallStackPath Write-Debug "[$stackPath] - Start" } process { try { $result = @{} if ($Object -is [hashtable]) { Write-Debug 'Converting [hashtable] to [PSCustomObject]' $Object = [PSCustomObject]$Object } elseif ($Object -is [string] -or $Object -is [int] -or $Object -is [bool]) { Write-Debug 'returning as string' return $Object } foreach ($property in $Object.PSObject.Properties) { $name = $property.Name $value = $property.Value Write-Debug "Processing [$name]" Write-Debug "Value: $value" Write-Debug "Type: $($value.GetType().Name)" if ($value -is [datetime]) { Write-Debug '- as DateTime' $result[$property.Name] = $value.ToString('o') } elseif ($value -is [string] -or $Object -is [int] -or $Object -is [bool]) { Write-Debug '- as string, int, bool' $result[$property.Name] = $value } elseif ($value -is [System.Security.SecureString]) { Write-Debug '- as SecureString' $value = $value | ConvertFrom-SecureString -AsPlainText $result[$property.Name] = "[SECURESTRING]$value" } elseif ($value -is [psobject] -or $value -is [PSCustomObject] -or $value -is [hashtable]) { Write-Debug '- as PSObject, PSCustomObject or hashtable' $result[$property.Name] = Convert-ContextObjectToHashtableRecursive $value } elseif ($value -is [System.Collections.IEnumerable]) { Write-Debug '- as IEnumerable, including arrays and hashtables' $result[$property.Name] = @( $value | ForEach-Object { Convert-ContextObjectToHashtableRecursive $_ } ) } else { Write-Debug '- as regular value' $result[$property.Name] = $value } } return $result } catch { Write-Error $_ throw 'Failed to convert context object to hashtable' } } end { Write-Debug "[$stackPath] - End" } } Write-Debug "[$scriptName] - [functions] - [private] - [ObjectToJson] - [Convert-ContextObjectToHashtableRecursive] - Done" #endregion [functions] - [private] - [ObjectToJson] - [Convert-ContextObjectToHashtableRecursive] #region [functions] - [private] - [ObjectToJson] - [ConvertTo-ContextJson] Write-Debug "[$scriptName] - [functions] - [private] - [ObjectToJson] - [ConvertTo-ContextJson] - Importing" function ConvertTo-ContextJson { <# .SYNOPSIS Takes an object and converts it to a JSON string. .DESCRIPTION Takes objects or hashtables and converts them to a JSON string. SecureStrings are converted to plain text strings and prefixed with [SECURESTRING]. The conversion is recursive for any nested objects. Use ConvertFrom-ContextJson to convert back to an object. .EXAMPLE ConvertTo-ContextJson -Context ([pscustomobject]@{ Name = 'MySecret' AccessToken = '123123123' | ConvertTo-SecureString -AsPlainText -Force }) Returns a JSON string representation of the object. ```json { "Name": "MySecret", "AccessToken ": "[SECURESTRING]123123123" } ``` #> [OutputType([string])] [CmdletBinding()] param ( # The object to convert to a Context JSON string. [Parameter()] [object] $Context = @{}, # The ID of the context. [Parameter(Mandatory)] [string] $ID ) begin { $stackPath = Get-PSCallStackPath Write-Debug "[$stackPath] - Start" } process { try { $processedObject = Convert-ContextObjectToHashtableRecursive $Context $processedObject['ID'] = $ID return ($processedObject | ConvertTo-Json -Depth 100 -Compress) } catch { Write-Error $_ throw 'Failed to convert object to JSON' } } end { Write-Debug "[$stackPath] - End" } } Write-Debug "[$scriptName] - [functions] - [private] - [ObjectToJson] - [ConvertTo-ContextJson] - Done" #endregion [functions] - [private] - [ObjectToJson] - [ConvertTo-ContextJson] Write-Debug "[$scriptName] - [functions] - [private] - [ObjectToJson] - Done" #endregion [functions] - [private] - [ObjectToJson] #region [functions] - [private] - [Utilities] Write-Debug "[$scriptName] - [functions] - [private] - [Utilities] - Processing folder" #region [functions] - [private] - [Utilities] - [PowerShell] Write-Debug "[$scriptName] - [functions] - [private] - [Utilities] - [PowerShell] - Processing folder" #region [functions] - [private] - [Utilities] - [PowerShell] - [Get-PSCallStackPath] Write-Debug "[$scriptName] - [functions] - [private] - [Utilities] - [PowerShell] - [Get-PSCallStackPath] - Importing" function Get-PSCallStackPath { <# .SYNOPSIS Create a string representation of the current call stack. .DESCRIPTION This function creates a string representation of the current call stack. You can use the SkipFirst and SkipLatest parameters to skip the first and last. By default it will skip the first (what called the initial function, typically <ScriptBlock>), and the last (the current function, Get-PSCallStackPath). .EXAMPLE Get-PSCallStackPath First-Function\Second-Function\Third-Function Shows the call stack of the last function called, Third-Function, with the first (<ScriptBlock>) and last (Get-PSCallStackPath) functions removed. .EXAMPLE Get-PSCallStackPath -SkipFirst 0 <ScriptBlock>\First-Function\Second-Function\Third-Function Shows the call stack of the last function called, Third-Function, with the first function included (typically <ScriptBlock>). .EXAMPLE Get-PSCallStackPath -SkipLatest 0 First-Function\Second-Function\Third-Function\Get-PSCallStackPath Shows the call stack of the last function called, Third-Function, with the last function included (Get-PSCallStackPath). #> [CmdletBinding()] param( # Number of the functions to skip from the last function called. # Last function is this function, Get-PSCallStackPath. [Parameter()] [int] $SkipLatest = 1, # Number of the functions to skip from the first function called. # First function is typically <ScriptBlock>. [Parameter()] [int] $SkipFirst = 1 ) $skipFirst++ $cmds = (Get-PSCallStack).Command $functionPath = $cmds[($cmds.Count - $skipFirst)..$SkipLatest] -join '\' $functionPath = $functionPath -replace '^.*<ScriptBlock>\\' $functionPath = $functionPath -replace '^.*.ps1\\' return $functionPath } Write-Debug "[$scriptName] - [functions] - [private] - [Utilities] - [PowerShell] - [Get-PSCallStackPath] - Done" #endregion [functions] - [private] - [Utilities] - [PowerShell] - [Get-PSCallStackPath] Write-Debug "[$scriptName] - [functions] - [private] - [Utilities] - [PowerShell] - Done" #endregion [functions] - [private] - [Utilities] - [PowerShell] Write-Debug "[$scriptName] - [functions] - [private] - [Utilities] - Done" #endregion [functions] - [private] - [Utilities] Write-Debug "[$scriptName] - [functions] - [private] - Done" #endregion [functions] - [private] #region [functions] - [public] Write-Debug "[$scriptName] - [functions] - [public] - Processing folder" #region [functions] - [public] - [Get-Context] Write-Debug "[$scriptName] - [functions] - [public] - [Get-Context] - Importing" function Get-Context { <# .SYNOPSIS Gets a context. .DESCRIPTION Gets a context from the loaded contexts that are in memory. If no name is specified, all contexts will be returned. .EXAMPLE Get-Context Get all contexts from the context vault (in memory). .EXAMPLE Get-Context -ID 'MySecret' Get the context called 'MySecret' from the context vault (in memory). .EXAMPLE Get-Context -ID 'My*' Get all contexts that start with 'My' from the context vault (in memory). .LINK https://psmodule.io/Context/Functions/Get-Context/ #> [OutputType([object])] [CmdletBinding()] param( # The name of the context to retrieve from the vault. [Parameter( ValueFromPipeline, ValueFromPipelineByPropertyName )] [AllowEmptyString()] [SupportsWildcards()] [string[]] $ID = '*' ) begin { $stackPath = Get-PSCallStackPath Write-Debug "[$stackPath] - Start" if (-not $script:Config.Initialized) { Set-ContextVault } } process { try { foreach ($item in $ID) { Write-Debug "Retrieving contexts like [$item]" $script:Contexts.Values | Where-Object { $_.ID -like $item } } } catch { Write-Error $_ throw 'Failed to get context' } } end { Write-Debug "[$stackPath] - End" } } Write-Debug "[$scriptName] - [functions] - [public] - [Get-Context] - Done" #endregion [functions] - [public] - [Get-Context] #region [functions] - [public] - [Remove-Context] Write-Debug "[$scriptName] - [functions] - [public] - [Remove-Context] - Importing" #Requires -Modules @{ ModuleName = 'Microsoft.PowerShell.SecretManagement'; RequiredVersion = '1.1.2' } function Remove-Context { <# .SYNOPSIS Removes a context from the context vault. .DESCRIPTION This function removes a context (or multiple contexts) from the vault. It supports: - Supply one or more IDs as strings (e.g. -ID 'Ctx1','Ctx2') - Supply objects that contain an ID property .EXAMPLE Remove-Context -ID 'MySecret' Removes a context called 'MySecret' by specifying its ID .EXAMPLE Remove-Context -ID 'Ctx1','Ctx2' Removes two contexts, 'Ctx1' and 'Ctx2' .EXAMPLE 'Ctx1','Ctx2' | Remove-Context Removes two contexts, 'Ctx1' and 'Ctx2' .EXAMPLE $ctxList = @( [PSCustomObject]@{ ID = 'Ctx1' }, [PSCustomObject]@{ ID = 'Ctx2' } ) $ctxList | Remove-Context Accepts pipeline input: multiple objects each having an ID property .LINK https://psmodule.io/Context/Functions/Remove-Context/ #> [CmdletBinding(SupportsShouldProcess)] param( # One or more IDs as string of the contexts to remove. [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] [string[]] $ID ) begin { $stackPath = Get-PSCallStackPath Write-Debug "[$stackPath] - Begin" if (-not $script:Config.Initialized) { Set-ContextVault } } process { try { foreach ($item in $ID) { Write-Debug "Processing ID [$item]" $script:Contexts.Values.ID | Where-Object { $_ -like $item } | ForEach-Object { $name = "$($script:Config.SecretPrefix)$_" Write-Debug "Removing context [$name]" if ($PSCmdlet.ShouldProcess($name, 'Remove secret')) { Get-SecretInfo -Name $name -Vault $script:Config.VaultName | Remove-Secret $null = $script:Contexts.Remove($name) } } } } catch { Write-Error $_ throw 'Failed to remove context' } } end { Write-Debug "[$stackPath] - End" } } Write-Debug "[$scriptName] - [functions] - [public] - [Remove-Context] - Done" #endregion [functions] - [public] - [Remove-Context] #region [functions] - [public] - [Rename-Context] Write-Debug "[$scriptName] - [functions] - [public] - [Rename-Context] - Importing" function Rename-Context { <# .SYNOPSIS Renames a context. .DESCRIPTION This function renames a context. It retrieves the context with the old ID, sets the context with the new ID, and removes the context with the old ID. .EXAMPLE Rename-Context -ID 'PSModule.GitHub' -NewID 'PSModule.GitHub2' Renames the context 'PSModule.GitHub' to 'PSModule.GitHub2'. .EXAMPLE 'PSModule.GitHub' | Rename-Context -NewID 'PSModule.GitHub2' Renames the context 'PSModule.GitHub' to 'PSModule.GitHub2'. .LINK https://psmodule.io/Context/Functions/Rename-Context/ #> [CmdletBinding(SupportsShouldProcess)] param ( # The ID of the context to rename. [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] [string] $ID, # The new ID of the context. [Parameter(Mandatory)] [string] $NewID, # Force the rename even if the new ID already exists. [Parameter()] [switch] $Force ) begin { $stackPath = Get-PSCallStackPath Write-Debug "[$stackPath] - Start" if (-not $script:Config.Initialized) { Set-ContextVault } } process { $context = Get-Context -ID $ID if (-not $context) { throw "Context with ID '$ID' not found." } $existingContext = Get-Context -ID $NewID if ($existingContext -and -not $Force) { throw "Context with ID '$NewID' already exists." } if ($PSCmdlet.ShouldProcess("Renaming context '$ID' to '$NewID'")) { try { $context | Set-Context -ID $NewID } catch { Write-Error $_ throw 'Failed to set new context' } try { Remove-Context -ID $ID } catch { Write-Error $_ throw 'Failed to remove old context' } } } end { Write-Debug "[$stackPath] - End" } } Write-Debug "[$scriptName] - [functions] - [public] - [Rename-Context] - Done" #endregion [functions] - [public] - [Rename-Context] #region [functions] - [public] - [Set-Context] Write-Debug "[$scriptName] - [functions] - [public] - [Set-Context] - Importing" #Requires -Modules @{ ModuleName = 'Microsoft.PowerShell.SecretManagement'; RequiredVersion = '1.1.2' } function Set-Context { <# .SYNOPSIS Set a context and store it in the context vault. .DESCRIPTION If the context does not exist, it will be created. If it already exists, it will be updated. The context is cached in memory for faster access. .EXAMPLE Set-Context -ID 'PSModule.GitHub' -Context @{ Name = 'MySecret' } Create a context called 'MySecret' in the vault. .EXAMPLE Set-Context -ID 'PSModule.GitHub' -Context @{ Name = 'MySecret'; AccessToken = '123123123' } Creates a context called 'MySecret' in the vault with the settings. .LINK https://psmodule.io/Context/Functions/Set-Context/ #> [OutputType([object])] [CmdletBinding(SupportsShouldProcess)] param( # The ID of the context. [Parameter(ValueFromPipelineByPropertyName)] [string] $ID, # The data of the context. [Parameter(ValueFromPipeline)] [object] $Context = @{}, # Pass the context through the pipeline. [Parameter()] [switch] $PassThru ) begin { $stackPath = Get-PSCallStackPath Write-Debug "[$stackPath] - Start" if (-not $script:Config.Initialized) { Set-ContextVault } } process { if (-not $ID) { $ID = $Context.ID } if (-not $ID) { throw 'ID is required in either the ID parameter or the Context object' } try { $secret = ConvertTo-ContextJson -Context $Context -ID $ID } catch { Write-Error $_ throw 'Failed to convert context to JSON' } $Name = "$($script:Config.SecretPrefix)$ID" $param = @{ Name = $Name Secret = $secret Vault = $script:Config.VaultName } Write-Debug ($param | ConvertTo-Json -Depth 5) try { if ($PSCmdlet.ShouldProcess($Name, 'Set Secret')) { Set-Secret @param $data = ConvertFrom-ContextJson -JsonString $secret $script:Contexts[$Name] = $data } } catch { Write-Error $_ throw 'Failed to set secret' } if ($PassThru) { $data } } end { Write-Debug "[$stackPath] - End" } } Write-Debug "[$scriptName] - [functions] - [public] - [Set-Context] - Done" #endregion [functions] - [public] - [Set-Context] Write-Debug "[$scriptName] - [functions] - [public] - Done" #endregion [functions] - [public] #region [variables] - [private] Write-Debug "[$scriptName] - [variables] - [private] - Processing folder" #region [variables] - [private] - [Config] Write-Debug "[$scriptName] - [variables] - [private] - [Config] - Importing" $script:Config = [pscustomobject]@{ Initialized = $false # $script:Config.Initialized SecretPrefix = 'Context:' # $script:Config.SecretPrefix VaultName = 'ContextVault' # $script:Config.VaultName VaultType = 'Microsoft.PowerShell.SecretStore' # $script:Config.VaultType } Write-Debug "[$scriptName] - [variables] - [private] - [Config] - Done" #endregion [variables] - [private] - [Config] #region [variables] - [private] - [Contexts] Write-Debug "[$scriptName] - [variables] - [private] - [Contexts] - Importing" $script:Contexts = [System.Collections.Generic.Dictionary[string, [PSCustomObject]]]::new() Write-Debug "[$scriptName] - [variables] - [private] - [Contexts] - Done" #endregion [variables] - [private] - [Contexts] Write-Debug "[$scriptName] - [variables] - [private] - Done" #endregion [variables] - [private] #region [completers] Write-Debug "[$scriptName] - [completers] - Importing" Register-ArgumentCompleter -CommandName 'Get-Context', 'Set-Context', 'Remove-Context', 'Rename-Context' -ParameterName 'ID' -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter $script:Contexts.Values.ID | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Write-Debug "[$scriptName] - [completers] - Done" #endregion [completers] #region [main] Write-Debug "[$scriptName] - [main] - Importing" Set-ContextVault Write-Debug "[$scriptName] - [main] - Done" #endregion [main] #region Member exporter $exports = @{ Alias = '*' Cmdlet = '' Function = @( 'Get-Context' 'Remove-Context' 'Rename-Context' 'Set-Context' ) } Export-ModuleMember @exports #endregion Member exporter |