Understand and script the Get-MailboxFolderStatistics cmdlet

This blog post is going to give you some insight on how you can script the Get-MailboxFolderStatistics cmdlet, but also how to understand it’s output, as well as how is it important to plan a mailbox migration project.

Why are the mailbox folder statistics important to plan a migration project?

That is indeed the first thing you need to consider: Why do I need those statistics? To better answer that let me start by showing you an output of the command.

1

What you see above is the output of a mailbox, where I filtered the Folder Path, the number of items in the folder and sub folders, as well as the size of each folder. So let me bullet point why is this important for a migration project planning:

  • It will give you item counts per user and per item type (Mail, calendar, contacts). Those numbers are important, specially if extremely large, to estimate migration timelines when using the Bittitan MigrationWiz mailbox migration.
  • It will show you the size of every folder as well as the entire mailbox size. This is important to identify which folders are larger on a very big mailbox. I’ve seen examples of some of those folder being considered irrelevant for a migration (i.e deleted items, sent items) and with a flexible tool like the Bittitan MigrationWiz you can filter them out.
  • It will show you not only the folders visible to the end user, but also the recoverable deleted items folder. This might be very important in scenarios where you have in place hold active. It will give you the exact estimate of how many items are under in place hold on the recoverable items, that you will eventually need to move to the same folder on the destination mailbox.

So in summary, and giving a quick example, if you’re moving 1000 mailbox from Exchange Online tenantA to tenantB, you should use the Bittitan MigrationWiz tool, that will be not only fast but also flexible on what you can include and exclude in the migration, leveraging the Exchange Web Services API. To do that the insight of what’s in the mailbox is fundamental to plan the entire migration. You can plan the timelines and how long it will take, what type of licensing you will need at the destination tenant and many other important variables for the project.

How can I filter the output per item type?

When running the cmdlet you need to use the “-FolderScope” parameter to filter the output per item type. That parameter allows you to enter many different folder scope types, the most common being:

  • All
  • Calendar
  • Contacts
  • RSSSubscriptions
  • RecoverableItems

See the entire list in the official Technet article of the Get-MailboxFolderStatistics cmdlet.

Some cmdlet examples

See below some examples on how to obtain specific data:

Get total number of items in the mailbox (does not include recoverable deleted items):

Get-MailboxFolderStatistics user@domain.com | Where-Object {$_.foldertype -eq ‘root’} |ft folderpath, itemsinfolderandsubfolders, folderandsubfoldersize

2

Get total number of contacts in the mailbox:

Get-MailboxFolderStatistics user@domain.com -FolderScope Contacts |ft folderpath, items*, folderandsubfoldersize

3

Note: the above will work for calendar items if you change the folder scope from “Contacts” to “Calendar”

Get total number of items in the Recoverable deleted items folders:

Get-MailboxFolderStatistics user@domain.com| where {$_.FolderType -eq ‘RecoverableItemsRoot’}|ft folderpath, itemsinfolderandsubfolders, folderandsubfoldersize

4

And finally… the script that exports all of this to a csv:

Now that we discussed how important it is to get the statistics, and I gave you same examples on how to run some one line commands to get some insight on the mailbox, I will share with you a script that was done by me and my colleague and friend Alberto Nunes.

Below are the details of what the script does:

  • The script will run the statistics against a list of users that needs to be specified on a file called users.csv (see csv format below). The file needs to be on the same folder as the script.
  • The script runs Exchange PowerShell commands, so make sure you’re connected to the Exchange PowerShell (online or on premises)
  • The script will export the results to a file statistics.csv
  • The script will ask you for a source or destination parameter. The statistics for the recoverable deleted items folders will only be included if you select Destination.
  • This script is provided as is and there’s no guarantee that it will work in your environment.

The Users.csv file:

5

Note: Make sure you name the column A “EmailAddress”

The Script:

Copy the text below (in red) to a notepad. Save it as .ps1 and run it from a PowerShell session connected to Exchange.

#Start Script
Param
(
[Parameter(Position=0,Mandatory = $true)][ValidateSet(‘Source’,’Destination’,’MigrationWiz’)][String]$Location,
[Parameter(Mandatory = $false)][String]$Folder = “.”,
[Parameter(Mandatory = $false)][String]$UsersCSV = “Users.csv”,
[Parameter(Mandatory = $false)][String]$OutputCSV = “Statistics.csv”
)
Get-Date
$ErrorActionPreference = “SilentlyContinue”
$SourceFile = $Folder + “\” + $UsersCSV
$OutputFile = $Folder + “\” + $OutputCSV
$mailboxes = Import-Csv -Path “$SourceFile”
$CSV = @()
Foreach ($mailbox in $mailboxes)
{
Write-Host “Working on $($mailbox.EmailAddress)”

$AllStats = Get-MailboxFolderStatistics $($mailbox.EmailAddress)| Where-Object {$_.foldertype -eq ‘root’}

$ContactStats = Get-MailboxFolderStatistics $($mailbox.EmailAddress) -FolderScope Contacts
$TotalContactsItems = ($ContactStats | select -expand ItemsInFolder |Measure-Object -Sum).Sum

$CalendarStats = Get-MailboxFolderStatistics $($mailbox.EmailAddress) -FolderScope Calendar
$TotalCalendarItems = ($CalendarStats | select -expand ItemsInFolder |Measure-Object -Sum).Sum

$RSSFeeds = Get-MailboxFolderStatistics $($mailbox.EmailAddress) -FolderScope RssSubscriptions -ErrorAction SilentlyContinue
If ($RSSFeeds)
{
$TotalRSSFeeds = ($RSSFeeds | select -expand ItemsInFolder | Measure-Object -Sum).Sum
}
Else
{
$TotalRSSFeeds = 0
}

# In the source, we don’t need to know the Size of the RecovableItems
If ($Location -eq “Destination”)
{
$recoverableItems = Get-MailboxFolderStatistics $($mailbox.EmailAddress)| where {$_.FolderType -eq ‘RecoverableItemsRoot’}
[double]$recoverableSize = $recoverableItems.FolderAndSubfolderSize.TrimEnd(” bytes)”).Split(“(“)[1].replace(‘,’,”) |%{“{0:N2}” -f ($_ /1mb)}
}

$TotalMailItems = $AllStats.ItemsInFolderAndSubfolders – $TotalContactsItems – $TotalCalendarItems – $TotalRSSFeeds

$ISSize = $AllStats.FolderAndSubfolderSize.tostring()
[double]$ISSize =$ISSize.TrimEnd(” bytes)”).Split(“(“)[1].replace(‘,’,”) |%{“{0:N2}” -f ($_ /1mb)}

$Output = New-Object PSObject
$Output | Add-Member -MemberType NoteProperty -Name “User” -Value $mailbox.EmailAddress
$Output | Add-Member -MemberType NoteProperty -Name “Location” -Value $Location
$Output | Add-Member -MemberType NoteProperty -Name “TotalItems” -Value $AllStats.ItemsInFolderAndSubfolders
$Output | Add-Member -MemberType NoteProperty -Name “MailboxSize MB” -Value $ISSize
$Output | Add-Member -MemberType NoteProperty -Name “TotalContactItems” -Value $TotalContactsItems
$Output | Add-Member -MemberType NoteProperty -Name “TotalCalendarItems” -Value $TotalCalendarItems
$Output | Add-Member -MemberType NoteProperty -Name “TotalMailItems” -Value $TotalMailItems
$Output | Add-Member -MemberType NoteProperty -Name “TotalRSSFeeds” -Value $TotalRSSFeeds
If ($Location -eq “Destination”)
{
$Output | Add-Member -MemberType NoteProperty -Name “RecovableSize in MB” -Value $recoverableSize
$Output | Add-Member -MemberType NoteProperty -Name “RecovableItems” -Value $recoverableItems.ItemsInFolderAndSubfolders
$Output | Add-Member -MemberType NoteProperty -Name “Total Mailbox Size including Recovable” -Value $($ISSize + $recoverableSize)
$Output | Add-Member -MemberType NoteProperty -Name “Total Mailbox Count including Recovable” -Value $($AllStats.ItemsInFolderAndSubfolders + $recoverableItems.ItemsInFolderAndSubfolders)
}
$CSV += $Output
Write-Host “Completed $($mailbox.EmailAddress)” -ForegroundColor Green
}

$CSV | export-CSV “$OutputCSV” -NoTypeInformation
#End Script

To run the script you need to specify the location parameter:

.\MailboxFolderStats.ps1 -Location Source

.\MailboxFolderStats.ps1 -Location Destination (includes recoverable deleted items statistics)

As always I hope the above is helpful. Thanks for reading.

Office 365: Script to bulk change the UserPrincipalName to match the Email Address

When you are preparing your local Active Directory, to be synced with Office 365, one of the things you should consider is to make the UserPrincipalName of each user you are syncing to match the user’s email address. Why? Because that is going to be his UserPrincipalName and his primary SMTP address on Office 365.

So there are different ways of achieving this, some more manual than others. The procedure I am going to outline today on this blog post is a two step procedure:

Step 1: Export all UserPrincipalNames and Email Addresses from the local AD to a CSV File.

Step 2: Use that CSV file to bulk change the UserPrincipalNames to match those Email Addresses.

Like I said there are different ways of doing this, and I will probably develop a more elaborated script that can do this in a single step. The reason I went for this two step process is because most of the times customers want to check the CSV generated on step 1, and remove all the users that they don’t want to change the UPN, because those users will not be synced to Office 365.

Before we detail the steps above, make sure that you’ve added additional UPN domain suffixes for all the primary SMTP domains that you will have. See the article “How to add UPN suffixes to a forest” for more information.

Also have a detailed read on the article “Prepare to provision users through Directory Synchronization to Office 365”, to fully understand all the tasks you have to do to prepare your local Active Directory.

Making the UPN’s match the email addresses and have a domain that is validated on Office 365 is just one of the several tasks you have to do.

Now back to the two step process to change those UPN’s.

Step 1:

On step one all you have to do is open a PowerShell module on your local AD, and run the cmdlet below.

#Make sure you Import the Active Directory Module into your PowerShell session before you run the cmdlet

Import-Module ActiveDirectory

#Run the cmdlet to export all the users to a CSV. Change the CSV name and path as appropriate

Get-AdUser -Filter * -Properties UserPrincipalName, Name, EmailAddress | Select-Object UserPrincipalName, Name, EmailAddress | Export-CSV -Path C:\MyADUsers.csv -NoTypeInformation

After you run the cmdlet you should get a CSV like the one shown below:

ChangeUPN1

On the example above you can see that the UserPrincipalName does not match the user’s email address, and therefore needs to be changed.

Once you get the CSV check all users that you want to change and remove from that CSV the ones that you don’t.

Step 2:

Now that you have the CSV with all the users you want to change, all you have to do on step 2 is run the script below. The script will change all the UPN’s to match the email address, based on the CSV file you will use.

#Script to Change the UPN on the Active Directory#
#This script should run from an Active Directory Module for Windows PowerShell#
#Version 1.0 – 06/30/2015#
#Author: Antonio Vargas#

$UserCount = 0

Import-Csv -Path C:\MyADUsers.csv | ForEach-Object {

$UPN = $_.UserPrincipalName
Write-Host “Working on user:” $UPN
Get-ADUser -Filter {UserPrincipalName -Eq $UPN} | Set-AdUser -userprincipalname $_.EmailAddress
$usercount = $usercount +1
}
Write-Host “Number of users on your CSV: $UserCount”
Write-Host “UPN’s Changed”

Copy the entire content above into a notepad, and save it as a .ps1 file.

The script above is a very simple script that will read from the CSV. Before you run the script make sure you import the Active Directory module, as described on step 1, into your PowerShell session.

I highly recommend running the script first against a small group of up to 5 users, and then make sure that the changes were applied successfully. Also you need to take into account that you are changing the UserPrincipalName of the user on your local Active Directory, so make sure to test the access to all internal systems that rely on AD for authentication, before you replicate the change to all of your users.

As always, if you have any questions please let me know.

Office 365 Script: Enable users with a value on the mail attribute as mail users

When your Office 365 migration, is not from an environment where the mailboxes are on an Exchange On Premises (Hybrid/Staged/Cutover Migrations), and you’re are using Microsoft Dirsync to push your objects from the on premises active directory, into Office 365, then you need to prepare those objects on premises. That means that you should have users with the following attributes:

  1. A valid User Principal Name
  2. A Mail attribute
  3. Valid Proxy Addresses and ideally Exchange Attributes to be managed on premises

Regarding point 3, if like me, you’ve already done migrations from a 3rd party mail system to Office 365, with dirsync and just by adding a mail attribute to the users on premises, and pushing it into the cloud, you might ask, why should we have valid proxy addresses, and why should we have Exchange Attributes on those users? Well that is my opinion, and the way i work, and i will outline some reasons below:

  1. If the domain on 365 is federated, having the mail attribute will not be good enough, and you need to add proxy addresses, as described here. Enabling the users as mail users is a very quick and easy way of adding those proxy addresses, as opposed to having to edit those attributes on the Active Directory.
  2. Synced objects with Office 365 (Users, groups, etc) cannot have their attributes edited in the cloud. For example if you want to add an additional e-mail address to a mailbox on Office 365, and that mailbox is from a synced user, you will have to do it on premises and wait for dirsync to replicate those attributes to Office 365. There’s no better and easy way to edit those attributes than having those users enabled as Exchange objects on premises, and having an Exchange Server purely for management purposes.

The above does not mean in any way that your Exchange objects or your Exchange Server on premises will be part in any Hybrid Deployment. We are talking about migrations from a system which is not an Exchange on premises. The source can be Hosted Exchange, Lotus Notes, Google Mail, etc. And of course this scenario applies also when you have Dirsync.

Anyway I really highly recommend doing the described above, and to do that there’s sometimes a need to enable users as mail users. As a pre requisite I always ask my customers to prepare (or help them in the process, usually with scripting) the users with two important attributes: a valid User Principal Name and a valid mail attribute.

And once you have the two attributes above, and you want to enable those users as mail users, you can use the following script:

#This script enables users, on a specific OU, that have an e-mail address defined and are not exchange objects#
#This script is limited to the “TestAV” OU #
#Date:30/12/2014#
#Author: Antonio Vargas#

$users = Get-ADUser -SearchBase “OU=TestAV,DC=domain,DC=com” -Filter * -properties msExchRecipientTypeDetails, EmailAddress |where-object {$_.msExchRecipientTypeDetails -eq $null -AND $_.EmailAddress -ne $null}

foreach ($user in $users){
Enable-mailuser $User.UserPrincipalName -ExternalEmailAddress $User.EmailAddress
write-host $User.EmailAddress “<- This user was enabled as a Mail User.”
}

Now a quick example from my lab. I have 3 users on the OU being used by the script:

1

See below the output of the first part of the script, where it filters all the relevant users. User2 is the only one that does not have a mail attribute, and therefore was excluded from the output, which means that it will not be enabled as mail user by the script:

2

And when you run the script:

3

 

 

 

Finally you can see that both users are now enabled as Exchange Mail Users, and all your attributes, like the proxy addresses, should be in place. You can now manage those objects via your on premises Exchange Management Server.

This script should be executed on the Exchange Management Shell.

Again i very often bump into situations where my option is to execute a script like this one, and enable everyone as Exchange objects on premises, and if you are reading this post then probably that is also what you want to achieve.

Hope the above helped and as always any questions let me know.