| description | PowerShell 7.5+ coding standards and cross-platform compatibility patterns |
|---|---|
| applyTo | **/*.ps1 |
- PowerShell 7.5+ syntax only (no Windows PowerShell 5.1 compatibility)
- Cross-platform by default (Windows, Linux, macOS, containers)
Join-Pathfor all path operations (never hardcode\or/)- Full cmdlet names only — never aliases (
gci,?,%) in scripts
function Verb-Noun {
[CmdletBinding()]
[OutputType([pscustomobject])]
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$Name,
[Parameter()]
[ValidateScript({Test-Path $_ -PathType Container})]
[string]$Path
)
process {
try {
$result = Get-Item -LiteralPath $Path -ErrorAction Stop
$result
}
catch {
$PSCmdlet.ThrowTerminatingError($PSItem)
}
}
}Function Rules:
- Always
[CmdletBinding()]— enables-Verbose,-ErrorAction, common parameters - Always
[OutputType()]— documents return type for callers and tooling - Validate every parameter —
[ValidateNotNullOrEmpty()],[ValidateSet()],[ValidateRange()],[ValidateScript()] SupportsShouldProcesson any function that modifies state-LiteralPathfor user-provided paths (prevents wildcard injection)- Prefix unused parameters with
_(e.g.,$_options)
| Element | Convention | Example |
|---|---|---|
| Functions | Verb-Noun (approved verbs) | Get-UserProfile |
| Parameters | PascalCase | $OutputPath |
| Local variables | camelCase | $itemCount |
| Script scope | $script:PascalCase |
$script:CacheData |
| Constants | UPPER_SNAKE_CASE | $MAX_RETRIES |
# Typed exceptions for specific errors
throw [System.IO.FileNotFoundException]::new("Config not found: $path")
throw [System.ArgumentException]::new("Invalid format: $format")-ErrorAction Stopinsidetry— converts non-terminating to terminating$PSCmdlet.ThrowTerminatingError($PSItem)— error points at caller, not internals- Never empty catch blocks — at minimum log the error
- Include context in error messages: what failed, what value, what to do
# FASTEST: Direct loop assignment (preferred)
$results = foreach ($item in $collection) { Process-Item $item }
# GOOD: List<T> for complex accumulation
$list = [System.Collections.Generic.List[object]]::new()
foreach ($item in $collection) { $list.Add((Process-Item $item)) }
# NEVER: += in loops (copies entire array each iteration)- Hashtable caches for O(1) lookups — never
Where-Objectin loops $null =for output suppression (fastest)-joinfor string building in loops — never+=concatenation[pscustomobject]@{}for object creation — 5-7x faster thanNew-Object- Set-based operations — process collections as wholes, never row-by-row
$status = $isActive ? 'Active' : 'Inactive' # Ternary
$value = $config.Setting ?? 'default' # Null-coalescing
$config.Timeout ??= 30 # Null-coalescing assignment# Preferred for 3+ parameters
$params = @{ Path = $source; Destination = $dest; Force = $true }
Copy-Item @params- Never hardcode credentials — use
[PSCredential], environment variables, orSecretManagement - Validate all user input — use
[ValidateScript()]parameter attributes - Use
-LiteralPathfor user-provided paths (prevents wildcard injection) - Escape regex from user input with
[regex]::Escape() - Never
Invoke-Expressionwith user input
Write-Verbose "Debug-level detail" # -Verbose to see
Write-Information "Informational message" # -InformationAction to see
Write-Warning "Non-blocking issue" # Always visible
Write-Error "Error with context" # Error stream
# Never Write-Host in functions — bypasses output streams- Public functions: Export explicitly from manifest
FunctionsToExport— never'*' - Private functions: Place in
Private/, dot-source in.psm1 - Module-level caches: Use
$script:scope for hashtables and lookup tables
$nullalways on the left:if ($null -eq $value)— PSScriptAnalyzer rule- Check before property access:
if ($items -and $items.Count -gt 0) - Guarantee array from pipeline:
$results = @(Get-ChildItem ...)
Invoke-Build Test
Invoke-ScriptAnalyzer -Path '.' -Recurse -Settings ./PSScriptAnalyzerSettings.psd1Deep reference: The
powershell-7skill provides benchmarks, Pester 5 testing patterns, module development, cross-platform details, anti-patterns, and PSScriptAnalyzer configuration.