Overview
- Suggested Best Practices
- Write your function with one purpose
- Follow naming conventions
- Use the built in comment-based help system
- Let PowerShell do the work for you
- Use advanced parameters
- Provide flexibility with your parameters
- Document your code for yourself, readers, and users
- Avoid dependencies
- Provide error handling with helpful messages
- Do not break the user’s environment
- Test, Test, Test!
- If your function provides output, use objects
- Why bother?
- Illustrating the best practices
- Get-InstalledSoftware in action
- Helpful resources
I spend a good deal of time wrapping common tasks into PowerShell functions. Here are a few best practices I’ve picked up along the way. My apologies if I miss any attributions!
Suggested Best Practices
Write your function with one purpose
- Write your function with one purpose. Don’t build in everything but the kitchen sink.
- This is one of PowerShell’s strengths – there are existing functions to work with input or output objects, or you can write your own set of functions to work together.
Follow naming conventions
- Follow naming conventions.
- Use the Verb-Noun format, use an approved verb, and ensure that your Noun is unique and will not collide with another author’s function now or in in the future.
- Approved Verbs
- Example: I’m writing commands to work with a Hyper-V lab. Set-Lab and Get-Lab are generic and may be used by another author. I can add a prefix like HV to avoid this – Set-HVLAB and Get-HVLab
- Use common parameter names and types as appropriate.
- Example: Use ComputerName to specify systems. Do not use ComputerNames, Computer, PC, or any other non-standard parameter name. If desired, provide an alias for the parameter.
- Use the Verb-Noun format, use an approved verb, and ensure that your Noun is unique and will not collide with another author’s function now or in in the future.
Use the built in comment-based help system
- Use the built in comment-based help system. At a minimum, provide a helpful synopsis, description, parameter (for all), and example
Let PowerShell do the work for you
- Let PowerShell do the work for you. Always use [cmdletbinding()], which allows you to take advantage of the following:
- Common parameters such as -Verbose and -ErrorAction
- SupportsShouldProcess to allow -Confirm and -Whatif parameters
- Set advanced [Parameter()] attributes such as Mandatory and ValueFromPipeline
- Use Parameter Sets
- About_Functions_Advanced
- About_Common_Parameters
- About_Functions_Advanced_Parameters
- About_Functions_CmdletBindingAttribute
Use advanced parameters
- Use advanced parameters for validation, accepting pipeline input, specifying mandatory parameters, and other functionality, where possible and appropriate.
Provide flexibility with your parameters
- Provide flexibility with your parameters. Provide default values, allow arrays instead of single objects, allow wildcards, and provide other helpful parameter features.
- Example:
[string[]]$ComputerName = $env:computername
is more helpful than[string]$ComputerName
- Example:
Document your code for yourself, readers, and users
- Document your code for yourself, readers, and users.
- Use write-verbose, write-debug and write-error to provide insight at the shell
- Comment your code in everyday language for readers. If you used a specific command or logic for a reason, explain why it was necessary and why changing it could break things.
- Use full command names and full named parameters. This makes the code more readable. It also prevents issues that could arise if you rely on aliases or positional parameters.
Avoid dependencies
- Avoid dependencies. This includes external scripts and modules, binaries, or features exclusive to PowerShell or .NET Framework versions. If you must include dependencies, be sure to indicate this and provide appropriate error handling.
- Example: Get-ADGroupMember requires the ActiveDirectory module. Instead of relying on this, include or write your own function.
- Example: To create a new object, use
New-Object -TypeName PSObject -Property @{A=1; B="Two" } | Select-Object A, B
instead of[PSCustomObject]@{A=1; B=”Two”}
to provide compatibility with PowerShell 2.
Provide error handling with helpful messages
- Provide error handling with helpful messages
Do not break the user’s environment
- Do not break the user’s environment. Don’t touch the global scope.
Test, Test, Test!
- Test, Test, Test! Test any reasonable scenario your function might run under.
- Test with and without a profile. Test with 64 and 32 bit PowerShell hosts. Test with the ISE and Console Host. Test with a single-threaded apartment and multi-threaded apartment. Test with and without the administrative token, with and without actual administrative authority.
If your function provides output, use objects
- If your function provides output, use objects.
- Do not output strings. Do not use Write-Host. Do not format the results. You and your users will get the most out of PowerShell when you provide output in objects, that can be passed down the pipeline to other commands.
- Creating Custom Objects
Why bother?
- Following these best practices will help you and the greater PowerShell community if you chose to share your code.
- Your function will fit into the PowerShell world, enabling integration with the many technologies PowerShell can work with.
- Your function will be usable by wider audiences, who may even provide suggestions and tweaks to help improve it.
- Your function will be flexible and gracefully handle various scenarios you throw at it.
- Your function will last. If you avoided or accounted for dependencies, your function should withstand changes to PowerShell, the .NET Framework, and the user’s environment.
- These practices apply to scripts as well. You can use the majority of these best practices when writing scripts, rather than functions.
Illustrating the best practices
We will look at Get-InstalledSoftware, a quick function that extracts installed software details from the registry.
Write your function with one purpose
This function does one thing: get installed software.
Follow naming conventions
Get-InstalledSoftware follows the Verb-Noun naming format, uses an approved verb, and uses typical parameter names such as ComputerName… but the function name is not unique. In fact, there is another script out there with the same name. Perhaps I should have chosen a better example!
Use the built in comment-based help system
The help system provides a synopsis, a description that points out prerequisites, describes each parameter, provides two examples, and provides a link that will take the user to the Technet Gallery page if they use Get-Help Get-InstalledSoftware –Online
Let PowerShell do the work for you
The function uses [cmdletbinding()] and many of the features it enables.
Use advanced parameters
This function uses advanced parameters for computername. This allows input from the pipeline (e.g. an array of strings), input from the pipeline by property name (e.g. an array of objects with a computername property), and validates that the argument is not null or empty.
Provide flexibility with your parameters
ComputerName is given a default value of the local machine and allows an array of strings rather than a single string. The Publisher and DisplayName parameters are used with the –Match operator and can thus take in regular expressions.
Document your code for yourself, readers, and users
The code uses Write-Verbose and Write-Error. Comments explain what is happening. The ‘help’ information describes prerequisites, and if connectivity fails, verbose output suggests where to start troubleshooting. Aliases are not used in the function.
Avoid dependencies
This code does depend on certain factors – privileges, connectivity, and the Remote Registry service. This is detailed in the help information and in the verbose output. Language, including the output objects we create, is compatible with PowerShell v2.
Provide error handling with helpful messages
Try/Catch blocks are used to capture errors where they would likely occur, and are used in a way that will allow continued processing if errors occur. For example, if multiple computers are specified and one fails, we move to the next computer (continue), rather than breaking execution of the command.
Do not break the user’s environment
The global scope is not altered by this function
Test, Test, Test!
This script was tested in a limited number of expected scenarios. With and without a profile. In the ISE and console host. With and without the administrative token.
One scenario that illustrates the importance of testing is this command’s behavior in a 32 bit session on a 64 bit machine. In this scenario, the script will miss 64 bit items, and will pull double copies of everything else (the native and Wow6432Node keys will point to the same location). I added this to the description. Ideally I should test for and handle this, but doing so would add undue overhead to a lightweight function for what I consider a niche scenario.
If your function provides output, use objects.
This function provides object based output. Not text. Not a CSV. You can use the output with any number of built in or custom commands.
Get-InstalledSoftware in action
-
The end user can use the built in Get-Help command for help. The online switch takes you right to the TechNet gallery site.
-
We can pass in multiple computers and filter Publisher and DisplayName using regular expressions
Helpful resources
The following resources will provide further help and suggestions for best practices when writing PowerShell.
- PowerShell Best Practices: Advanced Functions - Ed Wilson
- PowerShell Best Practices: Simple Functions - Ed Wilson
- Grown-Up PowerShell Functions – Laerte Junior
- Advanced Functions Part 1, Part 2, Part 3, Update, Template – Richard Siddaway
- Writing Cmdlets in Script – Don Jones
- The Advanced Function Lifecycle – Don Jones
- My 12 PowerShell Best Practices – Don Jones
- PowerShell Script Module Best Practices - William Kempf
- My 10 best practices leading to the Scripting Games 2012 – Part 1, Part 2 – Ifiok Moses
- The Unofficial PowerShell Best Practices and Style Guide
- Other PowerShell resources I’ve found helpful – Cheat sheets, books, blogs, videos, etc.
Good luck! If you do end up writing advanced functions, please consider posting them to websites like PoshCode, TechNet Script Gallery, or GitHub!