PowerShell How-To

The Evolution of a PowerShell Function Parameter

Making code adaptable is a key skill for any coder. Here's a primer on how to stretch function parameters to fit a variety of situations.

As with any programming language, a PowerShell coder is always adapting to environmental circumstances and changing code.

One day, a simple script may do; the next, you might find yourself having to create a module with lots of functions. Refactoring code is commonplace for anyone who's improving their PowerShell skills.

An area of PowerShell that one becomes better at over time is defining function parameters.

Function parameters are highly flexible constructs in PowerShell that allow you to build them and to pass values to them in a lot of different ways. In this article, we're going to go over the most basic way to define function parameters all the way up to some fancy-looking parameters that do just about everything!

Let's start with a function that queries one or more servers for some basic inventory and writes the result to a .CSV file with some basic parameters.

function Get-ServerInventory {
    [CmdletBinding()]
    param(
        $ComputerName,
        $CsvFilePath
    )

    foreach ($c in $ComputerName) {
        Get-CimInstance -ComputerName $c -ClassName 'Win32_OperatingSystem' | Export-Csv -Path $CsvFilePath -Append
    }

}

This function will work as-is, but I'm assuming a lot of things here. I'm assuming that:

  1. I'll remember to use both parameters since they are both required.
  2. The value passed to ComputerName is a string or collection of strings that Get-CimInstance understands.
  3. The CSV I specify isn't already being used for something else and can be written to.
  4. All computers will be online.

When it comes to code, you shouldn't assume anything but rather keep the code built in a way that eliminates potential problems before they crop up. We can make these parameters much better.

The most basic improvement we can do here is adding types to the parameters. By setting types to parameters, it restricts the type of objects that can be used and won't even execute the function if the wrong type is passed.

[string[]]$ComputerName,
[string]$CsvFilePath

We can now be sure that only one or more strings are passed to ComputerName and a single string is passed to CsvFilePath.

Next, since we know CsvFilePath and ComputerName are both needed in the function, both parameters need to be required. We shouldn't be reminded it's necessary when the function errors out. Instead, this should be checked ahead of time in the parameters themselves. We should set these both to be mandatory.

[Parameter(Mandatory)]
[string[]]$ComputerName,

[Parameter(Mandatory)]
[string]$CsvFilePath

Next, perhaps I'm using the same .CSV file a lot and would rather not provide that every time. I can set a default value to CsvFilePath that will always be set to that unless I choose to override it.

[Parameter(Mandatory)]
[string[]]$ComputerName,

[Parameter(Mandatory)]
[string]$CsvFilePath = 'C:\ServerInventory.csv'

Next, I need to ensure each computer that I pass to ComputerName is online before Get-CimInstance queries them. To do this, I can use one of the many great parameter validation attributes called ValidateScript to ping each server before we even enter the function. I could also do the same for CsvFilePath to ensure that no .CSV file exists before starting if I wanted to.

[Parameter(Mandatory)]
[ValidateScript({
    if (Test-Connection -ComputerName $_ -Quiet -Count 1) {
        $true
    } else {
        throw "The computer $($_) is not available."
    }
})]
[string[]]$ComputerName,

[Parameter(Mandatory)]
[ValidateScript({
    if (-not (Test-Path -Path $_ -PathType Leaf)) {
        $true
    } else {
        throw "The CSV file $($_) already exists."
    }
})]
[string]$CsvFilePath = 'C:\ServerInventory.csv'

By using various parameter validation attributes like this, you can ensure that all of the values that are passed to your parameters are expected, and the environment that the function running in is in a state that your code can handle.

We could do a lot more with these parameters, such as looking into parameter sets, adding additional parameter validation attributes and so on, but we'll stop here. The important thing to remember is even though a parameter may work in one situation, doesn't mean it will work in all situations. It's important to build your code in a way that's flexible and will work regardless of the environment -- or at least handle errors gracefully.

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.


comments powered by Disqus
Most   Popular