PSScriptAnalyzer: The Ultimate PowerShell Script Analyzer and Linter
As you get more into DevOps and running CI/CD pipelines with PowerShell code, you will find that you want to have a way to check your code as the pipeline runs. PSScriptanalyzer is a free PowerShell module that provides the ability to check your PowerShell code in your pipelines for code quality and other issues. Let’s look at PSScriptAnalyzer and see how it is the ultimate linter for PowerShell code in CI/CD.
Table of contents
What does PSScriptAnalyzer do?
PSScriptAnalyzer is a PowerShell extension developed by the PowerShell team for use as a script analyzer for PowerShell script files and modules. It is a static code checker that scrutinizes PowerShell code to detect potential code defects in PowerShell files (using built-in rules), using best practices, and common syntax errors feedback. It helps developers improve the quality of their PowerShell scripts.
You can also use it as a stage in your CI/CD pipelines running PowerShell code to have the linting stage run before the code actually does anything in the environment. If errors or other bad code artifacts are detected, your CI/CD pipeline will fail the stage before it moves on to other aspects of the pipeline.
It then displays in its log output its findings. These allow the developer to check the recommendations and make sure these are corrected before rerunning the pipeline.
What does it check for?
There is a wide range of things the module checks for, and findings will result in informational warnings or error statuses. Note some examples of findings and guidelines you can receive with PSScriptAnalyzer and their severity to improve formatting, code performance and efficiency, security, and maintainability. You can find the full Microsoft documentation here: PSScriptAnalyzer Module – PowerShell | Microsoft Learn.
Informational
- Use only Approved Verbs UseApprovedVerbs
- Cmdlets names with unusable characters AvoidReservedCharInCmdlet
- Parameter names that can’t be used AvoidReservedParams
- Support confirmation requests UseShouldProcessForStateChangingFunctions and UseShouldProcessForStateChangingFunctions
- Must call ShouldProcess when the ShouldProcess attribute is present and vice versa UseShouldProcess
- Nouns should be singular UseSingularNouns
- Missing module manifest fields MissingModuleManifestField
- Version
- Author
- Description
- LicenseUri (for PowerShell Gallery)
- Switch parameters shouldn’t default to true AvoidDefaultValueSwitchParameter
Warning
- Avoid using aliases AvoidUsingCmdletAliases
- Avoid using Write-host
- Avoid using uninitialized variables
- Avoid using deprecated WMI cmdlets AvoidUsingWMICmdlet
- Avoid using empty catch blocks AvoidUsingEmptyCatchBlock
- Invoke existing cmdlets with correct parameters UseCmdletCorrectly
- Cmdlets should have ShouldProcess/ShouldContinue and Force parameter if using certain system-modifying verbs (Update, Set, Remove, New): UseShouldProcessForStateChangingFunctions
- Avoid using positional parameters AvoidUsingPositionalParameters
- Avoid using global variables AvoidGlobalVars
- Declared variables should be used after their assignment UseDeclaredVarsMoreThanAssignments
- Avoid using Invoke-Expression AvoidUsingInvokeExpression
Error
- Avoid using plain text passwords AvoidUsingPlainTextForPassword
- Avoid -Username and -Password parameters (use PSCredential instead): UsePSCredentialType
- Avoid hardcoding a -ComputerName parameter argument (information disclosure): AvoidUsingComputerNameHardcoded
- Avoid using ConvertTo-SecureString with plaintext (information disclosure): AvoidUsingConvertToSecureStringWithPlainText
Installation and Getting Started
To install PSScriptAnalyzer, we can use the Install-Module command. You can use this command in either Windows PowerShell or PowerShell Core.
As an example, to install the module, we can use the following command:
Install-Module PSScriptAnalyzer
In the above, we are forcing the installation and setting the scope to be available for all users. If you need to you can also use additional parameters:
Install-Module PSScriptAnalyzer -Force -Scope AllUsers -AllowClobber
The “allowclobber” parameter overrides warnings about installation conflicts. And then we can skip the publisher check.
Using PSScriptAnalyzer with Invoke-ScriptAnalyzer
The simple way to use the new PSScriptAnalyzer module is using the cmdlet:
Invoke-ScriptAnalyzer
To point the PSScriptAnalyzer to a PowerShell file, we can use the “-Path” parameter to specify where the PowerShell script is located.
Invoke-ScriptAnalyzer -Path powershellscript.ps1
Using the Recurse parameter
Another cool thing we can do is use the recurse parameter. With this parameter, it makes checking a large number of PowerShell script much easier. When you use the -Recurse parameter with the -Path parameter, it tells ScriptAnalyzer to not only analyze the scripts located directly at the path specified, but also to recursively analyze all scripts found in all subdirectories under that path.
Using PSScriptAnalyzer in CI/CD
The power of PSScriptAnalyzer really comes into play with CI/CD pipelines if you are using PowerShell scripts for automation. You can integrate it as a stage in your CI/CD pipeline process and automate the analysis of PowerShell scripts at every code check-in. Below, you can see the output of a Gitlab pipeline I am using with PSScriptAnalyzer checks with best practices identified in the diagnosticresults below.
Below is a run with even more output and recommended improvements to review as documentation, including uninitialized variables use, trailing whitespace, and aliases used:
This helps to make sure that every piece of code is scrutinized for issues before it is deployed which increases the quality of PowerShell code and leads to more stable and secure applications.
Adding PSScriptAnalyzer to a pipeline
How exactly do you add PSScriptAnalyzer to a pipeline? Well there are a few ways to do it. If you just want to run the PSScriptAnalyzer with all the checks and fail your pipeline on any type of message back, including informational, you can do something like the following in your gitlab-ci.yml file:
image:
name: vmware/powerclicore
before_script:
- apt-get update
- apt-get install -y smbclient
# Install PowerShell ScriptAnalyzer
- pwsh -Command "if (-not (Get-Module -ListAvailable -Name PSScriptAnalyzer)) { Install-Module PSScriptAnalyzer -Force -Scope AllUsers }"
stages:
- lint
- deploy
lint_powershell_script:
stage: lint
script:
# Run PowerShell Script Analyzer
- pwsh -Command "Invoke-ScriptAnalyzer -Path myscript.ps1 -Recurse
powercliscript:
stage: deploy
script:
- pwsh -File myscript.ps1
If you want to fail your pipeline for only “warning” and “error” messages from PSScriptAnalyzer, you can do this. Notice the lint stage is now calling a separate script for lining.
image:
name: vmware/powerclicore
before_script:
- apt-get update
- apt-get install -y smbclient
stages:
- lint
- deploy
lint_powershell_script:
stage: lint
script:
- pwsh -File run_psscriptanalyzer.ps1
allow_failure: false
only:
- main
powercliscript:
stage: deploy
script:
- pwsh -File poweredonvms.ps1
only:
- main
The contents of the run_psscriptanalyzer.ps1 script is the following:
Install-Module PSScriptAnalyzer -Force -Scope AllUsers -AllowClobber -SkipPublisherCheck
$analysisResults = Invoke-ScriptAnalyzer -Path poweredonvms.ps1 -Recurse
if ($analysisResults) {
$analysisResults | Format-Table | Out-String -Width 120 | Write-Output
$errorsAndWarnings = $analysisResults | Where-Object { $_.Severity -eq 'Error' -or $_.Severity -eq 'Warning' }
if ($errorsAndWarnings) {
exit 1
}
}
As you can see above, we refer to the PowerShell script checks as linting. What is it?
What is a linter?
A linter is a tool that analyzes source code to flag programming errors, bugs, style errors, and any less-than-desirable code. It helps enforce coding standards and improves code quality before it ships. It does this by identifying potential issues before the code is executed or compiled.
Linters are available for many programming languages, each tailored to the specific syntax for that particular programming language. By integrating a linter into the development process, you can catch and correct problems early, rather than having them make it into production. This helps ensure more reliable and maintainable code.
Customizing Rules for Your Needs
One of the strengths of PSScriptAnalyzer is the ability to customize and use it as you need for your particular environments. Users can enable, disable, or even create custom rules to suit their specific project requirements. It allows teams to inform users of their own coding standards and practices.
There is a special cmdlet that is part of the module to view the ScriptAnalyzer rules. You can use the cmlet:
Get-ScriptAnalyzerRule
It allows you to see which rules are checked and detailed information about each rule.
You can also as we did above, return the outputs that you want and send this back to the pipeline. This allows you to stop the pipeline based on certain messages returned in the script analysis.
Wrapping up PSScriptAnalyzer
PSScriptAnalyzer is a great tool for making sure your PowerShell code is clean and without issues before it is used in production. It can be used as a standalone checker or integrated into your CI/CD pipeline solutions. PSScriptAnalyzer helps developers make sure their PowerShell scripts are secure and aligned with best practices standards.
Even in the home lab environment, it is a great way to start looking at the quality of scripts you may be using and implement CI/CD pipelines to automate workflows in your lab to make things easier and more secure and learn more DevOps skills to carry into production.