Microsoft’s PowerShell Script Analyzer (PSScriptAnalyzer) module allows you to perform static code analysis on PowerShell scripts.
Timothy Warner
Latest posts by Timothy Warner (see all)

Static code analysis refers to reviewing and refactoring code without actually running it. Specifically, the Windows PowerShell team at Microsoft and the PowerShell community at large have developed a body of best practices for quality PowerShell scripting.

You can learn more about this collection of PowerShell scripting best practices by examining the following resources:

Anyway, the PowerShell development team created the PowerShell Script Analyzer (PSScriptAnalyzer) module as a way to help us administrative scripters check our code against best practices. As has become their custom of late, the project is open-source and hosted at GitHub. The open-source nature of the project means that you can clone the project and contribute changes and additional features yourself via Git pull requests.

Here are three good reasons for you to consider running the Script Analyzer against all your PowerShell scripts from now on:

With all that said, let’s use the PowerShell Script Analyzer.

Installing PSScriptAnalyzer

Although PSScriptAnalyzer is (supposedly) included in WMF v5, we’ll use PowerShellGet to install the latest and greatest version from The PowerShell Gallery. I say “supposedly” because some WMF v5 preview builds include the module, and some don’t. Welcome to rapid deployment cycles! J

Install-Module -Name PSScriptAnalyzer -Force

Let’s examine the contents of the PSScriptAnalyzer module:

Get-Command -Module PSScriptAnalyzer | Select-Object -Property CommandType,Name,Version | Format-Table -AutoSize

CommandType       Name                       Version
-----------       ----                       -------
Cmdlet           Get-ScriptAnalyzerRule     1.2.0
Cmdlet           Invoke-ScriptAnalyzer      1.2.0

I’m a big fan of reading PowerShell help with the -ShowWindow switch so I can put my console/ISE and help file side by side. Don’t forget to run Update-Help first!

Get-Help Invoke-ScriptAnalyzer -ShowWindow
Get-Help Get-ScriptAnalyzerRule -ShowWindow
Get-Help about_PSScriptAnalyzer -ShowWindow

Now, let’s scan the built-in ruleset:

Get-ScriptAnalyzerRule | Select-Object -Property CommonName | Sort-Object -Property CommonName

CommonName
----------
Avoid Default Value For Mandatory Parameter
Avoid Invoking Empty Members
Avoid Using Cmdlet Aliases
Avoid Using ComputerName Hardcoded
Avoid Using Deprecated Manifest Fields
Avoid Using Empty Catch Block
Avoid Using Get-WMIObject, Remove-WMIObject, Invoke-WmiMethod, Register-WmiEvent, Set-WmiInstance
Avoid Using Invoke-Expression
Avoid Using Plain Text For Password Parameter
Avoid Using Positional Parameters
Avoid Using SecureString With Plain Text
Avoid Using ShouldContinue Without Boolean Force Parameter
Avoid Using Username and Password Parameters
Avoid Using Write-Host
Basic Comment Help
Cmdlet Singular Noun
Cmdlet Verbs
DSC examples are present
Dsc tests are present
Extra Variables
Module Manifest Fields
No Global Variables
Null Comparison
PSCredential
Reserved Cmdlet Chars
Reserved Parameters
Return Correct Types For DSC Functions
Should Process
Switch Parameters Should Not Default To True
Use BOM encoding for non-ASCII files
Use Cmdlet Correctly
Use identical mandatory parameters for DSC Get/Test/Set TargetResource functions
Use Identical Parameters For DSC Test and Set Functions
Use OutputType Correctly
Use ShouldProcess For State Changing Functions
Use Standard Get/Set/Test TargetResource functions in DSC Resource
Use UTF8 Encoding For Help File
Use verbose message in DSC resource

Each of those rules is explained, to a greater or lesser degree, in the project’s GitHub repository.

Running a scan

Take a look at the following screenshot. In it, I wrote a PowerShell script called bad-script.ps1 that intentionally violates several PSScriptAnalyzer rules.

Our poorly-coded script

Our poorly coded PowerShell script

At base, my Retrieve-UptimeValues script actually does something quite useful; it reports system uptime in a reader-friendly format.

Invoke-PSScriptAnalyzer -Path ‘.\bad-script.ps1’

The following screenshot shows the results of my best-practices scan. I extended the pipeline to format the results in a more eye-friendly manner.

The results of our best practices scan

The results of our best-practices scan

What’s good is that we have no errors—only warnings. That said, I was surprised to see that some of my code didn’t trigger errors that I know are tracked in the default ruleset. Bugs are part of any open-source, community project.

At any rate, here’s the Retrieve-UptimeValues script in the Windows PowerShell ISE with the detected violations highlighted in yellow:

I highlighted the PSScriptAnalyzer rule violations.

I highlighted the PSScriptAnalyzer rule violations.

Now, I’ll explain my detected mistakes by line number:

  • 1: We have two problems here. First, we’re using an unapproved verb (use Get-Verb to see the full list of approved command verbs). Second, command nouns should be singular, not plural.
  • 10: To improve our code readability, we should avoid command aliases. Instead, we expand command names and parameter names. Also, we use named parameters instead of positional parameters wherever possible.
  • 13: Once again, we want to avoid the use of command aliases (here, cls is a built-in alias for Clear-Host).
  • 14: Unless we’re actually formatting output for the screen, we should use Write-Output so we have string data remaining in the pipeline for potential future processing.
  • 16: If we use Try/Catch blocks, then we need to actually code an exception handler in our Catch block.
  • 18: What’s the point of writing PowerShell functions unless they are dynamic? To that point, we need to remove hard-coded references and instead define parameter variables.

As I said, there’s some other wacky stuff in my script that wasn’t flagged by the script analyzer:

  • I defined an optional input parameter named $futureParam that’s actually not used in the function body. In fact, with a simple script such as this, it’s probably overkill to use the CmdletBinding directive at all.
  • On line 12, I’m using double quotes. Technically, we can enclose string data with single or double quotes. PowerShell best practice specifies that we use single quotes unless we need to automatically expand variables.
  • On line 11, I use get-date in lowercase. This is a picky detail, perhaps, but your command/function calls should use title case (Get-Date).

Here’s my slimmed-down, refactored code:

Our function is in much better shape now.

Our function is in much better shape now.

Working with PSScriptAnalyzer rules

Your IT department may have its own set of priorities in terms of quality PowerShell scripting. We can play around with Invoke-ScriptAnalyzer to, for instance, exclude one or more default rules:

Invoke-ScriptAnalyzer -Path .\bad-script.ps1 -ExcludeRule PSUsesSingularNouns

Or we can do just the opposite, specifying only some rules:

Invoke-ScriptAnalyzer -Path .\bad-script.ps1 -IncludeRule PSUseApprovedVerbs,PSAvoidUsingCmdletAliases

Finally, if you prefer to do static code analysis with the PowerShell Script Analyzer from the PowerShell ISE or another scripting tool, consider the following integration points:

6 Comments

Leave a reply

Please enclose code in pre tags: <pre></pre>

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2024

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending
WindowsUpdatePreventer

Log in with your credentials

or    

Forgot your details?

Create Account