Skip to the main content.

ScriptRunner Blog

Exchange Server 2019 Mailbox Health Checks

Table of Contents



Post Featured Image

Organizations are and have been migrating their Exchange Server on-premises mailboxes to Exchange Online for quite some time now. Sometimes, these migrations go smoothly, sometimes there are bumps, but many times the migrations can be made smoother for administrators the more they are aware of the data they are migrating. As an Administrator or consultant who is conducting a migration, making for the best user experience should also play into the conversation. An analysis of a user's mailbox properties should be taken up prior to that migration. What should we look at?

Some things we can review are large mailboxes and large item counts in single folders. These mailbox properties are important as they can indeed make a difference in performance of an end user's experience with Exchange and Exchange Online (EXO V2). Both can result in extended delays in downloading messages, responding to messages, and sending messages. Microsoft also has some recommendations and if we combine these with real world experience, we can provide a better experience for the user.

Some considerations for large mailbox performance:

  • Less than 100,000 items per folder
  • Less than 500 folders per mailbox
  • Faster hardware – SSD Drives (don’t defrag), more RAM
  • Use a newer Outlook client – adjustable slider for Cached Mode clients
  • Archive Mailbox – reduces email in active mailbox
  • Delete emails

As we can see, there are recommendations that relate to the items in a folder as well as the size of the mailbox. Let's dive into some PowerShell on extracting data on an organization's mailbox properties and how to handle the resulting data.


Large Item Counts

Over time, mailboxes tend to get cluttered and large item counts in folders are the result. While there is a recent trend of cleaning out your Inbox and keeping a zero count of email, this trend is not universal or possible for many of an organization's users. We find that there are some users who never delete email and try to retain all emails for future recall and use, and this tends to lead to large item counts which also can lead to poor performance. We can use PowerShell to identify these users and their large item count folders and work with them to reduce those items to enhance their mailbox performance in Outlook. How do we do this? Let's dive into the code next.


First, we need to set some base variables that we can reference later in the code. Note the use of commented lines for separation and identification of the code section's purpose:

# Variables
$Counter = 1
$AllMaxFolders = $Null
$AllMaxFolders = @()
$FiftyKPlusFolders = $Null
$FiftyKPlusFolders = @()

Retrieve a list of mailboxes:

# Mailboxes
$Mailboxes = Get-Mailbox -ResultSize Unlimited
$TotalCount = $Mailboxes.Count

** Using -ResultSize Unlimited is necessary in environments that have over 1,000 mailboxes. If we do not use that option, then the Get-Mailbox cmdlet will only retrieve the first 1,000 mailboxes.

With a list of mailboxes, we can now examine each folder in each mailbox for over 100,000 items.

Foreach ($Mailbox in $Mailboxes) {

Each loop will have a set of variables to use. Some of these reset values used in the loop to ensure data integrity and others are to extract values needed for the mailbox being processed:

# Variables
$Name = $Mailbox.Displayname
$PrimarySMTPAddress = [string]$Mailbox.PrimarySMTPAddress
$Items = $Null
$Stats = $Null
$MaxItems = 0
$MaxFolder = $Null
$Items = $True

Next, we need to grab the folder information for a mailbox:

 $FolderStats = Get-MailboxFolderStatistics -Identity $PrimarySMTPAddress -ErrorAction STOP | Select-Object Name,ItemsInFolder,FolderPath }

In the loop below, each folder is checked to see if there are over 50,000 items in a folder. Any folders found are stored in the $FiftyKPlusFolders variable.

Foreach ($FolderStat in $FolderStats) {

# Variables for this loop
$Number = $FolderStat.ItemsInFolder
$FolderPath = $FolderStat.FolderPath
$Result = $Null

If ($Number -gt 50000) {
$Result = "$Name,$Number,$FolderPath"
If ($Number -gt $MaxItems) {
$MaxItems = $Number
$MaxFolder = $FolderPath
} # Add folder with over 50K items to array
If ($Null -ne $Result) {
$FiftyKPlusFolders += $Result


File Output Sample

Output sample from an environment that has no large item folders:

Folder with over 50K items:

No folders were found to contain more than 50,000 items!

However, this is not usually the case as the script will usually find at least one mailbox with a lot of items in a mail folder. The results in this case would look something like this:

Folder with over 50K items:
Sam Anderson,71497,/Deleted Items
Eric Fornet,99917,/Deleted Items
Jennifer Dorsey,126569,/Inbox
Lory Kendall,88299,/Deleted Items
Anthony Cron,158919,/Inbox/Automated
Dave Smith,108682,/Inbox
Ronald Lee,109252,/Inbox/Monitoring

Once we have this report, we start with any users with over 100K items as this is the most impactful to end users. Then any folders that are listed, but under 100K should be reviewed next especially if the item counts are near 100k. An organization should use this report to reach out to their end users, via the Help Desk for example, to help the user reduce these high item counts.


Large Folder Counts

An unusual check is the number of folders that are present in a mailbox. While most (if not all) of an organization's users will not exceed this number, it is a good value to check as Microsoft states that over 500 folders could result in poor client performance. As such, a check of these is a good idea and similar to the previous code to check item counts, we will review each mailbox in a Foreach loop and drop results into a CSV file for later analysis. The key cmdlet here, also mimicking the previous section, is the Get-MailboxFolderStatistics cmdlet which we can use to pull the folder counts from.
In this scenario, we also rely on the Measure-Object cmdlet which will allow us to count the Folders. Why does this work? Well, the Get-MailboxFolderStatistics cmdlet will list all folders that are contained in a mailbox and the Measure-Object just simply counts those folders. Visually it would look something like this:

Get-MailboxFolderStatistics Damian | ft Name,FolderSize


Each of these lines is a folder in a mailbox in Exchange. Counting each of these lines will give us an accurate count of the folders present in a mailbox. On to PowerShell!


# Variables
$Destination = 'FolderReport.csv'
$Mailboxes = Get-ExOMailbox -ResultSize Unlimited

# Body
$Output = 'Mailbox,FolderCount' | Out-File -FilePath $Destination
Foreach ($Mailbox in $Mailboxes) {
$Folders = Get-MailboxFolderStatistics -Identity $Mailbox.PrimarySMTPAddress | Measure-Object | Select-Object -ExpandProperty Count
$MailboxName = $Mailbox.DisplayName
$Output = "$MailboxName,$Folders" | Out-File -FilePath $Destination -Append

As we can see above, we define an output file, as well as gather all mailboxes in an environment in our Variable section again. Then in the Body section, we review each mailbox, count the folders present and then export these results to a CSV file for analysis. The file would then look like this (which is easily importable into Excel):

Amy A,63
Amy C,233
Amy D,908
Amy K,373
Amy M,65
Amy R,837
Amy S,54
Amy T,171
Amy W,356

Notice that we have a wide range of folder counts from 18 to 908. To be clear, we need to concentrate on the ones that are over 500. We can then reduce the output, if we wish, by putting a query in there like this:

If ($Folders -gt 500) {
$MailboxName = $Mailbox.DisplayName
$Output = "$MailboxName,$Folders" | Out-File -FilePath $Destination -Append

Then only the mailboxes that contain over 500 folders would be exported to our results file.

Large Mailboxes (and Archives!)

Now that we've reviewed large item and folder counts, we can also review large overall mailboxes and their archives as part of our analysis of mailboxes in an environment. For this one, we will look at larger mailboxes where we may have issues with migration, specifically anything over 75GB in size. Using 75GB isn't necessarily arbitrary, but more of a guideline to use as a starting point for analyzing mailbox sizes and analyzing the potential for both performance issues as well as migration issues. Thus, when querying for mailboxes, we can report on any mailboxes that reach this size and could even potentially identify tiers of mailboxes, with one tier for 75-100 GB and another for any mailbox over 100GB. Next up, PowerShell code.


$Mailboxes = Get-Mailbox -ResultSize Unlimited
$OutputStat = @()
Foreach ($Mailbox in $Mailboxes) {
$UPN = $Mailbox.SamAccountName
$MailboxStatistics = Get-MailboxStatistics $UPN | Select-Object DisplayName,@{expression = { $_.TotalItemSize.Value.ToMB()} ; label="MailboxSize"}
$OutputStat += $MailboxStatistics
$OutputStat | Sort MailboxSize

This code will give us the basics to start with and all mailboxes and their relative size will be exported to the screen for us to review. To further enhance this code to identify our mailbox tiers, we can add some lines near the end to break out different mailbox sizes. As we discussed above, the $MailboxStatistics variable stores the size of the mailbox in MB and thus if we need to start at 75GB, we need to make sure that the $MailboxStatistics variable is above 75,000 in value.

# Variables
$Destination = 'LargeMailboxes.csv'
$Mailboxes = Get-Mailbox -ResultSize Unlimited
$Over75GB = @()
$Over100GB = @()

Foreach ($Mailbox in $Mailboxes) {
$UPN = $Mailbox.SamAccountName
$MailboxStatistics = Get-MailboxStatistics $UPN | Select-Object DisplayName,@{expression = { $_.TotalItemSize.Value.ToMB()} ; label="MailboxSize"}

#Tier 1 and 2 Identification
If ($MailboxStatistics.MailboxSize -gt 75000) {
If ($MailboxStatistics.MailboxSize -gt 100000) {
$Over100GB += $MailboxStatistics
} Else {
$Over75GB += $MailboxStatistics
# Output findings to Output File
$Output = "Mailboxes between 75 and 100 GB:" | Out-File -FilePath $Destination
$Over75GB | Out-File -FilePath $Destination -Append
$Output = "Mailboxes over 100 GB:" | Out-File -FilePath $Destination -Append
$Over100GB | Out-File -FilePath $Destination -Append


Sample Output File

Mailboxes between 75 and 100 GB:

DisplayName TotalItemSize
----------- -------------
Nick Smarth 82881.44
Sarah Johnson 93458.09

Mailboxes over 100 GB:

DisplayName TotalItemSize
----------- -------------
Damian Scoles 108520.78

From this report, we have a clear and concise idea of what users we need to have conversations with to reduce the size of their mailboxes. Archive mailboxes are essentially the same with the only change required is to add the '-Archive' switch to the Get-Mailbox Cmdlet. The rest of the code will run the same but be executed against any archive mailboxes an end user may have.



As always, PowerShell is a very practical and powerful tool that can be used to identify potential issues that need remediation in an Exchange Server environment. Identifying mailboxes with too many folder items, too many folders and huge mailboxes become a practical reality with PowerShell. These three code sections can also be combined into a single report for IT to use for troubleshooting and/or remediation of these common issues.




How can ScriptRunner
help you maintain Exchange resources?

With ScriptRunner, you can fully manage resources in M365 Exchange Online as well as in Exchange on-premises environments. Besides the execution of scripts, ScriptRunner also offers possibilities to query Exchange resources with ScriptRunner Queries and display them in input fields for the users. Together with the web-based no-code UI and delegation features, you can safely and easily delegate automated tasks to helpdesk and end users.

ScriptRunner supports you with 1,500 script templates directly through the GitHub ActionPacks. Within the ScriptRunner Portal, scripts can be downloaded and customized directly. Just choose “Import script”:

import script
Download the script natively through ScriptRunner’s GIT integration:GIT integration

There are several scripts for exchange, like Get-ExMailboxProperties.ps1 and Get-ExMailboxStatisctics.ps1.


Before you save the script, you can of course tweak your scripts with ScriptRunner’s integrated code editor.integrated code editor

Switch to the ScriptRunner Admin App to create a new action out of your newly created script. Choose your script and create a new action directly out of it.choose scripts create new action


On the first page of the wizard, you can define the name and the tags of the action:tags of the action

On the second page, you can choose your target, i.e. your M365 Account or your On-Premises Exchange.choose target ExoAdmin

You can also configure ScriptRunner to use PowerShell v7 or preconfigure parameters.

Expert Advice:

When you want to delegate the action to your helpdesk staff, just preconfigure the parameters and hide the input fields. This will simplify the steps later for your helpdesk users.assign parameter values and hide from form

ScriptRunner is the perfect solution for managing and writing scripts in a professional environment. Start your Trial now (click here)! And #KeepOnScripting



Related Links


Related posts

3 min read

ScriptRunner now available in the Microsoft Azure Marketplace

6 min read

Managing Microsoft Exchange with PowerShell

2 min read

VMUG Webcast: Mastering VMware Management with PowerCLI

About the author: