PowerShell: Building Functions
Posted on: January 5th, 2025
One of the more useful PowerShell tricks I've learned over the years is making functions. Functions can be incredibly simple or complex, single lines or several hundreds. These are great for managing repetitive tasks and streamlining specific workflows. There is no specific limitation on how long or complex your functions can be; the sky is the limit.
Creating A Function
Starting on the simple side, we can set up our function by simply declaring a name for the function and a cmdlet in a set of scriptblock.
### Create a function that displays a greeting when called
function WriteGreeting {
Write-Host "Hello $name!"
}
$name = [System.Environment]::UserName
### Call function to execute
WriteGreeting
Entering WriteGreeting into your CLI or placing it into a script will execute what is in the scriptblock, in this case, outputting a greeting. Notice that you can include a variable in your function before declaring its value. Simple, no additional input needed. What if you needed some extra flexibility like additional input that you can provide? Thats possible by adding some arguments or parameters. Below is an example of checking whether a number is even or odd.
function Check-EvenOdd {
### Declare parameters for input
param (
### Parameter is required, must be integer
[Parameter(Mandatory=$true)]
[int]$Number
)
### Determine if there is a remainder by using the modulus (%) arithmetic operator
### No remainder when divided by two indicates number is even, remainder of 1 indicates odd
if (($number%2) -eq 0) {
Write-Output "$number is even."
} else {
Write-Output "$number is odd."
}
}
Check-EvenOdd -Number 120 # "120 is odd."
Check-EvenOdd -Number 133 # "133 is even."
Calling the Check-EvenOdd function accompanied with an integer will answer the question. Adding the parameter allows you to input it similarly to other cmdlets, in this case with "-Number" and an integer. Making it a requirement causes the function to prompt you for the input if it is not provided before running. We can add multiple parameters since the param block works as a comma separated list. Note that these values are discarded after the function is run. These variables are only valid within the confines of the function. We can change that by declaring them as global variables by using "$Global:" followed by the name of the variable.
function OutputTest {
$Global:globalVariable = 'Global Variable'
$localVariable = 'Local Variable'
$globalVariable # Will output 'Global Variable'
$localVariable # Will output 'Local Variable'
}
### Call function
OutputTest
$globalVariable # Will output 'Global Variable'
$localVariable # Will not have a value
Processing Blocks
PowerShell functions have blocks that allow you to break up processing efficiently: the begin, process, and end blocks. These blocks process what is in them differently from each other and is very useful if the function is processing multiple items. Using these blocks in your function is entirely optional. Not using them, as in the previous examples above, PowerShell considers them to be in an imaginary end block. Note that if you do use blocks, everything within the function must be placed within a block. Nothing may be outside of or in between blocks.
The begin block is processed only once when the function is called. Everything in the begin block is run before moving on and is not run again unless the function is called again. The process block will loop for each input the function is provided, run once if only one input is provided, or be skipped entirely if no input is provided. The end block, like the begin block, is only run once after all prior processing is complete. It is the last thing your function does before finishing. Here is a simple example of all three blocks in use with the input being piped in:
function FunctionBlocks {
param (
### Set the input to accept our pipeline below and be required
[Parameter(ValueFromPipeline=$true,Mandatory=$true)]
[int]$number
)
begin {
### This write-output will only run once before processing each number
$last = $testArray | select -Last 1
Write-Output "Watch me count to $last..."
}
process {
### Output each number provided, looping until all have completed
Write-Output $number
}
end {
### Output after all processing is complete and then finish function
Write-Output "Counting complete!"
}
}
### Declare array and pipe to function
$testArray = @(1..10)
$testArray | FunctionBlocks
<#
Output will resemble the following:
Watch me count to 10...
1
2
3
4
5
6
7
8
9
10
Counting complete!
#>
There are more things to know and do with functions than what is shown here, and more is being added, like the clean block being introduced in PowerShell 7.3. As always, check the Microsoft Learn article for more information and examples. Functions are incredibly useful and completely changed how I build out some of my more intricate scripts. They are great for efficiently running similar lines multiple times in a single script or having more complex processing with input.