PSScriptAnalyzer, Rules, Style, and Static Diagnostics
Use current PSScriptAnalyzer tooling to discover rules, analyze PowerShell safely, define repository policy, handle suppressions, check compatibility, and refactor diagnostics without confusing linting with correctness.
Learning objectives
- Explain static analysis and how it complements runtime tests.
- Use PSScriptAnalyzer 1.24.0 tooling on the PowerShell 7.6 baseline.
- Discover rules and run structured analysis with Invoke-ScriptAnalyzer.
- Create repeatable project settings for security, safety, maintainability, and compatibility rules.
- Handle false positives and suppressions with narrow documented justification.
- Refactor an intentionally noncompliant script and rerun analysis/tests.
1. Static analysis inspects code without running every behavior
Static analysis examines source code and its parsed structure for patterns associated with defects, portability problems, security risks, or maintainability issues. It can find problems before a particular execution path is exercised.
Static analysis complements testing. A test can prove that one scenario produced the expected result; an analyzer can flag a risky command or unused variable in a path that your tests never executed. Neither one replaces the other.
2. Current baseline: PSScriptAnalyzer 1.24.0
The verified current Microsoft documentation describes PSScriptAnalyzer 1.24.0 as the latest documented release. It supports Windows PowerShell 5.1 and PowerShell 7.2.11 or later on Windows, Linux, and macOS. This course uses PowerShell 7.6.x as its main baseline.
$analyzer = Get-Module -ListAvailable PSScriptAnalyzer |
Sort-Object Version -Descending |
Select-Object -First 1
$analyzer | Select-Object Name,Version,Path
# If missing and PSResourceGet from Chapter 15 is available:
# Install-PSResource -Name PSScriptAnalyzer -Scope CurrentUser
# Import-Module PSScriptAnalyzer3. Discover rules instead of memorizing a stale list
Get-ScriptAnalyzerRule exposes the rules available in the installed module. Rule names, defaults, and capabilities can evolve, so discovery is more reliable than copying an old blog post.
Get-ScriptAnalyzerRule |
Sort-Object Severity,RuleName |
Select-Object RuleName,Severity,SourceName |
Format-Table -AutoSize
Get-ScriptAnalyzerRule -Name PSAvoidUsingCmdletAliases,PSUseApprovedVerbs |
Select-Object RuleName,Severity,DescriptionFormatting is appropriate here because the output is for human inspection. In automation, keep the diagnostic objects structured until the final reporting boundary.
4. Invoke-ScriptAnalyzer returns diagnostic objects
Invoke-ScriptAnalyzer analyzes .ps1, .psm1, and .psd1 content and returns diagnostic records. You can analyze one file or recurse through a project.
$diagnostics = Invoke-ScriptAnalyzer -Path ./src -Recurse
$diagnostics |
Select-Object RuleName,Severity,ScriptName,Line,Column,Message
# A focused CI view:
$blocking = Invoke-ScriptAnalyzer -Path ./src -Recurse -Severity Error,Warning
[pscustomobject]@{
DiagnosticCount = @($blocking).Count
HasBlockingIssues = @($blocking).Count -gt 0
}Severity helps triage, but a team still chooses policy. A warning can be release-blocking if it represents a class of risk your organization has decided to forbid.
5. Useful rule categories map to real engineering risks
PSScriptAnalyzer rules are not merely style preferences. Many describe operational failure modes: ambiguous aliases in shared scripts, plaintext credential parameters, state-changing functions without ShouldProcess, or compatibility issues on another PowerShell host.
| Category | Example current rule | Why it matters |
|---|---|---|
| Naming | PSUseApprovedVerbs | Predictable command discovery and consistent semantics. |
| Maintainability | PSUseDeclaredVarsMoreThanAssignments | Assigned-but-unused values often reveal dead or mistaken logic. |
| Portability | PSAvoidUsingCmdletAliases | Aliases can reduce clarity and differ across environments. |
| Security | PSAvoidUsingPlainTextForPassword | Plain string password parameters increase exposure risk. |
| Safety | PSUseShouldProcessForStateChangingFunctions | State-changing functions should expose preview/confirmation patterns. |
| Compatibility | PSUseCompatibleCommands / Syntax / Types | Detect dependencies unavailable on target PowerShell environments. |
6. Analyze an intentionally noncompliant script before refactoring it
The following file is intentionally poor training code. Do not run it as a deployment tool. The point is to let the analyzer identify multiple independent concerns.
# bad-training-script.ps1 -- analyze, do not use in production
function Delete-TrainingCache {
param(
[string]$Path,
[string]$Password
)
$unused = 'never read again'
gci $Path
Invoke-Expression "Remove-Item -Recurse -Force '$Path'"
}
Invoke-ScriptAnalyzer -Path ./bad-training-script.ps1 |
Sort-Object Severity,RuleName |
Select-Object RuleName,Severity,Line,MessageLikely diagnostics include the unapproved Delete verb, alias use, plaintext password parameter, unused variable, Invoke-Expression, and state-change design concerns. The exact set depends on the installed rule version and syntax.
7. A repository settings file makes analysis policy repeatable
A PSScriptAnalyzerSettings.psd1 file is a PowerShell data file that records team policy beside the code. This avoids developers running different rule subsets by memory.
# PSScriptAnalyzerSettings.psd1
@{
Severity = @('Error','Warning')
IncludeRules = @(
'PSUseApprovedVerbs'
'PSAvoidUsingCmdletAliases'
'PSUseDeclaredVarsMoreThanAssignments'
'PSAvoidUsingPlainTextForPassword'
'PSAvoidUsingInvokeExpression'
'PSUseShouldProcessForStateChangingFunctions'
)
}
# Run explicitly with the project policy:
Invoke-ScriptAnalyzer -Path ./src -Recurse -Settings ./PSScriptAnalyzerSettings.psd1PSScriptAnalyzer can also discover a settings file named PSScriptAnalyzerSettings.psd1 at the project root when you analyze that project. Explicit paths are useful in CI because the policy input is unambiguous.
8. Compatibility rules require an explicit target
“Cross-platform” is not one environment. A script can target PowerShell 7.6 on Linux, Windows PowerShell 5.1 on Windows, or a specific server image. Compatibility analysis is meaningful only when you configure the environment you actually support.
Get-ScriptAnalyzerRule -Name PSUseCompatibleCommands,PSUseCompatibleSyntax,PSUseCompatibleTypes |
Select-Object RuleName,Severity,Description
# Inspect the shipped settings/examples before enabling a compatibility rule:
$module = Get-Module -ListAvailable PSScriptAnalyzer |
Sort-Object Version -Descending |
Select-Object -First 1
$module.ModuleBase9. Suppress a diagnostic only with a narrow, documented reason
Static analyzers can produce false positives or flag a pattern that is intentionally acceptable at one boundary. PSScriptAnalyzer supports SuppressMessageAttribute. A suppression should be narrow, reviewable, and justified—not a blanket way to make the dashboard green.
function Show-InteractiveTrainingBanner {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
'PSAvoidUsingWriteHost',
'',
Justification='This function is explicitly a human-only host adapter; it returns no reusable data.'
)]
param([string]$Message)
Write-Host $Message
}
Invoke-ScriptAnalyzer -Path ./src -SuppressedOnly |
Select-Object RuleName,ScriptName,Line,MessageParser errors cannot be suppressed. More generally, if the code can be improved cheaply, prefer fixing the code over suppressing the rule.
10. Automatic fixes need source control and review
Some analyzer diagnostics offer suggested corrections. Invoke-ScriptAnalyzer -Fix can apply supported fixes, and the cmdlet supports -WhatIf. Automatic rewriting is still a code change: review the diff, rerun tests, and verify file encoding when encoding matters.
# Preview supported corrections first:
Invoke-ScriptAnalyzer -Path ./src -Recurse -Fix -WhatIf
# If the preview is acceptable, apply on a clean source-control branch/worktree:
# Invoke-ScriptAnalyzer -Path ./src -Recurse -Fix
# git diff
# Invoke-Pester -Path ./tests11. Lab — refactor the intentionally bad script
A safer rewrite removes the unused value and dynamic evaluation, uses an approved verb, treats the path literally, and exposes SupportsShouldProcess. The password parameter disappears because the operation did not actually need a credential.
function Remove-TrainingCache {
[CmdletBinding(SupportsShouldProcess,ConfirmImpact='Medium')]
param(
[Parameter(Mandatory)]
[string]$Path
)
if (-not (Test-Path -LiteralPath $Path)) {
return [pscustomobject]@{ Path=$Path; Changed=$false; Reason='NotFound' }
}
if ($PSCmdlet.ShouldProcess($Path,'Remove training cache')) {
Remove-Item -LiteralPath $Path -Recurse -Force
return [pscustomobject]@{ Path=$Path; Changed=$true; Reason='Removed' }
}
[pscustomobject]@{ Path=$Path; Changed=$false; Reason='WhatIfOrDeclined' }
}
Invoke-ScriptAnalyzer -Path ./src/TrainingCache.ps1
Remove-TrainingCache -Path ./disposable-cache -WhatIfStatic analysis does not prove this function is correct. Pester tests should still verify NotFound, WhatIf, successful removal in TestDrive, and error behavior.
12. Common static-analysis mistakes
| Mistake | Why it fails | Better pattern |
|---|---|---|
| Treat zero diagnostics as proof | Rules cannot cover every business requirement or runtime state. | Combine analysis with tests and review. |
| Enable every rule without a target policy | Noise produces mass suppression. | Choose rules based on supported environments and risks. |
| Suppress without justification | Future reviewers cannot tell intentional design from hidden debt. | Use narrow documented suppression. |
| Auto-fix without diff/test review | Automated edits can alter semantics or encoding. | Preview, diff, test, then commit. |
| Only run analyzer locally | Developer environments drift. | Run the same policy in CI. |
13. Verification checklist
- You can explain what static analysis can and cannot prove.
- You can discover rules with Get-ScriptAnalyzerRule.
- You can run Invoke-ScriptAnalyzer and preserve diagnostic objects.
- You can create a repository settings file with explicit rule/severity policy.
- You can justify a narrow suppression and inspect suppressed diagnostics.
- You can refactor analyzer findings without replacing tests.
14. Knowledge check
Question 1. What is static analysis?
Question 2. Does a clean PSScriptAnalyzer run prove business correctness?
Question 3. Why use a settings file?
Question 4. When is suppression acceptable?
Question 5. What should follow an automatic -Fix operation?
15. Summary and next bridge
PSScriptAnalyzer provides fast feedback on risky patterns, style, safety, security, and compatibility. Its value comes from explicit project policy and disciplined handling of findings—not from chasing a zero count at any cost. The next lesson combines tests and analysis with coverage data, machine-readable reports, and quality gates that a CI runner can enforce.
16. Authoritative references
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this address.