Use PowerShell Functions to Quickly Simplify Your Scripts

PowerShell

Are you creating PowerShell scripts for the first time? One important skill to master is how to write PowerShell functions, which work as a block of code that you can easily reuse. In this article, you will learn about the basics of building your first function and how to call it when you need it.

If you want to follow along with the article, you will want to use either Windows PowerShell ISE or Microsoft Visual Studio Code with the PowerShell extension.

Ready? Let’s learn about PowerShell functions!

What is a PowerShell function?

Imagine for a moment that you have a toolbox with a hammer, a wrench, and a pencil. Each tool performs a specific task. We can use independently these tools or in combination with each other to complete a task.

PowerShell functions are the tools in your toolbox.

A PowerShell function is a block of code that performs a specific task or set of tasks. Functions in PowerShell are like functions in other programming languages and can encapsulate a piece of code that can be called multiple times from within a script or from the PowerShell console.

What makes PowerShell functions useful

PowerShell functions are very useful for three main reasons: reusability, modularization, and abstraction.

Reusability

Functions allow you to encapsulate a specific piece of code that can be used repeatedly throughout your PowerShell scripts or modules. Once you define a function, you can call it multiple times with different inputs to perform the same action without having to rewrite the code each time.

Modularization

PowerShell functions make it easier to organize your code into smaller, more manageable chunks. By breaking down your script into smaller functions, you can focus on one task at a time, making it easier to read, maintain, and troubleshoot.

Abstraction

Functions allow you to abstract the details of a particular task, making it easier to use and understand. By hiding the implementation details of a task behind a function, you can create a simpler interface that other users can interact with without needing to know the details of how the task is performed.

Overall, PowerShell functions are a powerful tool for automating tasks and managing systems. By learning how to write and use functions effectively, you can become a more proficient PowerShell user.

How to create a PowerShell function

Now that you have a basic understanding of the role functions play, let’s get started creating your first one. You can see the syntax of a basic function below:

function Verb-Noun {
    code
}

Every function starts out with the keyword of the function followed by a name (verb-noun) for the function. The proper way to name a function is by using an approved verb-noun format. All code between the curly braces will be the tasks the function will perform when called.

PowerShell functions do’s and don’ts

  • Remember the KISS principle, meaning accomplishing a single task in the simplest way possible. The goal of a function is to do one job and do it well with the least amount of code as possible.
  • Avoid using aliases and positional parameters in your code. You should format for readability, as the next person troubleshooting your code could be you!
  • Don’t hard-code values. It’s best to create variables and use parameters.

Basic PowerShell functions

Let’s try it out by making a basic function that will fetch the first 5 processes running on your computer. The name of the function is Get-FiveProcesses, which will filter the first five processes passed down the pipeline from Get-Process:

function Get-FiveProcesses {
    Get-Process | Select-Object -First 5
}

Saving a function is just like saving a PowerShell script. Simply save the function using the same name used in your code, Get-FiveProcesses.ps1. Once saved, load the script using the dot-sourcing technique into your session. Dot-sourcing allows you to run a PowerShell script in the current scope instead of in the script scope. Now you can run Get-FiveProcesses just like built-in cmdlets:

After PowerShell functions, we can run it like a built-in cmdlet
After saving a PowerShell function, we can run it like a built-in cmdlet (Image credit: Petri/Bill Kindle)

Your function will remain loaded for the rest of your PowerShell session. When you close your session, your function will need to be reloaded again.

Advanced PowerShell functions

Advanced PowerShell functions give you some powerful features that make your functions professional grade. What do I mean by that? Professional grade scripts include help, possibly some error handling, parameters, or accepting pipeline input.

Using the previous example, Get-FiveProcesses was a basic function that included no help. Let’s make some changes to this script by adding some comment-based help:

function Get-FiveProcesses {

    <#
    .SYNOPSIS
        Gets the first five processes.
    .DESCRIPTION
        Get-FiveProcesses performs basic filtering of processes to show a user
        only the first five processes of a host system.
    .NOTES
        This function works on Windows and Linux running PowerShell 7+
    .LINK
        Be sure to check out more PowerShell articles on https://petri.com
    .EXAMPLE
        Get-FiveProcesses
        Get the first five processes.
    #>
   
    Get-Process | Select-Object -First 5
       
}

Now, if you load your function again, you will use PowerShell’s Get-Help cmdlet to find out how to use your function. Below is an example:

Showing built-in help for Get-FiveProcesses PowerShell function
Showing built-in help for the Get-FiveProcesses function (Image credit: Petri/Bill Kindle)

Using a function in a PowerShell script

You can use a function as a session cmdlet or as part of a broader PowerShell script. In the previous section, you learned about a technique called dot-sourcing. You can also use functions within a script. Here’s an example:

# Simple script with a function built-in

# Declare the function
function Get-FiveProcesses {

    <#
    .SYNOPSIS
        Gets the first five processes.
    .DESCRIPTION
        Get-FiveProcesses performs some basic filtering of processes to show a user
        only the first five processes of a host system.
    .NOTES
        This function works on Windows and Linux running PowerShell 7+
    .LINK
        Be sure to check out more PowerShell articles on https://petri.com
    .EXAMPLE
        Get-FiveProcesses
        Get the first five processes.
    #>
   
    Get-Process | Select-Object -First 5

    Write-Host “Here are the first five processes!”
   
}

# now you call your function
Get-FiveProcesses

Save the example above as ProcessScript.ps1. Now, run the script in your PowerShell session:

Running the ProcessScript.ps1 script in our PowerShell session
Running the ProcessScript.ps1 script in our PowerShell session (Image credit: Petri/Bill Kindle)

Passing parameters to a PowerShell function

PowerShell advanced functions can accept parameters if you define them using a parameter block within your function code. Below is a basic parameter block example that contains a Parameter() block to designate the type and name of the parameter:

function Write-MyFunctionMessage {
    [CmdletBinding()]
        param (
            # This parameter allows you to type a message string
            [Parameter()]
            [string]
            $MyName
        )
    Write-Host “Hello $MyName! How are you today?”
}

The Write-MyFunctionMessage function passes the string value typed when using the parameter, as shown below:

The Write-MyFunctionMessage PowerShell function in action
The Write-MyFunctionMessage function in action (Image credit: Petri/Bill Kindle)

Parameters can have attributes that change how a function behaves. Let’s say you have a parameter that is needed in order for a command to complete successfully. By default, all parameters are optional. Using the attribute Mandatory in the Parameter () block forces you to enter the parameter, as shown below:

function Write-MyFunctionMessage {
    [CmdletBinding()]
        param (
            # This parameter allows you to type a message string
            [Parameter(Mandatory)]
            [string]
            $MyName
        )
    Write-Host “Hello $MyName! How are you today?”
}

To test the behavior change, try to run the function again and this time, don’t use the -MyName parameter. Since the parameter is mandatory, PowerShell will prompt you for the value. If you were to leave the value blank again, you will get an error:

Write-MyFunctionMessage -MyName parameter error in action
Not using the -MyName parameter results in an error (Image credit: Petri/Bill Kindle)

Parameters can also have default values assigned. Let’s say that you want the default parameter value to be “Reader”. Your code would then use the = sign after $MyName followed by a string value “Reader”:

function Write-MyFunctionMessage {
    [CmdletBinding()]
        param (
            # This parameter allows you to type a message string
            [Parameter()]
            [string]
            $MyName = “Reader” # Default Value
        )
    Write-Host “Hello $MyName! How are you today?”
}

Now, when you forget to enter a parameter value for -MyName, the function will no longer return an error:

The Write-MyFunctionMessage PowerShell function no longer returns an error
The Write-MyFunctionMessage function no longer returns an error (Image credit: Petri/Bill Kindle)

Returning values from a PowerShell function

In PowerShell, you can return values from a function using the return keyword or by using the Write-Output cmdlet. I’ll give you an example of each of them below.

Using the return keyword

function Add-Numbers {
    param(
        [int]$num1,
        [int]$num2
    )

    $result = $num1 + $num2
    return $result
}

In this example, the Add-Numbers function accepts two integer parameters and returns their sum using the return keyword.

Add-Numbers function in action
The Add-Numbers returns the sum of our two integer parameters (Image credit: Petri/Bill Kindle)

Using the Write-Output cmdlet

function Get-ServiceNames {
    $services = Get-Service
    Write-Output $services.Name
}

In this example, the Get-ServiceNames function retrieves all services on the system and returns their names using the Write-Output cmdlet.

Get-ServiceNames function in action
The Get-ServiceNames function retrieves all services on the system (Image credit: Petri/Bill Kindle)

Both approaches can return values from a function. The difference between them is that the return keyword exits the function and returns the value immediately, while the Write-Output cmdlet continues executing the function and sends the value to the output stream.

In most cases, using the Write-Output cmdlet is preferable because it allows you to pipe the function’s output to other cmdlets for further processing.

Accepting pipeline input

To make a PowerShell advanced function accept pipeline input, you need to define a parameter that accepts input from the pipeline. You can do this by adding the ValueFromPipeline parameter attribute to the parameter you want to accept input from the pipeline.

Here’s an example of how to create a function that accepts pipeline input:

function Get-ProcessOwner {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline=$true)]
        [string[]]$ProcessName
    )
 
    begin {
        Write-Verbose “Starting the function”
    }
 
    process {
        foreach ($p in $ProcessName) {
            $process = Get-Process -Name $p -ErrorAction SilentlyContinue
            if ($process) {
                $owner = $process | Select-Object -ExpandProperty UserName
                Write-Output “$p is owned by $owner”
            } else {
                Write-Warning “$p is not running”
            }
        }
    }
 
    end {
        Write-Verbose “Ending the function”
    }
}

In this example, the ProcessName parameter accepts an array of strings from the pipeline. The ValueFromPipeline parameter attribute allows the function to accept pipeline input.

You can then use this function with pipeline input like this:

Get-Process | Get-ProcessOwner

This would return the owner (if found) of each (running) process that is piped to the Get-ProcessOwner function.

Error handling

You can use PowerShell’s advanced function error handling to define how errors within your script or function are handled. By default, PowerShell will halt the execution of a script or function if an error occurs, but advanced function error handling lets you customize this behavior to handle errors in a more controlled way.

To implement advanced function error handling in PowerShell, you can use the try/catch construct. Here’s an example:

function My-Function {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)]
        [string]$Path
    )

    try {
        # Attempt to perform some operation that may generate an error
        Get-ChildItem $Path -Recurse -ErrorAction Stop
    }
    catch {
        # Handle the error in some way
        Write-Error “An error occurred: $($_.Exception.Message)”
    }
}

In this example, the try block attempts to run the Get-ChildItem cmdlet with the specified $Path parameter. If an error occurs, the catch block will execute, and the function will display the error message using the Write-Error cmdlet:

My-Function in action
If an error occurs, the catch block will execute (Image credit: Petri/Bill Kindle)

You can also use the finally block to define code that will execute regardless of whether an error occurred. Consider the next example:

function My-Function {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)]
        [string]$Path
    )

    try {
        # Attempt to perform some operation that may generate an error
        Get-ChildItem $Path -Recurse -ErrorAction Stop
    }
    catch {
        # Handle the error in some way
        Write-Error “An error occurred: $($_.Exception.Message)”
    }
    finally {
        # Clean up any resources that were used in the try block
        Write-Verbose “Cleaning up resources...”
    }
}

In this example, the finally block will always execute, even if an error occurred in the try block. This can be useful for tasks like closing open file handles or releasing other resources.

My-Function -Path .
The My-Function -Path command in action (Image credit: Petri/Bill Kindle)

And here’s the command again, but this time with the -Verbose parameter, which details more information about the operation done by the command.

Running the same command with the -Verbose parameter
Running the same command with the -Verbose parameter (Image credit: Petri/Bill Kindle)

Notice that the Write-Verbose cmdlet as set in your function is the last bit of code to run before the function exists.

Overall, advanced function error handling in PowerShell gives you greater control over how errors are handled in your scripts and functions and can help make your code more robust and resilient.

Where can you learn more about writing PowerShell functions?

Congratulations! You’ve made it to the end of this article on writing PowerShell functions. If you want to know more, you read our guide on how to create advanced functions in PowerShell. You can also find more information on PowerShell functions on Microsoft Learn, the PowerShell.org forum, and the PowerShell Discord server.

Related Article: