PowerShell¶
PowerShell is a shell built around objects, pipelines, and cmdlet conventions. Code that ignores these conventions — using aliases in scripts, returning formatted strings, skipping -WhatIf support — breaks the pipeline and frustrates users. These 15 principles come from the PowerShell Best Practices and the community's hard-won experience.
Optional External Tool Augmentation¶
Consent-first external tooling
External tool execution is optional and disabled by default. Use
--enable-external-tools (CLI) or enable_external_tools=true (MCP)
to opt in. Missing tools should return recommendations; no automatic
installs occur during analysis.
| Tool | Default invocation | Output |
|---|---|---|
pwsh |
pwsh -Command Invoke-ScriptAnalyzer -ScriptDefinition $input |
Text / structured stderr |
Zen Principles¶
15 principles across 12 categories, drawn from PoshCode Style Guide.
Clarity · 1 principle Correctness · 1 principle Debugging · 1 principle Design · 2 principles Documentation · 1 principle Error Handling · 1 principle Idioms · 1 principle Naming · 2 principles Readability · 2 principles Robustness · 1 principle Safety · 1 principle Scope · 1 principle
| Rule ID | Principle | Category | Severity | Dogma |
|---|---|---|---|---|
ps-001 |
Use approved verbs | Naming | 7 | ZEN-UNAMBIGUOUS-NAME |
ps-002 |
Use proper error handling | Error Handling | 9 | ZEN-FAIL-FAST |
ps-003 |
Use cmdlet binding and parameters | Design | 8 | ZEN-RIGHT-ABSTRACTION |
ps-004 |
Use PascalCase for function names | Naming | 7 | ZEN-UNAMBIGUOUS-NAME |
ps-005 |
Use Write-Verbose and Write-Debug | Debugging | 6 | ZEN-EXPLICIT-INTENT |
ps-006 |
Avoid positional parameters | Readability | 7 | ZEN-UNAMBIGUOUS-NAME |
ps-007 |
Use pipeline properly | Idioms | 7 | ZEN-RIGHT-ABSTRACTION |
ps-008 |
Use -WhatIf and -Confirm support | Safety | 8 | ZEN-FAIL-FAST |
ps-009 |
Use splatting for readability | Readability | 6 | ZEN-UNAMBIGUOUS-NAME |
ps-010 |
Validate parameters properly | Robustness | 8 | ZEN-FAIL-FAST |
ps-011 |
Use comment-based help | Documentation | 7 | ZEN-UNAMBIGUOUS-NAME |
ps-012 |
Avoid aliases in scripts | Clarity | 6 | ZEN-EXPLICIT-INTENT, ZEN-UNAMBIGUOUS-NAME |
ps-013 |
Return objects, not formatted text | Design | 8 | ZEN-RIGHT-ABSTRACTION |
ps-014 |
Use script scope carefully | Scope | 7 | ZEN-STRICT-FENCES, ZEN-EXPLICIT-INTENT |
ps-015 |
Handle null values explicitly | Correctness | 7 | ZEN-EXPLICIT-INTENT, ZEN-FAIL-FAST |
ps-001 — Use approved verbs
Function names should use approved PowerShell verbs
Universal Dogmas: ZEN-UNAMBIGUOUS-NAME
Common Violations:
- Non-standard verbs in function names
- Custom verbs without approval
- Unclear function naming
Detectable Patterns:
function CustomVerb-Noun
Recommended Fix
Get-Verb to see approved verbs, use Verb-Noun pattern
ps-002 — Use proper error handling
Use try/catch/finally and -ErrorAction
Universal Dogmas: ZEN-FAIL-FAST
Common Violations:
- Commands without error handling
- Not using try/catch
- Ignoring $Error variable
- Silent failures
Detectable Patterns:
command without try/catch-ErrorAction SilentlyContinue abuse
ps-003 — Use cmdlet binding and parameters
Functions should use [CmdletBinding()] and proper parameters
Universal Dogmas: ZEN-RIGHT-ABSTRACTION
Common Violations:
- Functions without [CmdletBinding()]
- Using $args instead of parameters
- Missing parameter validation
- No parameter attributes
Detectable Patterns:
!CmdletBinding
Recommended Fix
[CmdletBinding()] with [Parameter()] attributes
ps-004 — Use PascalCase for function names
Follow PowerShell naming conventions
Universal Dogmas: ZEN-UNAMBIGUOUS-NAME
Common Violations:
- camelCase function names
- snake_case in PowerShell
- All lowercase names
- Inconsistent casing
Thresholds:
| Parameter | Default |
|---|---|
naming_convention |
PascalCase for functions, camelCase for variables |
ps-005 — Use Write-Verbose and Write-Debug
Use proper output streams for logging
Universal Dogmas: ZEN-EXPLICIT-INTENT
Common Violations:
- Using Write-Host for logging
- Not using Write-Verbose
- No debug output
- Mixing output streams
Detectable Patterns:
Write-Host for logging
Recommended Fix
Write-Verbose, Write-Debug, Write-Information
ps-006 — Avoid positional parameters
Always use named parameters in scripts
Universal Dogmas: ZEN-UNAMBIGUOUS-NAME
Common Violations:
- Positional parameter usage
- Unclear command calls
- Not using -ParameterName syntax
Detectable Patterns:
$args
Recommended Fix
Get-ChildItem -Path $path -Filter $filter
ps-007 — Use pipeline properly
Leverage PowerShell pipeline for object processing
Universal Dogmas: ZEN-RIGHT-ABSTRACTION
Common Violations:
- ForEach-Object loops instead of pipeline
- Not using pipeline when appropriate
- Breaking pipeline chain unnecessarily
Detectable Patterns:
!|
Recommended Fix
Get-Process | Where-Object { $_.CPU -gt 10 }
ps-008 — Use -WhatIf and -Confirm support
Implement ShouldProcess for destructive operations
Universal Dogmas: ZEN-FAIL-FAST
Common Violations:
- Destructive commands without -WhatIf
- Not using ShouldProcess
- No user confirmation for dangerous operations
Detectable Patterns:
!WhatIf
Recommended Fix
[CmdletBinding(SupportsShouldProcess)]
ps-009 — Use splatting for readability
Use splatting for multiple parameters
Universal Dogmas: ZEN-UNAMBIGUOUS-NAME
Common Violations:
- Long parameter lists inline
- Not using @splat syntax
- Unreadable command calls
Detectable Patterns:
!@{
Recommended Fix
$params = @{...}; Command @params
ps-010 — Validate parameters properly
Use parameter validation attributes
Universal Dogmas: ZEN-FAIL-FAST
Common Violations:
- No [ValidateNotNull()]
- No [ValidateSet()]
- Missing [ValidateRange()]
- No input validation
Detectable Patterns:
![Validate
Recommended Fix
[ValidateNotNullOrEmpty()], [ValidateSet('value1','value2')]
ps-011 — Use comment-based help
Include .SYNOPSIS, .DESCRIPTION, .EXAMPLE
Universal Dogmas: ZEN-UNAMBIGUOUS-NAME
Common Violations:
- Functions without help comments
- Missing .SYNOPSIS
- No usage examples
- Incomplete documentation
Detectable Patterns:
!.SYNOPSIS
ps-012 — Avoid aliases in scripts
Use full cmdlet names, not aliases
Universal Dogmas: ZEN-EXPLICIT-INTENT, ZEN-UNAMBIGUOUS-NAME
Common Violations:
- Using aliases like 'gci' instead of Get-ChildItem
- Using '%' instead of ForEach-Object
- Using '?' instead of Where-Object
- Unclear abbreviated commands
Detectable Patterns:
gci, ls, dir, cat, etc.
Recommended Fix
Full cmdlet names
ps-013 — Return objects, not formatted text
Return structured objects for pipeline compatibility
Universal Dogmas: ZEN-RIGHT-ABSTRACTION
Common Violations:
- Returning formatted strings
- Using Format-Table in functions
- Breaking pipeline with text output
- Not using PSCustomObject
Detectable Patterns:
Format-TableOut-String
Recommended Fix
[PSCustomObject]@{Property='Value'}
ps-014 — Use script scope carefully
Be explicit about variable scope
Universal Dogmas: ZEN-STRICT-FENCES, ZEN-EXPLICIT-INTENT
Common Violations:
- Unintended global variables
- Not using $script: or $global:
- Scope pollution
- Variable leakage
Detectable Patterns:
$global:$script:
ps-015 — Handle null values explicitly
Check for null before operations
Universal Dogmas: ZEN-EXPLICIT-INTENT, ZEN-FAIL-FAST
Common Violations:
- Not checking for $null
- NullReferenceException in scripts
- Assuming objects exist
- No null coalescing
Detectable Patterns:
!$null
Recommended Fix
if ($null -ne $variable) or $variable ?? 'default'
Detector Catalog¶
Clarity¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| PowerShellAliasUsageDetector | Detect built-in aliases (gci, ls, %, ?) used in scripts |
ps-012 |
Correctness¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| PowerShellNullHandlingDetector | Detect functions and param blocks that never check for $null |
ps-015 |
Debugging¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| PowerShellVerboseDebugDetector | Detect Write-Host calls that should use Write-Verbose or Write-Debug |
ps-005 |
Design¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| PowerShellCmdletBindingDetector | Detect advanced functions missing [CmdletBinding()] and param() |
ps-003 |
| PowerShellReturnObjectsDetector | Detect functions that return formatted text instead of objects | ps-013 |
Documentation¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| PowerShellCommentHelpDetector | Detect functions missing comment-based help with .SYNOPSIS |
ps-011 |
Error Handling¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| PowerShellErrorHandlingDetector | Detect scripts that lack any form of error handling | ps-002 |
Idioms¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| PowerShellPipelineUsageDetector | Detect ForEach-Object or foreach loops that should use pipelines |
ps-007 |
Naming¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| PowerShellApprovedVerbDetector | Detect function names that use unapproved PowerShell verbs | ps-001 |
| PowerShellPascalCaseDetector | Detect function names that violate PascalCase naming conventions | ps-004 |
Readability¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| PowerShellPositionalParamsDetector | Detect reliance on $args for positional parameter access |
ps-006 |
| PowerShellSplattingDetector | Detect cmdlet calls with many inline parameters that should use splatting | ps-009 |
Robustness¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| PowerShellParameterValidationDetector | Detect param() blocks that lack [Validate*] attributes |
ps-010 |
Safety¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| PowerShellShouldProcessDetector | Detect destructive cmdlet verbs missing SupportsShouldProcess |
ps-008 |
Scope¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| PowerShellScopeUsageDetector | Detect explicit $global: and $script: scope modifiers |
ps-014 |
Principle → Detector Wiring
%%{init: {"theme": "base", "flowchart": {"useMaxWidth": false, "htmlLabels": true, "nodeSpacing": 40, "rankSpacing": 60}}}%%
graph TD
ps_001["ps-001<br/>Use approved verbs"]
ps_002["ps-002<br/>Use proper error handling"]
ps_003["ps-003<br/>Use cmdlet binding and pa..."]
ps_004["ps-004<br/>Use PascalCase for functi..."]
ps_005["ps-005<br/>Use Write-Verbose and Wri..."]
ps_006["ps-006<br/>Avoid positional paramete..."]
ps_007["ps-007<br/>Use pipeline properly"]
ps_008["ps-008<br/>Use -WhatIf and -Confirm ..."]
ps_009["ps-009<br/>Use splatting for readabi..."]
ps_010["ps-010<br/>Validate parameters prope..."]
ps_011["ps-011<br/>Use comment-based help"]
ps_012["ps-012<br/>Avoid aliases in scripts"]
ps_013["ps-013<br/>Return objects, not forma..."]
ps_014["ps-014<br/>Use script scope carefull..."]
ps_015["ps-015<br/>Handle null values explic..."]
det_PowerShellAliasUsageDetector["Power Shell<br/>Alias Usage"]
ps_012 --> det_PowerShellAliasUsageDetector
det_PowerShellApprovedVerbDetector["Power Shell<br/>Approved Verb"]
ps_001 --> det_PowerShellApprovedVerbDetector
det_PowerShellCmdletBindingDetector["Power Shell<br/>Cmdlet Binding"]
ps_003 --> det_PowerShellCmdletBindingDetector
det_PowerShellCommentHelpDetector["Power Shell<br/>Comment Help"]
ps_011 --> det_PowerShellCommentHelpDetector
det_PowerShellErrorHandlingDetector["Power Shell<br/>Error Handling"]
ps_002 --> det_PowerShellErrorHandlingDetector
det_PowerShellNullHandlingDetector["Power Shell<br/>Null Handling"]
ps_015 --> det_PowerShellNullHandlingDetector
det_PowerShellParameterValidationDetector["Power Shell<br/>Parameter Validation"]
ps_010 --> det_PowerShellParameterValidationDetector
det_PowerShellPascalCaseDetector["Power Shell<br/>Pascal Case"]
ps_004 --> det_PowerShellPascalCaseDetector
det_PowerShellPipelineUsageDetector["Power Shell<br/>Pipeline Usage"]
ps_007 --> det_PowerShellPipelineUsageDetector
det_PowerShellPositionalParamsDetector["Power Shell<br/>Positional Params"]
ps_006 --> det_PowerShellPositionalParamsDetector
det_PowerShellReturnObjectsDetector["Power Shell<br/>Return Objects"]
ps_013 --> det_PowerShellReturnObjectsDetector
det_PowerShellScopeUsageDetector["Power Shell<br/>Scope Usage"]
ps_014 --> det_PowerShellScopeUsageDetector
det_PowerShellShouldProcessDetector["Power Shell<br/>Should Process"]
ps_008 --> det_PowerShellShouldProcessDetector
det_PowerShellSplattingDetector["Power Shell<br/>Splatting"]
ps_009 --> det_PowerShellSplattingDetector
det_PowerShellVerboseDebugDetector["Power Shell<br/>Verbose Debug"]
ps_005 --> det_PowerShellVerboseDebugDetector
Detector Class Hierarchy
%%{init: {"theme": "base"}}%%
classDiagram
direction TB
class ViolationDetector {
<<abstract>>
+detect(context, config)
}
class det_01["Power Shell Alias Usage"]
ViolationDetector <|-- det_01
class det_02["Power Shell Approved Verb"]
ViolationDetector <|-- det_02
class det_03["Power Shell Cmdlet Binding"]
ViolationDetector <|-- det_03
class det_04["Power Shell Comment Help"]
ViolationDetector <|-- det_04
class det_05["Power Shell Error Handling"]
ViolationDetector <|-- det_05
class det_06["Power Shell Null Handling"]
ViolationDetector <|-- det_06
class det_07["Power Shell Parameter Validation"]
ViolationDetector <|-- det_07
class det_08["Power Shell Pascal Case"]
ViolationDetector <|-- det_08
class det_09["Power Shell Pipeline Usage"]
ViolationDetector <|-- det_09
class det_10["Power Shell Positional Params"]
ViolationDetector <|-- det_10
class det_11["Power Shell Return Objects"]
ViolationDetector <|-- det_11
class det_12["Power Shell Scope Usage"]
ViolationDetector <|-- det_12
class det_13["Power Shell Should Process"]
ViolationDetector <|-- det_13
class det_14["Power Shell Splatting"]
ViolationDetector <|-- det_14
class det_15["Power Shell Verbose Debug"]
ViolationDetector <|-- det_15
Analysis Pipeline
%%{init: {"theme": "base", "flowchart": {"useMaxWidth": false, "htmlLabels": true, "nodeSpacing": 50, "rankSpacing": 70}}}%%
flowchart TD
Source(["Source Code"]) --> Parse["Parse & Tokenize"]
Parse --> Metrics["Compute Metrics"]
Metrics --> Pipeline{"15 Detectors"}
Pipeline --> Collect["Aggregate Violations"]
Collect --> Result(["AnalysisResult<br/>15 principles"])
Analysis States
%%{init: {"theme": "base"}}%%
stateDiagram-v2
[*] --> Ready
Ready --> Parsing : analyze(code)
Parsing --> Computing : AST ready
Computing --> Detecting : metrics ready
Detecting --> Reporting : 15 detectors run
Reporting --> [*] : AnalysisResult
Parsing --> Reporting : parse error (best-effort)
Configuration¶
languages:
powershell:
enabled: true
pipeline:
- type: powershell_approved_verbs
approved_verbs: ['get', 'set', 'new', 'remove', 'add', 'clear', 'start', 'stop']
See Also¶
- Bash — POSIX shell scripting counterpart
- Configuration — Per-language pipeline overrides
- Understanding Violations — Severity scale reference