Creating PowerShell functions
Functions are used to combine a series of commands into a reusable block of code that can be called using a single command. Functions can make a configuration change or return one or more objects that can either be displayed in the console or exported to an external file. You can assign the output of functions to a variable, or pipe a function to another cmdlet. In this recipe, you'll learn how to create a PowerShell function.
How to do it...
To create a function, you need to use the function
keyword, followed by the name of the function, and then the function code enclosed within curly braces {}
. For example, this very basic function displays three properties of a mailbox in list format:
function Get-MailboxList { param($name) Get-Mailbox $name | fl Name,Alias,ServerName }
When running the function, you must supply the identity of the mailbox as a parameter. The mailbox Name
, Alias
, and ServerName
are displayed in a list.
How it works...
PowerShell functions give us the ability to run a sequence of commands that can be called using a single function name. We can add input parameters to our own functions and also process pipeline input. This gives us the ability to write our own reusable functions that can behave just like a cmdlet.
There are a few ways you can add functions into your shell session. First, you can save your functions inside a .ps1
script. To make them available in the shell, your script just needs to be "dotted", or dot sourced. You do this by typing a period, a space, and then the path to the file. There has to be a space between the dot and the file name, otherwise it won't work. See the recipe Creating and Running Scripts for an example.
Another convenient method for adding functions to your shell session is to use a profile. PowerShell profiles are actually just a .ps1
script that gets executed when you start the shell. If you don't have a profile set up, check out the recipe titled Setting up a Profile.
If you're working interactively, you may find it convenient to simply copy and paste the function code straight into the shell. Keep in mind that, if you do this, the function will only be available during the current session. If you close the shell and open a new instance, the function will no longer be available.
There's more…
The best way to provide input to a function is to declare formal parameters. We did this with the previous example, and the $name
parameter was added to the function using the param
keyword. We can add a list of parameters using this syntax by separating each parameter name with a comma.
We can access informal function parameters inside the body of a function using the automatic $args
variable, which is an array that contains an element for each unbound argument passed to a function. While this can be useful in some cases, formal parameters provide much more flexibility. Formal parameters with descriptive names are easier to understand; they can be initialized with default values and support several attributes such as the position ID, whether or not they accept pipeline input, and whether they are required or optional.
In other scripting or programming languages, it is sometimes required to use a keyword to return a value from a function, but we don't have to do this in PowerShell. Let's say we've called the Get-Mailbox
cmdlet inside the body of a function, without capturing the output in a variable. In this case, the return value for the function will be the data returned by the cmdlet. You can explicitly return an object using the Write-Output
cmdlet and, although it makes for good readability when viewing the code, it is not required.
PowerShell functions can be written to accept and process pipeline input using three stages of execution by utilizing Begin
, Process
, and End
blocks, each of which Is described next:
- Begin: The begin block runs only once, at the beginning of the function. Any customization or initialization can happen here.
- Process: The process block runs once for each object in the pipeline. Each object that comes through the pipeline can be accessed using the
$_
automatic variable. - End: The end block runs after all of the objects in the pipeline have been processed.
We can create a simple pipeline function using only the Process
block. The Begin
and End
blocks are optional. For example, the following function will return the name for each mailbox sent across the pipeline:
function Get-MailboxName { process { "Mailbox Name: $($_.Name)" } }
We can pipe the Get-Mailbox
command to this function and each mailbox name will be returned:
Taking it a step further
Let's take a look at a practical example that combines the Get-MailboxStatistics
and Set-Mailbox
cmdlets into a function used to automate a task and demonstrate the capabilities of PowerShell functions. The following function will set the ProhibitSendReceiveQuota
limit for a mailbox, given values for the mailbox name and desired quota size. The function will only modify a mailbox if the total mailbox size does not already exceed the value provided for the quota setting:
function Set-SendReceiveQuota { param( [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName = $true)] $name, [Parameter(Mandatory=$true)] $quota ) begin { $count = 0 Write-Output "Started: $(Get-Date -format T)" } process { $count += 1 $mailboxstatistics = Get-MailboxStatistics $name $total = $mailboxstatistics.TotalItemSize.Value.ToMB() if($total -lt $quota) { Set-Mailbox $name -ProhibitSendReceiveQuota $quota ` -UseDatabaseQuotaDefaults $false } } end { Write-Output "Ended: $(Get-Date -format T)" Write-Output "Mailboxes Processed: $count" } }
You can see in this example that we've added the [Parameter ()]
attribute in order to define characteristics for each parameter. In this case, both parameters are mandatory and the $name
parameter will accept its value from the pipeline by property name.
Note
Parameters can use a number of arguments and attributes. For a complete list, run Get-Help about_Functions_Advanced_Parameters
.
Like a cmdlet, this function can process pipeline input and it can also be run against one object at a time. Let's start off by running the function for a single mailbox:
The Begin
block runs only once, immediately at the beginning, and the start time is returned as soon as the function is called. Within the Process
block, the code is run once and we increment the $count
variable to keep track of how many objects have been processed. The End
block is run last, reporting the total number of items that have been processed. We can see from the output in the previous screenshot that the function processed one mailbox and the operation only took one second to complete.
Now let's run the function for a collection of mailboxes:
The syntax of the command is very different this time. We pipe all of the mailboxes starting with the letter t
to the Set-SendReceiveQuota
function. Notice that we've only specified the -quota
parameter. This is because the $name
parameter will automatically receive a value from each mailbox objects Name
property as it comes across the pipeline. Looking at the output again, you can see that the operation took one second to complete, and we modified three mailboxes in the process.
PowerShell functions are a very broad topic and could easily be the focus of an entire chapter. We've covered some key points about functions here, but to learn more, run Get-Help about_functions
and Get-Help about_functions_advanced
.
See also
- Understanding the pipeline
- Creating and running scripts
- Setting up a profile