Smithery Logo
MCPsSkillsDocsPricing
Login
NewFlame, an assistant that learns and improves. Available onTelegramSlack
    JosiahSiegel

    powershell-security

    JosiahSiegel/powershell-security
    Security
    8

    About

    SKILL.md

    Install

    • Telegram
      Telegram
    • Slack
      Slack
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    • Download skill
    ├─
    ├─
    └─
    Smithery Logo

    Give agents more agency

    Resources

    DocumentationPrivacy PolicySystem Status

    Company

    PricingAboutBlog

    Connect

    © 2026 Smithery. All rights reserved.

    About

    Modern PowerShell security practices including SecretManagement, JEA, WDAC, and credential protection

    SKILL.md

    PowerShell Security Best Practices (2025)

    Modern security practices for PowerShell scripts and automation, including credential management, SecretManagement module, and hardening techniques.

    SecretManagement Module (Recommended 2025 Standard)

    Overview

    Microsoft.PowerShell.SecretManagement is the official solution for secure credential storage in PowerShell.

    Why use SecretManagement:

    • Never store plaintext credentials in scripts
    • Cross-platform secret storage
    • Multiple vault provider support
    • Integration with Azure Key Vault, 1Password, KeePass, etc.

    Installation

    # 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
    

    Basic Usage

    # 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
    

    Azure Key Vault Integration

    # 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
    

    Automation Scripts with SecretManagement

    <#
    .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() }
    }
    

    Credential Management Best Practices

    Never Hardcode Credentials

    # ❌ 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
    

    Service Principal Authentication (Azure)

    # 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 (JEA)

    What is JEA?

    Just Enough Administration restricts PowerShell remoting sessions to specific cmdlets and parameters.

    Use Cases

    • Delegate admin tasks without full admin rights
    • Compliance requirements (SOC 2, HIPAA, PCI-DSS)
    • Production environment hardening
    • Audit trail for privileged operations

    Creating a JEA Endpoint

    # 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)
    

    JEA Audit Logging

    # 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
    

    Windows Defender Application Control (WDAC)

    PowerShell Script Control

    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
    

    Code Signing

    Why Sign Scripts?

    • Verify script integrity
    • Meet organizational security policies
    • Enable WDAC enforcement
    • Prevent tampering

    Signing a Script

    # 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"
    

    Execution Policy

    # 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"
    

    Constrained Language Mode

    What is Constrained Language Mode?

    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
    

    Script Block Logging

    Enable Logging

    # 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
    

    Review Logs

    # 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
    

    Input Validation

    Prevent Injection Attacks

    # ❌ 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"
    }
    

    Security Checklist

    Script Development

    • Never hardcode credentials (use SecretManagement)
    • Use parameterized queries for SQL operations
    • Validate all user input with [ValidatePattern], [ValidateSet], etc.
    • Enable Set-StrictMode -Version Latest
    • Use try/catch for error handling
    • Avoid Invoke-Expression with user input
    • Sign production scripts
    • Enable Script Block Logging

    Automation

    • Use Managed Identity or Service Principal (never passwords)
    • Store secrets in SecretManagement or Azure Key Vault
    • Implement JEA for delegated admin tasks
    • Enable audit logging for all privileged operations
    • Use least privilege principle
    • Rotate credentials regularly
    • Monitor failed authentication attempts

    Production Environments

    • Implement WDAC policies for script control
    • Use Constrained Language Mode for non-admin users
    • Enable PowerShell logging (Script Block + Transcription)
    • Require signed scripts (via execution policy)
    • Regular security audits
    • Keep PowerShell updated (7.5+)
    • Use JEA for remote administration

    Resources

    • SecretManagement Documentation
    • JEA Documentation
    • WDAC Documentation
    • PowerShell Security Best Practices
    • Azure Key Vault
    Recommended Servers
    Infisical
    Infisical
    OpenZeppelin
    OpenZeppelin
    Guard Mail
    Guard Mail
    Repository
    josiahsiegel/claude-plugin-marketplace
    Files