Problem solve Get help with specific problems with your technologies, process and projects.

Locating unused Exchange Server mailboxes with PowerShell code

Unused Exchange mailboxes are not only a licensing issue, but also a security vulnerability. Pinpoint empty and inactive mailboxes with PowerShell.

Exchange Server is licensed by the number of mailboxes within an organization. Not only do unused Exchange mailboxes...

consume licenses that could be better allocated elsewhere, but unused or dormant Active Directory accounts also weaken an Exchange organization's security. There is no native option within Exchange that retrieves a list of unused mailboxes, but PowerShell is up to the task.

Retrieving Exchange mailbox size via PowerShell

To begin searching for unused Exchange mailboxes, use PowerShell to examine mailbox size. There are a number of different ways to accomplish this, but I prefer the following command:

 Get-Mailbox | Get-MailboxStatistics | where {$_.ObjectClass –eq “Mailbox”} | Sort-Object ItemCount –Descending | ft DisplayName, TotalItemSize, ItemCount

This command creates a report that lists the Exchange mailbox's display name, total mailbox size (in KB) and number of items in the mailbox. This report sorts the list by the item count in descending order. I did this because the total mailbox list often does not fit on the screen. Mailboxes with the fewest items are displayed at the end of the list.

Unfortunately, empty mailboxes don't always end up at the bottom. If a user has never used his mailbox, the unused Exchange mailbox is not listed. But that's not to say that the mailbox will not appear in the report. The command's output explains if a mailbox has never been used before. You can see this message and the command's full output in Figure 1.

PowerShell output if Exchange mailbox has never been used.

Figure 1. The PowerShell code's output displays if an Exchange mailbox has never been used.

In my example, the full output fits on the screen because I only have a few mailboxes. In most organizations, the output is too long to completely fit on the screen.

If this happens to you, dump the output to a text file using PowerShell's Out-File cmdlet. Append a pipe to the command above, then enter Out-File, followed by the path and filename of your choosing. The full command looks like this:

 Get-Mailbox | Get-MailboxStatistics | where {$_.ObjectClass –eq “Mailbox”} | Sort-Object ItemCount –Descending | ft DisplayName, TotalItemSize, ItemCount | Out-File C:\report.txt

You may receive warnings about unused mailboxes. These warnings will be easier to read because the full report is not listed; only the warning is listed.

Open the report in Notepad or enter the Type command, followed by the filename. In Figure 2, you can see the full command, the on-screen warning and the contents of the report file.

A text file is written for the Exchange mailbox report.

Figure 2. The Exchange mailbox report gets written to a text file.

Determine last login time to view inactive Exchange mailboxes

The method I just described displays Exchange mailboxes that are empty or have never been used. But what if you want to find mailboxes that contain data, but are no longer active? The key here is determining when the mailbox's owner last logged in.

Note: This technique works for user mailboxes, but not Exchange resource mailboxes.

Unfortunately, there isn't a single PowerShell cmdlet that retrieves each user's most recent login information; you must assemble a PowerShell script. Here is a simple block of code from that determines the most recent login for each user:

 $SearchAD = New-Object DirectoryServices.DirectorySearcher([adsi]"") $SearchAD.filter = "(objectclass=user)" $users = $SearchAD.findall() Foreach($user in $users) { if($"lastLogon") -ne 0) { $a = [datetime]::FromFileTime([int64]::Parse($"lastLogon"))) "$($`"name`")) $a" } }

View this script's output in Figure 3.

Get last login time for each Exchange mailbox.

Figure 3. The code's output displays last login time for each Exchange mailbox.

The block of code isn't perfect, but it effectively reports both user accounts and computer accounts. More importantly, user logon times are not replicated between domain controllers.

If you have multiple domain controllers, you need a more elaborate script capable of parsing each domain controller in the domain.


Brien Posey is an eight-time Microsoft MVP with two decades of IT experience. Before becoming a freelance technical writer, Brien worked as a chief information officer at a national chain of hospitals and health care facilities. He has also served as a network administrator for some of the nation's largest insurance companies and for the Department of Defense at Fort Knox.

Dig Deeper on Exchange Server setup and troubleshooting