From powershell-master
Implements PowerShell security practices like SecretManagement for credential storage, Azure Key Vault integration, JEA, and WDAC. Useful for secure automation and script hardening.
npx claudepluginhub josiahsiegel/claude-plugin-marketplace --plugin powershell-masterThis skill uses the workspace's default tool permissions.
Modern security practices for PowerShell scripts and automation, including credential management, SecretManagement module, and hardening techniques.
Creates isolated Git worktrees for feature branches with prioritized directory selection, gitignore safety checks, auto project setup for Node/Python/Rust/Go, and baseline verification.
Executes implementation plans in current session by dispatching fresh subagents per independent task, with two-stage reviews: spec compliance then code quality.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
Modern security practices for PowerShell scripts and automation, including credential management, SecretManagement module, and hardening techniques.
Microsoft.PowerShell.SecretManagement is the official solution for secure credential storage in PowerShell.
Why use SecretManagement:
# Install SecretManagement module
Install-Module -Name Microsoft.PowerShell.SecretManagement -Scope CurrentUser
# Install vault provider (choose one or more)
Install-Module -Name Microsoft.PowerShell.SecretStore # Local encrypted vault
Install-Module -Name Az.KeyVault # Azure Key Vault
Install-Module -Name SecretManagement.KeePass # KeePass integration
# Register a vault
Register-SecretVault -Name LocalVault -ModuleName Microsoft.PowerShell.SecretStore
# Store a secret
$password = Read-Host -AsSecureString -Prompt "Enter password"
Set-Secret -Name "DatabasePassword" -Secret $password -Vault LocalVault
# Retrieve a secret
$dbPassword = Get-Secret -Name "DatabasePassword" -Vault LocalVault -AsPlainText
# Or as SecureString
$dbPasswordSecure = Get-Secret -Name "DatabasePassword" -Vault LocalVault
# List secrets
Get-SecretInfo
# Remove a secret
Remove-Secret -Name "DatabasePassword" -Vault LocalVault
# Install and import Az.KeyVault
Install-Module -Name Az.KeyVault -Scope CurrentUser
Import-Module Az.KeyVault
# Authenticate to Azure
Connect-AzAccount
# Register Azure Key Vault as secret vault
Register-SecretVault -Name AzureKV `
-ModuleName Az.KeyVault `
-VaultParameters @{
AZKVaultName = 'MyKeyVault'
SubscriptionId = 'your-subscription-id'
}
# Store secret in Azure Key Vault
Set-Secret -Name "ApiKey" -Secret "your-api-key" -Vault AzureKV
# Retrieve from Azure Key Vault
$apiKey = Get-Secret -Name "ApiKey" -Vault AzureKV -AsPlainText
<#
.SYNOPSIS
Secure automation script using SecretManagement
.DESCRIPTION
Demonstrates secure credential handling without hardcoded secrets
#>
#Requires -Modules Microsoft.PowerShell.SecretManagement
[CmdletBinding()]
param()
# Retrieve credentials from vault
$dbConnectionString = Get-Secret -Name "SQLConnectionString" -AsPlainText
$apiToken = Get-Secret -Name "APIToken" -AsPlainText
# Use credentials securely
try {
# Database operation
$connection = New-Object System.Data.SqlClient.SqlConnection($dbConnectionString)
$connection.Open()
# API call with token
$headers = @{ Authorization = "Bearer $apiToken" }
$response = Invoke-RestMethod -Uri "https://api.example.com/data" -Headers $headers
# Process results
Write-Host "Operation completed successfully"
}
catch {
Write-Error "Operation failed: $_"
}
finally {
if ($connection) { $connection.Close() }
}
# ❌ WRONG - Hardcoded credentials
$password = "MyPassword123"
$username = "admin"
# ❌ WRONG - Plaintext in script
$cred = New-Object System.Management.Automation.PSCredential("admin", "password")
# ✅ CORRECT - SecretManagement
$password = Get-Secret -Name "AdminPassword" -AsPlainText
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential("admin", $securePassword)
# ✅ CORRECT - Interactive prompt (for manual runs)
$cred = Get-Credential -Message "Enter admin credentials"
# ✅ CORRECT - Managed Identity (Azure automation)
Connect-AzAccount -Identity
# Store service principal credentials in vault
Set-Secret -Name "AzureAppId" -Secret "app-id-guid"
Set-Secret -Name "AzureAppSecret" -Secret "app-secret-value"
Set-Secret -Name "AzureTenantId" -Secret "tenant-id-guid"
# Retrieve and authenticate
$appId = Get-Secret -Name "AzureAppId" -AsPlainText
$appSecret = Get-Secret -Name "AzureAppSecret" -AsPlainText
$tenantId = Get-Secret -Name "AzureTenantId" -AsPlainText
$secureSecret = ConvertTo-SecureString $appSecret -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($appId, $secureSecret)
Connect-AzAccount -ServicePrincipal -Credential $credential -Tenant $tenantId
Just Enough Administration restricts PowerShell remoting sessions to specific cmdlets and parameters.
# 1. Create role capability file
New-PSRoleCapabilityFile -Path "C:\JEA\RestartServices.psrc" `
-VisibleCmdlets @{
Name = 'Restart-Service'
Parameters = @{
Name = 'Name'
ValidateSet = 'Spooler', 'W32Time', 'WinRM'
}
}, 'Get-Service'
# 2. Create session configuration file
New-PSSessionConfigurationFile -Path "C:\JEA\RestartServices.pssc" `
-SessionType RestrictedRemoteServer `
-RoleDefinitions @{
'DOMAIN\ServiceAdmins' = @{ RoleCapabilities = 'RestartServices' }
} `
-LanguageMode NoLanguage
# 3. Register JEA endpoint
Register-PSSessionConfiguration -Name RestartServices `
-Path "C:\JEA\RestartServices.pssc" `
-Force
# 4. Connect to JEA endpoint (as delegated user)
Enter-PSSession -ComputerName Server01 -ConfigurationName RestartServices
# User can ONLY run allowed commands
Restart-Service -Name Spooler # ✅ Allowed
Restart-Service -Name DNS # ❌ Denied (not in ValidateSet)
Get-Process # ❌ Denied (not visible)
# Enable transcription and logging
New-PSSessionConfigurationFile -Path "C:\JEA\AuditedSession.pssc" `
-SessionType RestrictedRemoteServer `
-TranscriptDirectory "C:\JEA\Transcripts" `
-RunAsVirtualAccount
# All JEA sessions are transcribed to C:\JEA\Transcripts
# Review audit logs
Get-ChildItem "C:\JEA\Transcripts" | Get-Content
WDAC replaces AppLocker for controlling which PowerShell scripts can execute.
# Create WDAC policy for signed scripts only
New-CIPolicy -FilePath "C:\WDAC\PowerShellPolicy.xml" `
-ScanPath "C:\Scripts" `
-Level FilePublisher `
-Fallback Hash `
-UserPEs
# Allow only signed scripts
Set-RuleOption -FilePath "C:\WDAC\PowerShellPolicy.xml" `
-Option 3 # Required WHQL
# Convert to binary policy
ConvertFrom-CIPolicy -XmlFilePath "C:\WDAC\PowerShellPolicy.xml" `
-BinaryFilePath "C:\Windows\System32\CodeIntegrity\SIPolicy.p7b"
# Reboot to apply policy
Restart-Computer
# Get code signing certificate
$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert
# Sign script
Set-AuthenticodeSignature -FilePath "C:\Scripts\MyScript.ps1" -Certificate $cert
# Verify signature
$signature = Get-AuthenticodeSignature -FilePath "C:\Scripts\MyScript.ps1"
$signature.Status # Should be "Valid"
# Check current execution policy
Get-ExecutionPolicy
# Set execution policy (requires admin)
Set-ExecutionPolicy RemoteSigned -Scope LocalMachine
# Bypass for single script (testing only)
PowerShell.exe -ExecutionPolicy Bypass -File "script.ps1"
Restricts PowerShell language features to prevent malicious code execution.
# Check current language mode
$ExecutionContext.SessionState.LanguageMode
# Output: FullLanguage (admin) or ConstrainedLanguage (standard user)
# Set system-wide constrained language mode
# Via Environment Variable or Group Policy
# Set: __PSLockdownPolicy = 4
# Test constrained mode behavior
# FullLanguage allows:
[System.Net.WebClient]::new() # ✅ Allowed
# ConstrainedLanguage blocks:
[System.Net.WebClient]::new() # ❌ Blocked
Add-Type -TypeDefinition "..." # ❌ Blocked
# Enable via Group Policy or Registry
# HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging
New-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" `
-Name "EnableScriptBlockLogging" -Value 1 -PropertyType DWord
# Log location: Windows Event Log
# Event Viewer > Applications and Services Logs > Microsoft > Windows > PowerShell > Operational
# Query script block logs
Get-WinEvent -LogName "Microsoft-Windows-PowerShell/Operational" |
Where-Object { $_.Id -eq 4104 } | # Script Block Logging event
Select-Object TimeCreated, Message |
Out-GridView
# ❌ WRONG - No validation
function Get-UserData {
param($Username)
Invoke-Sqlcmd -Query "SELECT * FROM Users WHERE Username = '$Username'"
}
# Vulnerable to SQL injection
# ✅ CORRECT - Parameterized queries
function Get-UserData {
param(
[ValidatePattern('^[a-zA-Z0-9_-]+$')]
[string]$Username
)
Invoke-Sqlcmd -Query "SELECT * FROM Users WHERE Username = @Username" `
-Variable @{Username=$Username}
}
# ✅ CORRECT - ValidateSet for known values
function Restart-AppService {
param(
[ValidateSet('Web', 'API', 'Worker')]
[string]$ServiceName
)
Restart-Service -Name "App${ServiceName}Service"
}
[ValidatePattern], [ValidateSet], etc.Set-StrictMode -Version Latesttry/catch for error handlingInvoke-Expression with user input