PowerShell How-To
Best Practices for Designing PowerShell Functions
While not everyone will code the same, here are some of my go-to tips for creating functions.
- By Adam Bertram
- 10/27/2016
If you've been building PowerShell scripts for a while now you've surely came across instances where you need to reuse a particular snippet of code. Rather than copying and pasting that snippet of code over and over again, it's much better to save that snippet into a form to where it can be called. This is when you'll dive into creating functions. Functions are a great way to "save" that snippet of code for reuse and to make your code more modular which makes it easier to read and easier to troubleshoot. But, not all functions are created equal.
In this article, I'll be going over not only how to create a function in PowerShell but some best practices you can apply. To do this, I'll be expecting you to have at least PowerShell v4 installed on your machine to follow along.
Best Practice 1: Use Advanced Functions
In PowerShell, you can build basic and advanced functions. Basic functions are severely limited in built-in capabilities compared to advanced functions. Have you ever seen instances of people using the Verbose parameter to get that yellow text or perhaps –ErrorAction to suppress errors coming from a function? These and many others all come standard when you build an advanced function.
"Converting" a basic function to an advanced function is easy. Simply append the CmdletBinding keyword to the function or use the [Parameter()] keyword when defining at least one parameter. Here's an example below where I'm highlighting the two keywords.
function Do-Something {
[CmdletBinding()]
param(
[Parmeter()]
[string]$Param1
)
}
Advanced functions give you many advantages over basic functions. If you're not building advanced functions today, I highly encourage you check them out. You can learn more with my Pluralsight course entitled Building Advanced PowerShell Functions and Modules.
Best Practice 2: Pick the Right Parameters
When building functions, it's important to not statically place any elements inside of your functions that might change in the future. What do I mean by this? Take this example below where I'm reading a text file as part of a bigger process.
function Do-Something {
[CmdletBinding()]
param()
## some code is here
Get-Content –Path C:\Computers.txt
## some code is here
}
This works now since you have a text file called Computers.txt on your hard drive but what about tomorrow or the next day? Will you always have a file located there? Maybe not. If you did and needed to use that file in your function, you'd have to go into your function and modify it every time. It's much better to create a parameter from it so you can pass into the function whatever file you want without having to modify the function itself.
You could turn that file path into a parameter and then pass in whatever file name you wanted making your function more flexible.
function Do-Something {
[CmdletBinding()]
param(
[Parameter()]
[string]$FilePath
)
## some code is here
Get-Content –Path $FilePath
## some code is here
}
Do-Something –FilePath 'C:\Computers.txt'
Best Practice 3: Use Parameter Validation
It's always a best practice to limit the amount of input your function gets as narrowly as possible. This makes your function simpler which prevents any unforeseen problems. PowerShell has a number of different parameter validation attributes that allow you to validate what kind of input each of your parameters can accept.
Let's use the parameter above (FilePath) as an example. When this parameter is used in the function, what prerequisites need to be there to ensure your code will work? Well, the file needs to exist, right? If not, the code is going to error out. It's best to catch this ASAP and that place is in the parameter itself. PowerShell provides a validation attribute called ValidateScript that allows you to ensure this file exists before the function is allowed to proceed.
function Do-Something {
[CmdletBinding()]
param(
[Parameter()]
[ValidateScript({ Test-Path –Path $_ -PathType Leaf })]
[string]$FilePath
)
## some code is here
Get-Content –Path $FilePath
## some code is here
}
To add this parameter attribute, notice how I've added it inside of the parameter block right above the FilePath parameter declaration. The ValidateScript attribute require a scriptblock where you can run any kind of code you'd like. Just make sure it returns $true to pass the validation. Above, I'm running Test-Path on the file to ensure it exists replacing the path to the file with $_ to represent the current parameter value being used.
Below you can see where I'm executing the function and I'm now not allowed to pass in a file that does not exist.
The more functions you create in PowerShell, the better you will become. Designing PowerShell functions, like coding, is an art. There are lots of shortcuts and best practices that you will come up with yourself to make your coding easier over the long run. Use these best practices I've shown you here as a start and to give you an idea on where to start designing better PowerShell functions.
About the Author
Adam Bertram is a 20-year veteran of IT. He's an automation engineer, blogger, consultant, freelance writer, Pluralsight course author and content marketing advisor to multiple technology companies. Adam also founded the popular TechSnips e-learning platform. He mainly focuses on DevOps, system management and automation technologies, as well as various cloud platforms mostly in the Microsoft space. He is a Microsoft Cloud and Datacenter Management MVP who absorbs knowledge from the IT field and explains it in an easy-to-understand fashion. Catch up on Adam's articles at adamtheautomator.com, connect on LinkedIn or follow him on Twitter at @adbertram or the TechSnips Twitter account @techsnips_io.