powershell-security

PowerShell Security Best Practices (2025)

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "powershell-security" with this command: npx skills add josiahsiegel/claude-plugin-marketplace/josiahsiegel-claude-plugin-marketplace-powershell-security

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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Security

docker-security-guide

No summary provided by upstream source.

Repository SourceNeeds Review
Security

security-first-2025

No summary provided by upstream source.

Repository SourceNeeds Review
Security

git-security-2025

No summary provided by upstream source.

Repository SourceNeeds Review
General

tailwindcss-advanced-layouts

No summary provided by upstream source.

Repository SourceNeeds Review