Functions are a common occurrence in PowerShell and to truly understand the PowerShell language, it's important that you know how functions work. In this article, we're going to show how a PowerShell function evolves from basically nothing all the way to having parameters using validation attributes.
To demonstrate this, let's first start off with a function that can't get any simpler.
function Write-Log {
[CmdletBinding()]
param()
}
When run inside of a PowerShell session, this function will execute but will do nothing because there's no code to execute. PowerShell simply executes the function itself and returns.
For the function to actually do something, we need to add some code. Code is added in between the param block and the last curly brace. You can see below that I'm making my Write-Log function return the string "I did something" to the console.
function Write-Log {
[CmdletBinding()]
param()
'I did something'
}
PS> Write-Log
I did something
Our function is called Write-Log so I'm assuming that this will be a function that will eventually write some kind of text to a log file. Because we won't want to write the same thing to a log file every time, we need to provide some way to change the message when the function runs. To do that, we will add a parameter. Parameters allow the user to pass different values into the function at run-time. This allows the function to gather dynamic input at runtime.
To add a parameter, I'll add a variable. In this case, the variable is called Message. I'll add it inside of the param block as shown below. You can then see that I can reference that parameter inside of the function itself. When run, the function will return whatever value I pass to the Message parameter.
function Write-Log {
[CmdletBinding()]
param($Message)
$Message
}
PS> Write-Log -Message 'I did something'
I did something
You don't have to stop at just one parameter. We can add as many as we want here. Below, I'm adding a Severity parameter, providing a value when the function is run and you can see that it exhibits the same behavior.
function Write-Log {
[CmdletBinding()]
param($Message, $Severity)
"$Message - Severity: $Severity"
}
Write-Log -Message 'I did something' -Severity 1
Now that we know how to handle parameters, we can get into parameter types. A parameter has a type just like anything else in PowerShell. Above, it was using a type but it accepted any kind of object imaginable. The parameter wasn't explicitly typed. It's good practice to do this on all parameters to ensure only the values you expect are passed.
To define a type, we can add the type in square brackets right before the parameter is declared.
function Write-Log {
[CmdletBinding()]
param([System.ServiceProcess.ServiceController]$Message)
$Message
}
Once an explicit type is assigned to the parameter, PowerShell will only accept input of that type or an object that it can convert. In this example below, I'm passing the boolean value $false to the Message parameter. You can see that PowerShell won't allow it. This is because it can't convert a boolean type to a ServiceController type.
However, if we pass a ServiceController object that Get-Service returns, it works just fine.
PS> Write-Log -Message $service
Status Name DisplayName
------ ---- -----------
Stopped AdtAgent Microsoft Monitoring Agent Audit Fo...
We can also use parameter attributes. Parameter attributes allow us to define different characteristics of each parameter which determines how it works. In the example below, I'm using the Mandatory parameter. These attributes forces the user to pass a value to the Message parameter else the function will not run.
I'm also setting a default value on the Severity parameter. This allows me to force Severity to always be 1 unless it is overridden at run time by passing a value to that parameter.
function Write-Log {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Message,
[Parameter()]
[int]$Severity = 1
)
"$Message - Severity: $Severity"
}
Finally, we can use parameter validation attributes. Parameter validation attributes are a great way to restrict what values are passed to a parameter. In the example below, I'm using the ValidateRange() validation attribute. This attribute is used on parameters of type integer to define a range of allowed numbers. In this example, I am only allowing values of Severity to be 1-5. Any other value not in that range will fail.
function Write-Log {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[pscustomobject]$Message,
[Parameter()]
[ValidateRange(1, 5)]
[int]$Severity
)
"$Message - Severity: $Severity"
}
Wrap Up
By now you should have a basic understanding of how functions work in PowerShell. We have covered everything that you need to know to get started building useful functions in PowerShell. I encourage you to begin noticing times in your scripts where a function would be useful and begin buiding them. You'll see over time that your code will become cleaner and much easier to read.
Adam Bertram
Adam Bertram is a 25+ year IT veteran and an experienced online business professional. He’s a successful blogger, consultant, 6x Microsoft MVP, trainer, published author and freelance writer for dozens of publications. For how-to tech tutorials, catch up with Adam at adamtheautomator.com, connect on LinkedIn or follow him on X at @adbertram.