Context¶
Modules typically handle two types of data that benefit from persistent secure storage and management: module settings and user settings and secrets.
This module introduces the concept of Contexts
, which enable persistent and secure data storage for PowerShell modules. It allows module developers
to separate user and module data from the module code, enabling users to resume their work without needing to reconfigure the module or log in again,
provided the service supports session refresh mechanisms (e.g., refresh tokens).
The module uses NaCl-based encryption, provided by the libsodium
library (delivered via the Sodium
module),
to encrypt and decrypt Context
data. The Sodium
module is automatically installed when you install this
module.
What is a Context
?¶
The concept of Context
is widely used to represent a collection of data that is relevant to a specific use-case. In this module,
a Context
is a way to securely persist user and module data and offers a set of functions to manage this across modules that implement it.
Data that is stored in a Context
can include user-specific settings, secrets, and module configuration data.
A Context
is identified by a unique ID in the module that implements it.
Any data that can be represented in JSON format can be stored in a Context
.
Grouping Contexts into Vaults¶
Contexts can be grouped into ContextVaults
, which are logical containers for related contexts. This allows for organization and management of
contexts, especially when dealing with multiple users or modules. Vaults are automatically created when you store a context, and they can be managed
using the provided functions in this module.
Directory Structure¶
$HOME/.contextvaults/
├── GitHub/
│ ├── 64a5bbaf-96b8-4090-a77d-75e02ab6c4e0.json
│ ├── f201dc50-c163-4a7a-8d69-aea7f696737d.json
│ └── shard
├── AzureDevOps/
│ ├── cf49fceb-38d1-47da-a0ae-219ac40e4d8c.json
│ ├── b521a424-dd1c-445b-a0d6-c26a29d93654.json
│ └── shard
In this example there are two named vaults (GitHub
and AzureDevOps
). Each vault contains its own shard
file (for encryption) and two context
files (with unique GUID filenames) that store encrypted context data. Contexts in different vaults are completely isolated from each other.
1. Storing data (object or dictionary) to disk using Set-Context
¶
To store data to disk, use the Set-Context
function. The function needs an ID and the data object.
The object can be anything that can be converted and represented in JSON format.
Set-Context -ID 'john_doe' -Vault 'GitHub' -Context ([PSCustomObject]@{
Username = 'john_doe'
AuthToken = 'ghp_12345ABCDE67890FGHIJ' | ConvertTo-SecureString -AsPlainText -Force # gitleaks:allow
LoginTime = Get-Date
IsTwoFactorAuth = $true
TwoFactorMethods = @('TOTP', 'SMS')
})
2. The object is converted to JSON and prepared for encryption¶
The object that is passed into Set-Context
is first analyzed. If the object contains any SecureString
values, they are converted to plain-text and
prefixed with [SECURESTRING]
. This indicates that these values should be restored back to SecureString
. The whole object is then converted to a
JSON string.
{
"ID": "john_doe",
"Username": "john_doe",
"AuthToken": "[SECURESTRING]ghp_12345ABCDE67890FGHIJ",
"LoginTime": "2024-11-21T21:16:56.2518249+01:00",
"IsTwoFactorAuth": true,
"TwoFactorMethods": ["TOTP", "SMS"]
}
3. Storing the context object to disk¶
Finally the data is encrypted using the Sodium
module and saved to disk. The file is stored in the user's home
directory $HOME/.contextvaults/<VaultName>/<context_id>.json
, where <context_id>
is a generated GUID, providing a unique name for the file.
The encrypted JSON representation of the data is added to metadata object that holds other info such as the ID of the Context
and the path to the
file where it is stored.
{
"ID": "github.com/john_doe",
"Vault": "GitHub",
"Path": "C:\\Users\\JohnDoe\\.contextvaults\\GitHub\\d2edaa6e-95a1-41a0-b6ef-0ecc5d116030.json",
"Context": "0kGmtbQiEtih7 --< encrypted context data >-- ceqbMiBilUvEzO1Lk"
}
The metadata can be accessed using the Get-ContextInfo
function.
Installation¶
You can install the module from the PowerShell Gallery using the following command:
Install-PSResource -Name Context -TrustRepository -Repository PSGallery
Import-Module -Name Context
Implementation Guide for Module Developers¶
This section shows how to integrate the Context module into your PowerShell module to provide persistent, secure storage for module settings and user data.
Quick Start¶
The simplest way to implement Contexts is using Set-Context
, which automatically creates the vault if it doesn't exist:
# Store module configuration - vault is created automatically
Set-Context -ID 'ModuleSettings' -Vault 'MyModule' -Context @{
DefaultApiEndpoint = 'https://api.example.com'
TimeoutSeconds = 30
}
# Store user credentials
Set-Context -ID 'User.JohnDoe' -Vault 'MyModule' -Context @{
Username = 'johndoe'
ApiKey = 'secret-key' | ConvertTo-SecureString -AsPlainText -Force
LastLogin = Get-Date
}
Best Practices for Module Integration¶
1. Create Wrapper Functions¶
Create module-specific wrapper functions to provide a familiar interface for your users:
# In your module
function Set-MyModuleContext {
param(
[Parameter(Mandatory)]
[string] $ID,
[Parameter(Mandatory)]
[object] $Context
)
Set-Context -ID $ID -Vault 'MyModule' -Context $Context
}
function Get-MyModuleContext {
param(
[string] $ID = '*'
)
Get-Context -ID $ID -Vault 'MyModule'
}
2. Module Configuration Pattern¶
Store module-wide settings that persist across sessions:
# Initialize module settings on first load
if (-not (Get-Context -ID 'Settings' -Vault 'MyModule' -ErrorAction SilentlyContinue)) {
Set-Context -ID 'Settings' -Vault 'MyModule' -Context @{
DefaultUser = $null
ApiEndpoint = 'https://api.example.com'
EnableLogging = $false
}
}
3. User Context Pattern¶
Handle multiple user accounts within your module:
# Store user-specific data
function Connect-MyService {
param(
[Parameter(Mandatory)]
[string] $Username,
[Parameter(Mandatory)]
[SecureString] $ApiKey
)
# Store user context
Set-Context -ID "User.$Username" -Vault 'MyModule' -Context @{
Username = $Username
ApiKey = $ApiKey
ConnectedAt = Get-Date
}
# Update module settings to remember the current user
$moduleSettings = Get-Context -ID 'Settings' -Vault 'MyModule'
$moduleSettings.DefaultUser = $Username
Set-Context -ID 'Settings' -Vault 'MyModule' -Context $moduleSettings
}
Complete Example¶
Here's a complete example of how to implement Contexts in a hypothetical GitHub module:
# Module initialization (in .psm1 file)
$VaultName = 'GitHub'
# Initialize module settings if they don't exist
if (-not (Get-Context -ID 'ModuleSettings' -Vault $VaultName -ErrorAction SilentlyContinue)) {
Set-Context -ID 'ModuleSettings' -Vault $VaultName -Context @{
DefaultOrganization = $null
ApiEndpoint = 'https://api.github.com'
DefaultUser = $null
}
}
# Public function to connect user
function Connect-GitHub {
param(
[Parameter(Mandatory)]
[string] $Username,
[Parameter(Mandatory)]
[string] $Token
)
# Store user context with secure token
Set-Context -ID "User.$Username" -Vault $VaultName -Context @{
Username = $Username
Token = $Token | ConvertTo-SecureString -AsPlainText -Force
Organizations = @()
LastConnected = Get-Date
}
# Set as default user
$settings = Get-Context -ID 'ModuleSettings' -Vault $VaultName
$settings.DefaultUser = $Username
Set-Context -ID 'ModuleSettings' -Vault $VaultName -Context $settings
Write-Host "Connected to GitHub as $Username"
}
# Public function to get current user context
function Get-GitHubUser {
$settings = Get-Context -ID 'ModuleSettings' -Vault $VaultName
if ($settings.DefaultUser) {
Get-Context -ID "User.$($settings.DefaultUser)" -Vault $VaultName
}
}
Key Implementation Points¶
- Automatic Vault Creation:
Set-Context
creates the vault automatically if it doesn't exist, and preserves existing encryption keys - Consistent Vault Naming: Use your module name as the vault name for organization
- Wrapper Functions: Provide module-specific functions that hide the vault parameter from users
- SecureString Support: The Context module automatically handles
SecureString
encryption and decryption - Module Settings: Store module-wide configuration separate from user-specific data
Vault Management (Advanced)¶
For most use cases, you don't need to manage vaults directly since Set-Context
creates them automatically. However, you can manage vaults explicitly when needed:
# List all vaults
Get-ContextVault
# Get specific vault information
Get-ContextVault -Name "MyModule"
# Remove a vault and all its contexts (use with caution)
Remove-ContextVault -Name "OldModule"
Context Operations¶
Basic Operations¶
# Store a context (creates vault automatically)
Set-Context -ID 'UserSettings' -Vault 'MyModule' -Context @{
Theme = 'Dark'
Language = 'en-US'
}
# Retrieve a context
Get-Context -ID 'UserSettings' -Vault 'MyModule'
# Get all contexts in a vault
Get-Context -Vault 'MyModule'
# Remove a context
Remove-Context -ID 'UserSettings' -Vault 'MyModule'
# Rename a context
Rename-Context -ID 'OldName' -NewID 'NewName' -Vault 'MyModule'
# Get context metadata (without decrypting)
Get-ContextInfo -Vault 'MyModule'
Important Notes¶
- Vault Requirement: Every context must be stored in a named vault - there is no default vault
- Automatic Vault Creation:
Set-Context
automatically creates vaults if they don't exist - Encryption Key Preservation: Existing vault encryption keys are preserved when using
Set-Context
- Vault Isolation: Each vault is isolated with its own encryption keys and storage directory
- Storage Location: Vaults are stored under
$HOME/.contextvaults/<VaultName>/
- SecureString Support: The module automatically handles encryption/decryption of
SecureString
values