I recently read an interesting post by Don Jones about using the New-ADUser cmdlet to easily create user accounts from a CSV file. What is really cool about the New-ADUser cmdlet is that it supports about 50 parameters that map to AD attributes and accept pipeline input by property name. This means that you can have a CSV file with header names corresponding to the parameter names of the cmdlet and easily create accounts with a short command like this:
Import-CSV c:\users.csv | New-ADUser
Unfortunately, the New-Mailbox cmdlet in Exchange 20xx doesn't work this way. It does not provide any parameters that accept pipeline input, and in order to perform a simlar task our command would probably be about ten times longer.
So the question is, how can we emulate this type of functionality when provisioning Exchange mailboxes? The answer is PowerShell advanced functions. In this post we'll create an advanced function that uses the New-Mailbox cmdlet with parameters that accept pipeline input by property name. When we are done we'll be able to provision mailboxes from a CSV in exactly the same way one would using the New-ADUser cmdlet.
Getting Started
This is the basic structure we'll use for our advanced function. Notice that we are only defining one parameter here so we can get an idea of what is going on – we'll add more parameters to the final version of the function.
Function New-xMailbox { [CmdletBinding()] param( [Parameter()] [String] $Name ) Process { } }
Lets go over what we have here so far:
- First, we use the function keyword to define the name of the function, as we can see it is set to New-xMailbox.
- The [CmdletBinding()] attribute identifies a function as an advanced function and allows it to act in a way that is similar to a compiled cmdlet.
- The param block is where we will define all of our parameters. As you can see in the code, we are defining a single parameter using the [Parameter()] attribute. The parameter name is "Name" and is constrained to the String data type.
- The process block provides record-by-record processing for the function. This is where we'll handle objects coming across the pipeline and add the code to create the mailboxes.
Ok, now that we've got an idea of how this is laid out, let's talk a little more about parameters.
Parameters
The New-Mailbox cmdlet supports many parameters. For the sake of this example, we are only going to use a few key parameters, but you can extend the finished version of this function if you need to add more. The parameters that we'll add to our advanced function will be:
- Name
- DisplayName
- FirstName
- LastName
- Alias
- Password
- UserPrincipalName
- Database
All of these parameters will be mandatory, except the database parameter using Exchange 2010 Management Shell. The reason for this is that the database parameter is optional in Exchange 2010; if a value is not specified Exchange uses the Mailbox Resources Management cmdlet extention agent to choose the most appropriate database. So yes, this code will work with Exchange 2007 SP2 with PowerShell v2, but the Database parameter will be mandatory.
Parameter Properties
There are several properties that can be used with the [Parameter()] attribute. In our function, we'll be using the following parameter properties:
- Mandatory – Specifies that the parameter is either required or optional. This can be set to either $true or $false.
- ValueFromPipelineByPropertyName – Determines whether or not the parameter can accept a value by property name from an object coming across the pipeline. This can be set to either $true or $false.
Let's take a look at how the properties are set on a parameter attribute:
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [String] $Name
In this example, the $Name parameter is mandatory and the ValueFromPipelineByPropertyName property is set to true. Each parameter we will use in this function will accept pipeline input by property name – this is the key to getting the parameter binding to work.
Splatting
New in PowerShell v2 is the $PSBoundParameters automatic variable which is a hashtable containing all of the parameters bound to the function when it was invoked. We can use splatting to access the values that were bound to the parameters of our function, and we can also pass a hashtable to a cmdlet to fill in its parameter values.
For example, when we run the New-Mailbox cmdlet in our function, we can provide our bound parameter values to the New-Mailbox cmdlet using $PSBoundParameters. Creating mailbox becomes as easy as this:
Process { New-Mailbox @PSBoundParameters }
This reason this is really cool is that we don't have to specify each New-Mailbox parameter one by one. Also, if you decide to add more parameters to the function later, you don't have to worry about modifying the code that calls New-Mailbox – it happens automatically.
Dealing with Passwords
Ok, this is where we run into a roadblock. The New-Mailbox cmdlet requires that the password parameter value be of type System.Security.SecureString. We want to splat the New-Mailbox cmdlet with $PSBoundParameters, but this will not work if the Password parameter value is a simple string.
To solve this, we need to convert the Password string value to a secure string before splatting the New-Mailbox cmdlet. Here is the solution:
Process { foreach($p in $PSBoundParameters.GetEnumerator()) { if($p.key -eq "Password"){ $Password = ConvertTo-SecureString -AsPlainText $Password -Force } } [Void] $PSBoundParameters.Remove("Password") $PSBoundParameters.Add("Password",$Password) New-Mailbox @PSBoundParameters }
To put this simply, we are replacing the Password string value in $PSBoundParameters with the converted value of type System.Security.SecureString.
Without splatting our options would be limited. Doing it this way allows us to easily extend this function by adding as many parameters as needed without having to mess with the code that creates the mailboxes.
Setting up a CSV file
Setup your CSV file so that the header names match the parameter names of the function. You'll need the following header names in the CSV: Name, DisplayName, FirstName, LastName, Alias, Password, UserPrincipalName, and Database (optional).

Running the Code
You can download the code for the New-xMailbox function here. After you have downloaded it, dot source it into your Exchange Management Shell session. Running the function is as simple as this:
Import-CSV c:\mailboxes.csv | New-xMailbox

We've barely scratched the surface with advanced functions, but I hope you've found this example useful. To learn more about advanced functions, check out these topics from the PowerShell v2 online help:
- about_Functions_Advanced
- about_Functions_Advanced_Methods
- about_Functions_Advanced_Parameters
- about_Functions_CmdletBindingAttribute





{ 1 trackback }
{ 3 comments… read them below or add one }
Hello Mike,
nice idea.
You can simplify the code by just using:
$PSBoundParameters["Password"] = ConvertTo-SecureString -AsPlainText $Password -Force
Instead using the foreach and removing/readding the parameter.
Ciao,
Claudio
Yeah, good point thanks.
Thanks Mike!
One observation. In my (admittedly rushed) experience, if I wanted to leave out the $Database parameter I had to change:
ValueFromPipelineByPropertyName=$true
to false even though it was set to Mandatory=$false. Otherwise “” was being piped to new-mailbox and resulting in errors for me.