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] - [JsonToObject] Write-Verbose "[$scriptName] - [functions] - [private] - [JsonToObject] - Processing folder" #region - From [functions] - [private] - [JsonToObject] - [Convert-ContextHashtableToObjectRecursive] Write-Verbose "[$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 ) $result = [pscustomobject]@{} foreach ($key in $Hashtable.Keys) { $value = $Hashtable[$key] Write-Debug "Processing [$key]" Write-Debug "Value type: $($value.GetType().FullName)" Write-Debug "Value: $value" 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 } Write-Verbose "[$scriptName] - [functions] - [private] - [JsonToObject] - [Convert-ContextHashtableToObjectRecursive] - Done" #endregion - From [functions] - [private] - [JsonToObject] - [Convert-ContextHashtableToObjectRecursive] #region - From [functions] - [private] - [JsonToObject] - [ConvertFrom-ContextJson] Write-Verbose "[$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(Mandatory)] [string] $JsonString ) $hashtableObject = $JsonString | ConvertFrom-Json -Depth 100 -AsHashtable return Convert-ContextHashtableToObjectRecursive $hashtableObject } Write-Verbose "[$scriptName] - [functions] - [private] - [JsonToObject] - [ConvertFrom-ContextJson] - Done" #endregion - From [functions] - [private] - [JsonToObject] - [ConvertFrom-ContextJson] Write-Verbose "[$scriptName] - [functions] - [private] - [JsonToObject] - Done" #endregion - From [functions] - [private] - [JsonToObject] #region - From [functions] - [private] - [ObjectToJSON] Write-Verbose "[$scriptName] - [functions] - [private] - [ObjectToJSON] - Processing folder" #region - From [functions] - [private] - [ObjectToJSON] - [Convert-ContextObjectToHashtableRecursive] Write-Verbose "[$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. [object] $Object ) $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) { $value = $property.Value Write-Debug "Processing [$($property.Name)]" Write-Debug "Value type: $($value.GetType().FullName)" 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 } Write-Verbose "[$scriptName] - [functions] - [private] - [ObjectToJSON] - [Convert-ContextObjectToHashtableRecursive] - Done" #endregion - From [functions] - [private] - [ObjectToJSON] - [Convert-ContextObjectToHashtableRecursive] #region - From [functions] - [private] - [ObjectToJSON] - [ConvertTo-ContextJson] Write-Verbose "[$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(Mandatory)] [object] $Context ) $processedObject = Convert-ContextObjectToHashtableRecursive $Context return ($processedObject | ConvertTo-Json -Depth 100 -Compress) } Write-Verbose "[$scriptName] - [functions] - [private] - [ObjectToJSON] - [ConvertTo-ContextJson] - Done" #endregion - From [functions] - [private] - [ObjectToJSON] - [ConvertTo-ContextJson] Write-Verbose "[$scriptName] - [functions] - [private] - [ObjectToJSON] - Done" #endregion - From [functions] - [private] - [ObjectToJSON] #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.VaultName) { throw 'Context vault name not set' } Write-Verbose "Connecting to context vault [$($script:Config.VaultName)]" $secretVault = Get-SecretVault | Where-Object { $_.Name -eq $script:Config.VaultName } if (-not $secretVault) { Write-Error $_ throw "Context vault [$($script:Config.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.VaultName, # The type of the secret vault. [Parameter()] [string] $Type = $script:Config.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' } filter Get-Context { <# .SYNOPSIS Retrieves a context from the context vault. .DESCRIPTION Retrieves a context from the context vault. If no name is specified, all contexts from the context vault will be retrieved. .EXAMPLE Get-Context Get all contexts from the context vault. .EXAMPLE Get-Context -ID 'MySecret' Get the context called 'MySecret' from the vault. #> [OutputType([pscustomobject])] [CmdletBinding()] param( # The name of the context to retrieve from the vault. [Parameter()] [SupportsWildcards()] [Alias('ContextID')] [string] $ID ) $contextVault = Get-ContextVault if (-not $PSBoundParameters.ContainsKey('ID')) { Write-Verbose "Retrieving all contexts from [$($contextVault.Name)]" $contexts = Get-SecretInfo -Vault $contextVault.Name | Select-Object -ExpandProperty Name } elseif ([string]::IsNullOrEmpty($ID)) { Write-Verbose "Return 0 contexts from [$($contextVault.Name)]" return } else { $ID = "$($script:Config.SecretPrefix)$ID" Write-Verbose "Retrieving context [$ID] from [$($contextVault.Name)]" $contexts = Get-SecretInfo -Vault $contextVault.Name -Name $ID | Select-Object -ExpandProperty Name } Write-Verbose "Found [$($contexts.Count)] contexts in [$($contextVault.Name)]" $contexts | ForEach-Object { Write-Verbose " - $_" $contextJson = Get-Secret -Name $_ -Vault $contextVault.Name -AsPlainText ConvertFrom-ContextJson -JsonString $contextJson } } 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 Removes 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 -ID 'MySecret' Removes the context called 'MySecret' from the vault. #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess)] param( # The name of the context to remove from the vault. [Parameter()] [Alias('ContextID')] [string] $ID ) $contextVault = Get-ContextVault $ID = "$($script:Config.SecretPrefix)$ID" $contextName = Get-SecretInfo -Vault $script:Config.VaultName -Name $ID | Select-Object -ExpandProperty Name Write-Verbose "Removing context [$ID] from [$($contextVault.Name)]" if ($PSCmdlet.ShouldProcess('Remove-Secret', $contextName)) { Write-Verbose "Removing secret [$contextName]" Remove-Secret -Name $contextName -Vault $contextVault.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 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. .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. #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess)] param( # The ID of the context. [Parameter(Mandatory)] [Alias('ContextID')] [string] $ID, # The data of the context. [Parameter(Mandatory)] [object] $Context ) $contextVault = Get-ContextVault $param = @{ Name = "$($script:Config.SecretPrefix)$ID" Secret = ConvertTo-ContextJson -Context $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. .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('ContextID')] [string] $ID, # Name of a setting to get. [Parameter(Mandatory)] [string] $Name ) $null = Get-ContextVault $context = Get-Context -ID $ID if (-not $context) { throw "Context [$ID] not found" } Write-Verbose "Returning setting: [$Name]" $context.$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)] [Alias('Setting')] [string] $Name, # The name of the context where the setting will be removed. [Parameter(Mandatory)] [Alias('ContextID')] [string] $ID ) $null = Get-ContextVault $context = Get-Context -ID $ID if (-not $context) { throw "Context [$ID] not found" } if ($PSCmdlet.ShouldProcess("[$($context.Name)]", "Remove [$Name]")) { Write-Verbose "Setting [$Name] in [$($context.Name)]" $context.PSObject.Properties.Remove($Name) Set-Context -Context $context -ID $ID } } 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. .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('ContextID')] [string] $ID ) $null = Get-ContextVault $context = Get-Context -ID $ID if (-not $context) { throw "Context [$ID] not found" } if ($PSCmdlet.ShouldProcess($Name, "Set value [$Value]")) { Write-Verbose "Setting [$Name] to [$Value] in [$($context.Name)]" if ($context.PSObject.Properties[$Name]) { $context.$Name = $Value } else { $context | Add-Member -NotePropertyName $Name -NotePropertyValue $Value -Force } Set-Context -Context $context -ID $ID } } 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]@{ SecretPrefix = 'Context:' # $script:Config.SecretPrefix VaultName = 'SecretStore' # $script:Config.VaultName VaultType = 'Microsoft.PowerShell.SecretStore' # $script:Config.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 { Initialize-ContextVault } 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 |