AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Powershell

skill-dstreefkerk-claude-skills-powershell · by dstreefkerk

Enterprise PowerShell coding standards. Use when writing, reviewing, or generating any PowerShell code, creating PS1 scripts or functions, debugging PowerShell, or asked to help with PowerShell. Enforces best practices for structure, error handling, security, performance, and output patterns.

No reviews yet
0 installs
34 views
0.0% view→install

Install

$ agentstack add skill-dstreefkerk-claude-skills-powershell

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-dstreefkerk-claude-skills-powershell)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Powershell? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

This skill enforces enterprise-grade PowerShell standards when writing, reviewing, or generating any PowerShell code. Apply all rules below automatically — do not wait to be asked.

Triggers

  • "write a PowerShell script"
  • "create a function in PowerShell"
  • "review this PowerShell"
  • "generate a PS1 file"
  • "help me with PowerShell code"

Resource Files

Load these when needed for detailed patterns, examples, and gotchas:

| File | When to load | |---|---| | [resources/common-patterns.md](resources/common-patterns.md) | Need a code pattern (script skeleton, error handling, collections, splatting, regex, pipeline functions, string building, hashtable lookups, safe property accessor helper, module structure, ShouldProcess+Force, etc.) | | [resources/compatibility-and-clm.md](resources/compatibility-and-clm.md) | Need PS5.1 vs PS7+ compatibility details, String.Split() changes, $IsWindows portability, or writing code that may run under AppLocker/WDAC (CLM) | | [resources/traps-and-gotchas.md](resources/traps-and-gotchas.md) | Debugging unexpected behaviour around nulls, booleans, comparison operators, pipeline output pollution, $PSBoundParameters, VerbosePreference in modules, PS class limitations, or defensive null-safe patterns (property access guards, ContainsKey null guards, hashtable key/value guards, null loop entries, Add-Member verification, array double-wrapping) | | [resources/get-help-and-get-member.md](resources/get-help-and-get-member.md) | Using an unfamiliar cmdlet or object — discover parameters, properties, and online docs before writing code | | [resources/performance-patterns.md](resources/performance-patterns.md) | Writing performance-sensitive code — benchmarked patterns for collections, strings, filtering, object creation, hashtable lookups, large file processing, function call overhead | | [resources/api-and-web.md](resources/api-and-web.md) | Calling REST APIs — authentication, pagination, rate limiting/retry, JSON depth, credential management, PSReadLine history protection | | [resources/security-hardening.md](resources/security-hardening.md) | Security controls — CLM enforcement (WDAC vs AppLocker), PowerShell logging (Module/Script Block/Transcription), Protected Event Logging, JEA, code signing, script injection prevention |


Quick Reference

| Area | Rule | |---|---| | Structure | #Requires → help → param() → Functions → Main → Cleanup | | Strict Mode | Set-StrictMode -Version Latest always | | Indent | 4 spaces, ≤120 chars per line | | Encoding | UTF-8 without BOM; always specify -Encoding utf8NoBOM explicitly | | Naming | PascalCase functions/params, camelCase locals | | Functions | Always [CmdletBinding()]; Verb-Noun singular nouns | | State-changing | SupportsShouldProcess + $PSCmdlet.ShouldProcess() | | Output | Emit [PSCustomObject]; never Write-Host for data | | Errors | try/catch with -ErrorAction Stop; never empty catch | | Arrays | Capture foreach output directly — never += in loops | | WMI | Get-CimInstance not Get-WmiObject | | Events | Get-WinEvent not Get-EventLog | | Secrets | PSCredential/SecureString/SecretManagement vault — no plaintext | | Native cmds | Check $LASTEXITCODE after native executables; use try/catch for cmdlets | | Aliases | No aliases in scripts — always full cmdlet names | | Paths | Join-Path $PSScriptRoot 'file.csv' — never assume working directory | | CLM | Check $ExecutionContext.SessionState.LanguageMode; avoid .NET::new(), Add-Type if CLM is possible |


Critical Rules

These are non-negotiable. Apply them to every script and function.

  1. Set-StrictMode -Version Latest at the top of every script (not inside functions).
  2. [CmdletBinding()] on every advanced function, no exceptions.
  3. -ErrorAction Stop on every cmdlet call inside a try block, or set $ErrorActionPreference = 'Stop' for the scope. Non-terminating errors do NOT trigger catch without this.
  4. Never empty catch blocks. Always log at minimum Write-Warning or Write-Error.
  5. Save $_ immediately at the start of a catch block: $err = $_
  6. No += in loops. Capture foreach output directly, or use [System.Collections.Generic.List[PSObject]]::new() + .Add() in FullLanguage mode only.
  7. No Invoke-Expression on untrusted or constructed input — ever.
  8. No plaintext credentials in code, parameters, or log output.
  9. No aliases in scripts (%, ?, gci, ft, etc.).
  10. No Format-* mid-pipeline — emit objects; let the caller format.
  11. SupportsShouldProcess on any function that modifies state (files, registry, AD, etc.).
  12. No begin/process/end at script top level — only inside pipeline-aware functions.
  13. No positional parameters in scripts — always use named parameters (e.g., Get-Item -Path $p not Get-Item $p).
  14. Always specify -Encoding on file I/O cmdlets — default encoding differs between PS5.1 and PS7+.

Anti-Patterns

| Anti-Pattern | Replace With | |---|---| | Get-WmiObject | Get-CimInstance | | Get-EventLog | Get-WinEvent -FilterHashtable @{...} | | $array += $item in loops | Capture foreach output, or [List[PSObject]]::new() + .Add() | | Write-Host for data output | Write-Output / emit objects | | Format-Table mid-pipeline | Emit objects; format at end | | Invoke-Expression $cmd | Parameterised calls / splatting | | Plaintext password in param | [PSCredential] + SecretManagement | | Empty catch {} | Always log or re-throw | | $? after native executables | $LASTEXITCODE (for cmdlets use try/catch) | | ConvertTo-Json without -Depth | Always ConvertTo-Json -Depth 10 (default 2 silently truncates) | | $global: for cross-function state | $script: scope — contained to the script file | | Building HTML without encoding | Regex replace for 5 HTML special chars (CLM-safe); HttpUtility in FullLanguage only | | Read-Host for required input | [Parameter(Mandatory)] | | Aliases (gci, %, ?) | Full cmdlet names | | begin/process/end at script top | Only inside pipeline-aware functions | | Positional parameters (Get-Item $p) | Named parameters (Get-Item -Path $p) | | Out-Null in hot paths | [void](...) or $null = ... (no pipeline overhead) | | Where-Object when source has -Filter | Use -Filter on the source cmdlet | | ForEach-Object { $_.Prop } for single property | Select-Object -ExpandProperty Prop | | New-Object PSObject -Property @{} | [PSCustomObject]@{} (3x faster, cleaner) | | [array]::new() or List[T]::new() in CLM | Capture foreach output directly | | Add-Type in potentially CLM environments | Cmdlet-based alternatives | | continue inside ForEach-Object | return (acts as continue in pipeline context) | | if ($array -eq $null) | if ($null -eq $array) (left-side null check) | | if ($results) to test for empty collection | if ($results.Count -gt 0) | | -Encoding utf8 without knowing PS version | -Encoding utf8NoBOM (explicit, portable) | | "abc" -contains "ab" for substring | "abc".Contains("ab") or "abc" -match "ab" | | -like "pattern\d+" (regex in glob) | -match "pattern\d+" for regex patterns | | String "False" as a boolean | Explicit -eq 'True' or -eq $true comparison | | $list.Add($item) on ArrayList | [void]$list.Add($item).Add() returns the index to the pipeline | | New-Item/New-Object output leaked | $null = New-Item ... or assign to variable | | Write-Output $x (usually) | Just emit $x implicitly; use Write-Output -NoEnumerate only for arrays | | Inconsistent output types per code path | Always emit the same type; use error stream for errors | | No [OutputType()] on functions | Declare [OutputType([PSCustomObject])] on functions with defined output | | Invoke-RestMethod without $ProgressPreference | Set $ProgressPreference = 'SilentlyContinue' at script top for non-interactive use | | Parameter named Verbose, Debug, WhatIf, etc. | These are reserved by [CmdletBinding()] — rename to avoid conflicts | | Parameter named Error, Input, Host, Args | These shadow automatic variables — use distinct names | | $string += "text" in loops | -join operator (790× faster at scale) | | Nested Where-Object for cross-collection joins | Hashtable lookup — O(n+m) vs O(n×m) | | FunctionsToExport = '*' in manifest | Explicit function list — avoids ~15s import penalty | | "str".Split('ab') for multi-char splitting | "str".Split([char[]]'ab') — portable across PS5.1 and PS7+ | | if ($IsWindows) without edition check | $PSVersionTable.PSEdition -eq 'Desktop' -or $IsWindows | | Invoke-RestMethod -Authentication + manual Authorization header | Use only one — -Authentication silently overrides the header | | -FollowRelLink without -MaximumFollowRelLink | Always set -MaximumFollowRelLink to prevent infinite loops | | Class method with implicit output | Class methods discard all output except return — assign or return explicitly | | Import-Module MyModule; [ClassType]::new() | using module MyModule required for class types | | hidden property assumed private | hidden properties ARE serialized by ConvertTo-Json | | $obj.Prop on object that may lack Prop | if ($obj.PSObject.Properties['Prop']) { $obj.Prop } — safe under Set-StrictMode and avoids PropertyNotFoundException | | $hash[$key] without null-guarding the key | if ($key) { $hash[$key] } — null key crashes Dictionary; guard first | | $hash.ContainsKey($key) without null guard | $key -and $hash.ContainsKey($key) — null argument throws on Dictionary<> types | | Loop body with no null guard on $item | if ($null -eq $item) { continue } at top — API/pipeline collections can contain null entries | | Building lookup without guarding key or value | Check if ($id -and $value) before $lookup[$id] = $value — null keys create silent corrupt entries | | $connector | Add-Member ...; $connector._Prop | Verify with if ($obj.PSObject.Properties['_Prop'])Add-Member silently fails on read-only objects | | return ,$array in function + @(Func) at call site | Double-wrap bug: $items = @(Func) when function returns ,$arr gives a 1-element wrapper; use direct assignment $items = Func |


Review Checklist

Before finalising any generated PowerShell, verify:

  • [ ] Set-StrictMode -Version Latest present at script top
  • [ ] [CmdletBinding()] on every advanced function
  • [ ] -ErrorAction Stop on all cmdlet calls in try blocks (or $ErrorActionPreference = 'Stop' set)
  • [ ] No empty catch blocks; $err = $_ saved at catch start
  • [ ] No += inside loops for collection building
  • [ ] No aliases used anywhere
  • [ ] No Write-Host used for data (only acceptable for interactive UI messaging)
  • [ ] No Invoke-Expression on dynamic/user-supplied input
  • [ ] No plaintext secrets in code or output
  • [ ] SupportsShouldProcess on all state-modifying functions
  • [ ] finally block for any resource cleanup
  • [ ] Output is objects ([PSCustomObject]), not pre-formatted strings
  • [ ] Get-CimInstance used instead of Get-WmiObject
  • [ ] $LASTEXITCODE checked after every native executable call
  • [ ] $PSScriptRoot used for all paths relative to the script file
  • [ ] Parameter variables not mutated; copied to local variables first
  • [ ] Lines ≤120 characters; 4-space indentation throughout
  • [ ] No positional parameter usage — all parameters named explicitly
  • [ ] -Encoding utf8NoBOM specified on all file I/O operations
  • [ ] $null is on the LEFT side of all null comparisons
  • [ ] Empty collection checked with .Count -eq 0, not bare if ($collection)
  • [ ] @($results) used when .Count is needed on cmdlet output
  • [ ] return (not continue) used to skip items in ForEach-Object
  • [ ] CLM-unsafe patterns (.NET::new(), Add-Type) avoided if script may run under AppLocker/WDAC
  • [ ] PS7-only syntax (??, ? :, -Parallel) annotated or avoided if PS5.1 support required
  • [ ] -Filter used on source cmdlets rather than downstream Where-Object where possible
  • [ ] -like used for glob patterns, -match used for regex — not mixed
  • [ ] -contains/-in used for collection membership, not string substring checks
  • [ ] [void]$list.Add(...) used when calling ArrayList.Add() (it returns the index)
  • [ ] Intermediate cmdlets (New-Item, New-Object, etc.) inside functions have their output suppressed or assigned
  • [ ] No parameter names clash with common parameters (Verbose, Debug, WhatIf, Confirm, ErrorAction, etc.)
  • [ ] No parameter names shadow automatic variables (Error, Input, Host, Args, This)
  • [ ] [OutputType()] declared on functions that emit a defined object type
  • [ ] $ProgressPreference = 'SilentlyContinue' set in non-interactive scripts that call Invoke-WebRequest/Invoke-RestMethod
  • [ ] Write-Host not used for data — only for interactive UI messages
  • [ ] String building in loops uses -join, not += (790× slower at scale)
  • [ ] Cross-collection joins use hashtable lookup, not nested Where-Object (O(n+m) vs O(n×m))
  • [ ] Module manifest uses explicit FunctionsToExport list, not '*' (~15s penalty at import)
  • [ ] String.Split() with multi-char argument uses [char[]] cast for portable behaviour
  • [ ] $IsWindows portability uses $PSVersionTable.PSEdition -eq 'Desktop' -or $IsWindows
  • [ ] External/API object property access is guarded: if ($obj.PSObject.Properties['Prop']) before $obj.Prop (strict-mode-safe)
  • [ ] Hashtable keys are null-guarded before indexing: if ($key) { $hash[$key] } not bare $hash[$key]
  • [ ] ContainsKey() calls are null-guarded: $key -and $hash.ContainsKey($key)
  • [ ] Loop bodies guard against null items at the top: if ($null -eq $item) { continue }
  • [ ] Lookup hashtable population guards both key and value before inserting: if ($id -and $value) { $lookup[$id] = $value }
  • [ ] Add-Member note properties are verified before access: if ($obj.PSObject.Properties['_Name']) { $obj._Name }
  • [ ] Functions using return ,$array convention are called with direct assignment ($x = Func), NOT $x = @(Func) (double-wrap bug)

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.