Menu
Business.com aims to help business owners make informed decisions to support and grow their companies. We research and recommend products and services suitable for various business types, investing thousands of hours each year in this process.
As a business, we need to generate revenue to sustain our content. We have financial relationships with some companies we cover, earning commissions when readers purchase from our partners or share information about their needs. These relationships do not dictate our advice and recommendations. Our editorial team independently evaluates and recommends products and services based on their research and expertise. Learn more about our process and partners here.
Instead of copying and pasting the same code repeatedly, create a PowerShell function.
When you first start writing scripts, modularity, reusability, and best practices may not be top of mind. As your scripts become more complex, however, creating reusable elements becomes essential. By defining Microsoft PowerShell functions within PowerShell modules, you can organize your scripts, avoid duplicating code, and streamline your workflow. This approach saves significant time and reduces errors caused by repetitive copying and pasting.
PowerShell functions act as small, reusable building blocks of code that streamline your workflow and make your scripts more efficient. We’ll explain more about PowerShell functions and share basic and advanced examples.
A function in PowerShell is a grouping of code with optional inputs and outputs. It allows you to perform tasks repeatedly without duplicating code. You can build functions to perform tasks such as:
There are two kinds of functions in PowerShell: basic and advanced. We’ll share examples of each below.
Basic PowerShell functions are straightforward and lack advanced features. They are declared using the function keyword, followed by a set of curly braces:
function Do-Something {}
The example above is technically a function and can be invoked by calling Do-Something, but it doesn’t perform any actions yet because no code is defined inside it.
Let’s add some simple code to make the function do something. For example, you can use the Write-Host command to output text to the PowerShell console:
function Do-Something {
Write-Host ‘I did something!’
}
You can then invoke this function using the following:
PS > Do-Something
I did something!
As you can see, the code runs inside the function when invoked.
By incorporating parameters, even basic PowerShell functions can become more versatile. Parameters allow functions to accept input, enabling them to adapt to various situations and handle different tasks dynamically.
The Do-Something function performs a single job, for example, but you can make it more useful by adding parameters. The parameters enable the function to process user input dynamically and customize output messages easily.
To do this, you can set up parameters in a parameter block, which allows your function to accept input values and modify its behavior. Here’s an example:
function Do-Something {
param( $String )
Write-Host “I did something — $String!”
}
To call this function with a parameter, use this:
PS > Do-Something -String ‘thing’
I did something — thing!
Notice how the String parameter is specified by adding a dash, followed by the parameter name and then the value after calling Do-Something. These are the basics of passing parameters to functions.
William Mabotja, an Azure-certified senior software developer at Atlas Finance, emphasized that parameters make functions more flexible and reusable. He shared the following example of using parameters to create a personalized greeting function:
function Say-HelloTo {
param (
[Parameter(Mandatory)]
[string]$Name
)
Write-Host “Hello, $Name!”
}
This function allows you to greet someone by name, demonstrating how parameters can customize a function’s behavior.
“By marking the parameter as mandatory using [Parameter(Mandatory)], PowerShell ensures you always provide a name when calling the function,” Mabotja said. “This makes the function flexible and prevents unintended behavior when there are missing inputs.”
Basic functions can handle straightforward tasks, but advanced functions are the better choice for more complex needs. Advanced functions build on the foundation of basic functions while offering additional built-in features that make them more versatile and powerful.
Below are some notable advanced PowerShell functions.
Parameter validation is a crucial feature of advanced PowerShell functions that ensures your scripts handle data reliably and predictably. Mabotja described it as a method for enforcing rules on input parameters to ensure PowerShell functions receive and process only valid data.
PowerShell provides several built-in validation attributes, such as:
[ValidateNotNullOrEmpty]: Ensures the parameter is not null or an empty string.
[ValidateSet()]: Restricts input to a predefined set of values.
[ValidateRange()]: Limits numeric inputs to a specific range.
Here’s an example:
function Set-User {
param (
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$Username,
[ValidateRange(18, 120)]
[int]$Age
)
# Function logic here
}
By using validation attributes, you can enforce rules on input parameters, preventing invalid data from being processed. This approach strengthens your functions by reducing the risk of errors and ensuring consistent behavior across use cases.
In addition to ensuring valid inputs, PowerShell’s advanced functions allow you to chain multiple functions to create workflows. “Chaining not only enhances readability, but also allows for more complex operations to be built from simpler, reusable components,” Mabotja said.
Function chaining allows you to pass the output of one function directly into another, ensuring a smooth data flow. This technique is particularly useful for workflows in which each function performs a specific task, making scripts more modular and easier to debug.
Here’s an example Mabotja shared:
function Get-Data {
# Simulate data retrieval
return “Sample Data”
}
function Process-Data {
param (
[string]$Data
)
Write-Host “Processing: $Data”
}
# Chaining the functions
Process-Data -Data (Get-Data)
In this script, Get-Data retrieves data and Process-Data processes it, forming a seamless workflow. “By chaining functions, you can create scripts that are flexible and scalable, so they adapt to new requirements without needing major changes,” Mabotja said.
Port scanning is another useful advanced function. It allows you to check if specific ports on a computer or server are open and accessible. IT professionals in network management and cybersecurity use port scanning to test connections or identify possible vulnerabilities.
Jeremy Manuel, cybersecurity solutions architect at Myriad360, shared an example of how advanced functions can handle tasks such as checking network connectivity or testing specific ports.
Start with a basic function:
function Test-Port {
Write-Host “Testing port…”
}
Next, add functionality to test a specific port of a computer:
function Test-Port {
param (
[string]$ComputerName = “localhost”,
[int]$Port = 3389
)
try {
$connection = Test-NetConnection -ComputerName $ComputerName -Port $Port
if ($connection.TcpTestSucceeded) {
Write-Host “Port $Port is open on $ComputerName.”
} else {
Write-Host “Port $Port is closed on $ComputerName.”
}
} catch {
Write-Host “An error occurred while testing the port.”
}
}
This function uses parameters to make it reusable and flexible. It tests any computer and port by specifying the parameters when calling the function. For example:
Test-Port -ComputerName “192.168.1.1” -Port 22
With this approach, you can create a simple tool to quickly check connections or spot potential network problems. By including parameters, you can dynamically test different ports and configurations without rewriting the function.
Advanced PowerShell functions provide built-in capabilities for managing and controlling how errors are flagged and displayed during script execution. This functionality leverages PowerShell’s streams, such as Error, Warning, and Verbose, which help display output correctly to users. Unlike basic functions, advanced functions inherently understand and utilize these streams.
Say you want a function to display an error message under specific conditions but also allow the user to suppress the error in certain scenarios. Basic functions would find this functionality cumbersome, but advanced functions can seamlessly handle it.
To create an advanced function, start with the [CmdletBinding()] keyword. This attribute transforms a basic function into an advanced function, unlocking built-in parameters such as ErrorAction, Verbose, and WarningVariable, enabling robust error management. Here’s an example:
function Do-Something {
[CmdletBinding()]
param ( $String )
Write-Error -Message ‘Danger, Will Robinson!’
}
When this function is called without additional parameters, the error is displayed in red text, indicating it came from the error stream:
PS> Do-Something
Do-Something : Danger, Will Robinson!
At line:1 char:1
+ Do-Something
+ ~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Do-Something
However, the ErrorAction parameter allows users to suppress or modify error-handling behavior dynamically. For example:
Do-Something -ErrorAction SilentlyContinue
Here, the SilentlyContinue option prevents the error message from displaying, letting the function continue without interruption.
Here are some tips for creating more effective, reusable, and maintainable functions to improve your PowerShell scripting.
Manuel recommended scripters use clear and consistent naming conventions. “Name your functions using a verb-noun structure to describe what the function does,” Manuel said. “Try and choose names that clearly indicate the function’s purpose.”
Here’s an example:
function Get-FirewallStatus {
# Function code here
}
In this example, the verb Get shows the action the function performs, and the noun FirewallStatus points to the data or object it’s working with. Together the name makes it clear that the function retrieves the current status of a firewall. By using this naming style, you make your functions more descriptive and keep them consistent with PowerShell’s standard conventions.
Manuel emphasized the importance of focusing functions on a single task to make them easier to test and reuse. “This makes testing, debugging, and reusing your functions much simpler,” Manuel said. “If a task grows complex, consider breaking it into smaller, more focused functions that can be chained together.”
Mabotja highlighted the importance of understanding variable scope. “By default, variables defined within a function have local scope, meaning they are only accessible within that function,” Mabotja said. “If you need variables to persist outside of a function, use $global: or $script: prefixes carefully.”
Mabotja also reminded programmers that environment variables such as $env:Path can store and retrieve configuration data, reducing reliance on hardcoded values and improving function flexibility.
Manuel recommended providing clear explanations and detailed comments to describe exactly what your function does. This practice makes your scripts more professional, user-friendly, and easier to maintain, especially when sharing them with others.
For example, if your function retrieves files from a directory:
function Get-DirectoryFiles {
<#
.SYNOPSIS
Retrieves a list of files from a specified directory.
.PARAMETER DirectoryPath
The path to the directory containing the files.
.EXAMPLE
Get-DirectoryFiles -DirectoryPath “C:\Temp”
#>
param (
[Parameter(Mandatory)]
[string]$DirectoryPath
)
Get-ChildItem -Path $DirectoryPath -File
}
Manuel recommended defining parameters clearly and ensuring that they handle specific inputs effectively.
For example:
function Get-OpenPorts {
param (
[Parameter(Mandatory)]
[string]$ComputerName
)
# Function code here
}
You should set default values for optional parameters to ensure the function runs even without specific inputs:
param (
[string]$ComputerName = ‘localhost’
)
You should also ensure the correct input using validation attributes:
param (
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$ComputerName,
[ValidateRange(1, 65535)]
[int]$Port = 80
)
By following these tips, your functions will be easier to maintain and share, and they’ll also adapt better to changing requirements.
Adam Bertram contributed to this article.