Exchange Server 2010 Windows PowerShell: Mailboxes and Reports


Microsoft Exchange 2010 PowerShell Cookbook

Microsoft Exchange 2010 PowerShell Cookbook

Manage and maintain your Microsoft Exchange 2010 environment with the Exchange Management Shell and Windows PowerShell 2.0 using this book and eBook

        Read more about this book      

(For more resources on this subject, see here.)

The reader will benefit from referring two previous articles: Troubleshooting Mailboxes and Managing Mailboxes.


The concept of the mailbox is the core feature of any Exchange solution, and it's likely that almost everything you do as Exchange administrator will revolve around this component. Change 2010 SP1 includes several new cmdlets that make life much easier for any Exchange administrator, allowing you to do just about anything you can think of when it comes to managing mailboxes through scripts and one-liners. This includes tasks such as moving, importing, exporting, removing, and reconnecting mailboxes, just to name a few.

Performing some basic steps

To work with the code samples in this article, follow these steps to launch the Exchange Management Shell:

  1. Log onto a workstation or server with the Exchange Management Tools installed.
  2. Open the Exchange Management Shell by clicking on Start | All Programs | Exchange Server 2010.
  3. Click on the Exchange Management Shell shortcut.

Reporting on the mailbox size

Using cmdlets from both the Exchange Management Shell and Windows PowerShell gives us the ability to generate detailed reports. In this recipe, we will use these cmdlets to report on all of the mailboxes within an organization and their total size.

How to do it...

  1. Use the following one-liner to generate a report of each mailbox in the organization and the total mailbox size:

    Get-MailboxDatabase | Get-MailboxStatistics |
    ?{!$_.DisconnectDate} |
    Select-Object DisplayName,TotalItemSize

  2. Pipe the command even further to export the report to a CSV file that can be opened and formatted in Excel:

    Get-MailboxDatabase | Get-MailboxStatistics |
    ?{!$_.DisconnectDate} |
    Select-Object DisplayName,TotalItemSize |
    Export-CSV c:\mbreport.csv -NoType

How it works...

In both commands, we're using the Get-MailboxDatabase cmdlet to pipe each database in the organization to the Get-MailboxStatistics cmdlet . Notice that in the next stage of the pipeline we are filtering on the DisconnectDate propert y. Inside the filter we are using the exclamation (!) character, which is a shortcut for the -not operator in PowerShell. So we are basically saying, give me all the mailboxes in the organization that are not in a disconnected state. This can be standard mailboxes as well as archive mailboxes. We then select the DisplayName and TotalItemSize properties that give us the name and total mailbox size of each mailbox.

There's more...

When using the first example to view the mailboxes and their total size, you will see the output in the shell is similar to the following screenshot:

Microsoft Exchange Server 2010 Windows PowerShell tutorial

Here you can see that we get the total size in megabytes as well as in bytes. If you find that this additional information is not useful, you can extend the previous one-liner using a calculated property:

Get-MailboxDatabase | Get-MailboxStatistics |
?{!$_.DisconnectDate} |
Select-Object DisplayName,
@{n="SizeMB";e={$_.TotalItemSize.value.ToMb()}} |
Sort-Object SizeMB -Desc

Running the preceding one-liner will provide output similar to the following:

Microsoft Exchange Server 2010 Windows PowerShell tutorial

Notice that we now have a custom property called SizeMB that reports only the mailbox size in megabytes. We have also sorted this property in descending order and the mailboxes are now listed from largest to smallest. You can continue to pipe this command down to the Export-CSV cmdlet to generate a report that can be viewed outside of the shell.

Working with move requests and performing mailbox moves

Even if you performed mailbox moves with PowerShell in Exchange 2007, it's important that you understand that the process is completely different in Exchange 2010 SP1. There is a new set of cmdlets available for performing and managing mailbox moves, and the previously-used Move-Mailbox cmdlet no longer exists. The architecture used by Exchange to perform mailbox moves uses a new concept known as move requests, which have been implemented in this latest version. In this recipe, you will learn how to manage move requests from the Exchange Management Shell.

How to do it...

To create a move request and move a mailbox to another database within the Exchange organization, use the New-MoveRequest cmdlet, as shown next:

New-MoveRequest -Identity testuser -TargetDatabase DB2

How it works...

Mailbox moves are performed asynchronously with this new method and, unlike using the Move-Mailbox cmdlet in Exchange 2007, the New-MoveRequest cmdlet d oes not perform the actual mailbox move. Mailbox moves are handled by Client Access Servers (CAS) that run the Mailbox Replication Service (MRS). This is a major improvement because mailbox data does not move through an administrative workstation when performing a move; instead, the CAS servers are responsible for transferring the data from one database to another. Not only does this make mailbox moves faster, but it also allows you to kick off one or more mailbox moves from any machine in the organization. You can later check on the status of those move requests from any other machine with PowerShell or the Exchange Management Tools installed.

When you create a new move request with the New-MoveRequest cmdlet, the command places a special message in the target mailbox database's system mailbox. The MRS scans the system mailboxes on a regular basis looking for queued mailbox move requests and, once they are found, the MRS will start the move process. Once the move has been completed, a record of the mailbox move is saved and can be viewed using the Get-MoveRequest cmdlet.

This recipe only covers local move requests that are performed within an Exchange organization. It is possible to use the New-MoveRequest cmdlet t o perform a mailbox move across Active Directory forest boundaries. For more details, see Managing Remote Move Requests on TechNet at the following URL:

If you will be automating mailbox moves using the Exchange Management Shell, it is likely that you will be doing so in bulk. The following example shows how you can move all of the mailboxes from one database to another:

Get-Mailbox -Database DB1 | New-MoveRequest -TargetDatabase DB2

In this example, we are retrieving all of the mailboxes in the DB1 database and creating a new move request for each one that will then be moved to the target database of DB2. The -TargetDatabase parameter is actually an optional parameter. If you have multiple mailbox databases in your organization, you can omit the -TargetDatabase parameter in the previous command, and the mailboxes will be moved evenly across the available mailbox databases, as long as those databases have not been suspended or excluded from provisioning and as long as the Mailbox Resources Management Agent is enabled, which is the default setting.

There's more...

In order to view detailed information about move requests, you can use the Get-MoveRequestStatistics cmdlet. This will return a great deal of useful information for a given move request such as the move status, percent complete, the total bytes transferred, and more. You can also use the -IncludeReport, switch parameter when running the cmdlet to provide a debug level details for mailbox moves. This can be very beneficial when troubleshooting an issue.

One of the greatest uses of this cmdlet is reporting on the current status of mailbox moves in progress, especially during large migrations. The following one-liner can be used to gather the statistics for the currently-running mailbox moves and can be run periodically throughout the migration to check the status:

Get-MoveRequest |
?{$_.Status -ne ‘Completed’} |
Get-MoveRequestStatistics |
select DisplayName,PercentComplete,BytesTransferred

The preceding command would produce an output for each mailbox similar to the following screenshot:

Microsoft Exchange Server 2010 Windows PowerShell tutorial

In this example, we're selecting just a few of the properties from the output of the command. Alternatively, it may be useful to export this information to a CSV file or to mail the results to an administrator mailbox. Either way, it gives you a method for monitoring the status of your mailbox moves interactively in the shell or through an automated script.

If you just want to do some basic interactive monitoring from the shell to determine when all moves are complete, you can use the following code:

while($true) {
Get-MoveRequest| ?{$_.Status -ne 'Completed'}
Start-Sleep 5

The output from this command will give you a view of all the incomplete move requests and will refresh every five seconds. This is done by using an endless while loop th at runs Get-MoveRequest, waits for five seconds, clears the screen, and starts over again. Once all moves are completed, just press Ctrl + C to break out of the loop.

Removing the move requests

You cannot perform a move request for a mailbox if there is an existing move request associated with that mailbox. This is true regardless of the move request status, whether it is complete, pending, cancelled, or failed. You can use the Remove-MoveRequest to delete an existing move request for a single mailbox, using the following syntax:

Remove-MoveRequest -Identity testuser -Confirm:$false

If you perform frequent moves you may find it necessary to regularly delete all existing move requests in the organization. To do this, use the following command:

Get-MoveRequest -ResultSize Unlimited |
Remove-MoveRequest -Confirm:$false

Keep in mind that stored move requests can provide detailed information that can be used for monitoring or generating reports for mailbox moves. Make sure you no longer need this information before removing these move requests from your organization.

Moving the archive mailboxes

Consider the following example. The testuser account has a mailbox in the DB1 database, and also a personal archive mailbox in the DB1 database. We can use the following command to move testuser to DB2:

New-MoveRequest testuser -TargetDatabase DB2

In this case, both the primary mailbox and the archive mailbox will be moved to DB2. We can customize this behaviour by using some additional parameters made available by the New-MoveRequest cmdlet. For example, if we wanted to only move this user's primary mailbox and leave the archive mailbox in its current location, we could use the following command:

New-MoveRequest testuser -TargetDatabase DB2 -PrimaryOnly

This command adds the -PrimaryOnly switch parameter, w hich will indicate to the New-MoveRequest cmdlet th at we do not want to move the archive mailbox but we do want to move the primary mailbox to the DB2 database. Use the following command to move only the archive mailbox:

New-MoveRequest testuser -ArchiveOnly -ArchiveTargetDatabase DB2

This time, we have added the -ArchiveOnly switch parameter so that only the archive mailbox will be moved. The -ArchiveTargetDatabase is also used to specify that we want to move the archive mailbox to the DB2 database.

Moving the mailboxes in batches

When performing migrations or moving multiple mailboxes in bulk, it can be useful to move them in batches. The New-MoveRequest cmdlet pr ovides a -BatchName parameter to group multiple mailbox moves into a single, logical collection. Let's say that we are migrating multiple mailboxes to several different databases and we want to easily track the mailbox moves based on a certain criteria:

$mailboxes = Get-Mailbox `
-RecipientTypeDetails UserMailbox `
-Database DB1 |
Get-MailboxStatistics |
?{$_.TotalItemSize -gt 2gb}
$mailboxes | %{
New-MoveRequest -Identity $_.DisplayName `
-BatchName 'Large Mailboxes' `
-TargetDatabase DB2

Here we are retrieving all mailboxes in the DB1 database that are larger than two gigabytes and storing the results in the $mailboxes variable. We then pipe the $mailboxes object to the ForEach-Object cmdlet (u sing the % alias) and loop through each item. As each mailbox in the collection is processed within the loop, we create a new move request for that mailbox, indicating that it should be included in the Large Mailboxes batch and moved to the DB2 database. At this point, we can easily track the moves in the batch using a simple one-liner:

Get-MoveRequest -BatchName 'Large Mailboxes'

The preceding command will return each move request included in the Large Mailboxes batch and will provide several details including the display name, move status, and target database.

Moving mailboxes with corrupt items

When migrating from a previous version of Exchange, or when migrating large mailboxes, it's not uncommon to run into problems with users that have corrupted items in their mailbox. You can use the -BadItemLimit parameter to specify the acceptable number of corrupt, or "bad", items to skip when performing a mailbox move. Keep in mind that if you set the -BadItemLimit parameter to a value higher than 50 then you need to also use the -AcceptLargeDataLoss switch parameter, as shown in the following example:

New-MoveRequest -Identity testuser `
-BadItemLimit 100 `
-AcceptLargeDataLoss `
-TargetDatabase DB2

When executing this command, a move request will be created for the testuser mailbox. Up to 100 corrupt items in the source mailbox will be allowed in order to perform a successful move to the new database. You will see a warning in the shell when using these parameters and any corrupt items found in the source mailbox will be skipped when the mailbox is moved.

        Read more about this book      

(For more resources on this subject, see here.)

Generating mailbox folder reports

The ability to generate reports based on individual mailbox folders can be extremely useful at times. The Exchange Management Shell provides a versatile cmdlet called Get-MailboxFolderStatistics that allows you to obtain detailed information about specific mailbox folders such as the Inbox, Sent Items, Deleted Items, and more. Various pieces of information about these folders can be obtained including the total number of items, the size of the folder, and the folder ID. In this recipe, you will learn how to generate reports using the Get-MailboxFolderStatistics cmdlet.

How to do it...

To generate a report for the folders within a user's mailbox, use the following command:

Get-MailboxFolderStatistics -Identity testuser -FolderScope All |
select Name,ItemsInFolder,FolderSize |
Export-CSV C:\MB_Report.csv -NoType

How it works...

In this example, we are executing a one-liner that generates a report of the mailbox folder statistics for the testuser mailbox. We specify All for the -FolderScope so that the cmdlet retrieves information about each folder in the mailbox. From the output, we select a few properties and then export the results to a CSV file.

The information returned by this command can be useful when troubleshooting mailbox quota issues. For example, say you have a user that has reached their mailbox quota limit. We can view the output from the previous command instead of exporting it to a CSV file, as shown in the following screenshot:

Microsoft Exchange Server 2010 Windows PowerShell tutorial

In this example, it's clear that the Inbox folder contains the largest amount of data. You can use this information to inform the user of the current status and recommend they delete some of the data from the Inbox folder.

There's more...

You can run the Get-MailboxFolderStatistics cmdlet against specific folders by specifying the appropriate value for the -FolderScope parameter. There are nearly twenty well-known mailbox folders that can be assigned to the -FolderScope parameter. To view a complete list, run the following command:

Get-Help Get-MailboxFolderStatistics -Parameter FolderScope

Taking it a step further

We can use this cmdlet in a custom function to generate more sophisticated reports. For example, the following function can be used to generate a report detailing the usage of the Deleted Items folder for one or more mailboxes:

function Get-MailboxDeletedItemStats {

$folder = Get-MailboxFolderStatistics $id `
-FolderScope DeletedItems

$deletedFolder = $folder.FolderSize.ToMb()
$mb = (Get-MailboxStatistics $id).TotalItemSize.value.ToMb()
if($deletedFolder -gt 0 -and $mb -gt 0) {
$percentDeleted = "{0:P0}" -f ($deletedFolder / $mb)
else {
$percentDeleted = "{0:P0}" -f 0
New-Object PSObject -Property @{
Identity = $id
MailboxSizeMB = $mb
DeletedItems = $folder.ItemsInFolder
DeletedSizeMB = $deletedFolder
PercentDeleted = $percentDeleted

This function combines both the Get-MailboxFolderStatistics and Get-MailboxStatistics cmdlets to determine the total size of the Deleted Items folder and the total size of the mailbox. Several pieces of information are returned, including the percentage of total mailbox size that is comprised of the deleted items. You can add this function to your profile and it will be automatically added to your session every time you start the shell. You can then run the function just like a cmdlet against a single mailbox, as shown in the following screenshot:

Microsoft Exchange Server 2010 Windows PowerShell tutorial

This function can also be run against every mailbox, just like any other cmdlet:

foreach($mailbox in (Get-Mailbox -ResultSize Unlimited)) {
Get-MailboxDeletedItemStats $mailbox

The output from this function can be viewed interactively in the shell or exported to a CSV or text file for later review.

Reporting on mailbox creation time

If you work in an environment that frequently hires new employees, you may have a process in place to provision your mailboxes in bulk. You may have already used this book to help you do that. Now you might like to be able to generate reports or retrieve a list of mailboxes that were created during a specific time frame or after a specific date. In this recipe, you will learn a couple of ways to do that using the Exchange Management Shell.

How to do it...

Let's start off with a simple example. To generate a report of mailboxes created in the last week, execute the following command:

Get-Mailbox -ResultSize Unlimited |
?{$_.WhenMailboxCreated -ge (Get-Date).AddDays(-7)} |
Select DisplayName, WhenMailboxCreated, Database |
Export-CSV C:\mb_report.CSV -NoType

How it works...

This one-liner searches through every mailbox in the organization checking the WhenMailboxCreated property. If the date is within the last seven days, we select a few useful properties for each mailbox and export the list to a CSV file.

Mailboxes also have a property called WhenCreated, so why don't we just check this property instead? This is because the WhenCreated property is an Active Directory attribute that stores the creation date for the user account and not the mailbox. It is quite possible that your user accounts are created in Active Directory long before they are mailbox-enabled, so using this property may not be reliable in your environment.

There's more...

The WhenMailboxCreated property returns a DateTime object that can be compared to other DateTime objects. In the previous example, we used the following filter with the Where-Object cmdlet:

$_.WhenMailboxCreated -ge (Get-Date).AddDays(-7)

When running the Get-Date cmdlet without any parameters, a DateTime object for the current date and time is returned. Every DateTime object provides an AddDays method that can be used to create a new DateTime object. So, to get the DateTime from seven days ago, we simply provide a negative value when calling this method and the result is the date and time from a week ago. We compare the WhenMailboxCreated date to this value, and, if it is greater than or equal to the date seven days ago, the command retrieves the mailbox.

You can use other DateTime properties when performing a comparison. For example, let's say last month was March, the third month of the year. We can use the following command to retrieve all the mailboxes created in March:

Get-Mailbox | ?{$_.WhenMailboxCreated.Month-eq 3}

This gives us the ability to generate very customizable reports, such as reporting only on mailboxes that were created on Mondays in October:

Get-Mailbox | ?{
($_.WhenMailboxCreated.DayOfWeek -eq "Monday") -and `
($_.WhenMailboxCreated.Month -eq 10)

As you can see, there is a lot of flexibility here that you can use to customize the output to meet your needs. This is a good example of how we can extend the Exchange Management Shell by tapping into the capabilities of the .NET Framework.


Having covered Reporting on mailbox, in the next two articles we will cover Troubleshooting Mailboxes and Managing Mailboxes.

Further resources on this subject:

You've been reading an excerpt of:

Microsoft Exchange 2010 PowerShell Cookbook

Explore Title