Why Search-Mailbox is Still Valuable When You Need to Remove Mailbox Items

The Woes of Core eDiscovery

Last May I wrote about the GUI makeover Microsoft delivered for the Core eDiscovery functionality in the Microsoft 365 compliance center. In a nutshell, I wasn’t impressed because the update was slower and buggier than its predecessor. A few months on, some of the bugs don’t seem as obvious but the performance is still not as it should be. The lack of performance shows up in PowerShell too, where cmdlets like New-ComplianceSearch and New-ComplianceSearchAction do not perform as they once did and return more errors than before. I suspect that something is very wrong on the back end.

This all brought me back to Search-Mailbox, a cmdlet that Microsoft would like to eliminate from Exchange Online. Microsoft deprecated the cmdlet on July 1, 2020, along with a bunch of other search and compliance features specific to Exchange. However, even if it’s unsupported, the cmdlet continues to work perfectly well, especially when used to remove mailbox items.

Good Architecture, Flawed Outcome

I’m sure the architecture chosen for Microsoft 365 compliance searches seemed like a good idea at the time. Searches need to work across multiple workloads rather than just mailboxes, which is why Microsoft chose to decouple searches from the actions you can take with the results of the searches (like preview, export, and purge). For some unknown reason, they’ve never managed to get around to expanding the ability of the purge action to deal with anything other than Exchange mailboxes.

Even if you only need to remove mailbox items, content search actions restrict purging 10 items per mailbox at one time. Apparently, this is because a purge action isn’t designed to clean up mailboxes. Instead, it is to surgically remove items from mailboxes, which is fine if you need to remove 10 or less from each mailbox. If not, you’ll be forced to write a PowerShell script to loop until all matching items are exhausted, like we describe in this article. And when you throw in some errors (transient or otherwise) because the backend has problems of its own, content search actions suddenly look pretty undesirable.

Search-Mailbox Basics

Search-Mailbox is simpler. It searches user mailboxes to:

  • Estimate how many items a search will find, including searching the Recoverable Items folders and archive mailboxes.
  • Copy items from one mailbox to another.
  • Remove items.

Search-Mailbox can process up to 10,000 items per mailbox (when copying messages), which is enough to deal with most situations. The cmdlet will complain but can go higher when simply deleting messages. I haven’t found its upper limit.

There’s no GUI. Everything is done through PowerShell, and those who need to run Search-Mailbox must be hold the RBAC Mailbox Import Export role before they can remove mailbox items. To discover who holds the role, I use this code:

$Users = Get-ManagementRoleAssignment -Role "Mailbox Import Export" -GetEffectiveUsers | Select -ExpandProperty EffectiveUserName
$Users | Sort -Unique

Scripting a Demonstration of Search-Mailbox

To demonstrate how useful Search-Mailbox remains, I wrote a script (available on GitHub) which can either generate estimates or remove items based on a search query. The mode is controlled by a simple parameter which is True to remove items or False to estimate items.

.\PurgeMessagesWithSearchMailbox.PS1 -DeleteItems $True

The body of the script has some hardcoded parameters to generate the search query, which is formed in the Keyword Query Language. You could prompt the user for the different elements which compose the query, but it was easier to hard code the values for demonstration purposes.

For instance, to remove the digest emails sent by MyAnalytics (now rebranded as Viva Insights), the values are:

$Sender = "no-reply@microsoft.com"
$StartDate = "1-Jan-2019"
$EndDate = "13-Sep-2021"
$BodyText = "Your month in review"
$Subject = "MyAnalytics"

To create the search query, we do:

$SearchQuery = "From:" + $Sender + " Sent:" + $StartDate + ".." + $EndDate
If ([string]::IsNullOrWhiteSpace($BodyText) -eq $False) { $SearchQuery = $SearchQuery + ' Body: "*' + $BodyText + '*"' }
If ([string]::IsNullOrWhiteSpace($Subject) -eq $False) { $SearchQuery = $SearchQuery + ' Subject:"*' + $Subject + '*"' }

Using the hardcoded parameters, the code generates this search query, which is a good example of the kind of complex queries Search-Mailbox can use to find messages:

From:no-reply@microsoft.com Sent:1-Jan-2019..13-Sep-2021 Body: "*Your month in review*" Subject:"*MyAnalytics*"

After generating the search query, the code finds some mailboxes (you could limit the set to certain mailboxes) and loops to run Search-Mailbox against each mailbox. If an estimate is requested, the command is:

$SearchOutput = Search-Mailbox -Identity $M.UserPrincipalName -SearchQuery $SearchQuery -EstimateResultOnly -Force -WarningAction SilentlyContinue -SearchDumpster

While removal of items is done with:

$SearchOutput = Search-Mailbox -Identity $M.UserPrincipalName -SearchQuery $SearchQuery -DeleteContent -Force -WarningAction SilentlyContinue -SearchDumpster

The script runs very nicely (Figure 1) and is much faster than using a content search action to purge mailbox items. Its output is a CSV file containing details of the items found and removed (if that’s what happened).

Search-Mailbox processes a bunch of mailboxes
Figure 1: Search-Mailbox processes a bunch of mailboxes

Removal Means Retention Until Holds Lapse

Removal means that Exchange Online keeps the items in the Recoverable Items structure until the last hold expires. This could be a simple matter of a mailbox’s single item recovery period lapsing (14 days by default), or it could be an-place litigation or eDiscovery hold. In either case, the Managed Folder Assistant removes the items after the last hold expires. Until then, the items remain inaccessible to users but available for eDiscovery.

Remember that searches will find deleted items in Recoverable Items, which means that if you run Search-Mailbox multiple times it might seem that you’re deleting the same items. In fact, the first deletion run removes the items from user view. Subsequent runs process the items in Recoverable Items, but as they’re already where they should be, nothing much happens.

Old Stuff Can Be Good Too

I realize that Microsoft wants to eliminate what they consider to be old code that harks back to on-premises roots. They are right to do so, but only when a reasonable alternative exists to allow customers to have the same functionality through a new method. Although content search actions seem to do the same job, the sad fact is that they are not as effective as the Search-Mailbox cmdlet is in dealing with day-to-day removal of mailbox items in situations like when spam arrives in user mailboxes, or someone sends out a message they shouldn’t have. It saddens me that the new code is not as good as the old. Let’s hope Microsoft closes the performance and functionality gap soon.


Learn how to exploit the Office 365 data available to tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work. Search-Mailbox is covered in the companion volume (which we refreshed today).

3 Replies to “Why Search-Mailbox is Still Valuable When You Need to Remove Mailbox Items”

  1. I created a search and destroy script a while ago similar to what is posted here (although not nearly as elegant.) It moves it to a dedicated mailbox and subfolder (named after the case number.) We use it every time we get a malicious piece if email in. We first perform a trace to identify recipients of the bad email. The KQL for creating the queries was the trickiest part as it is very unforgiving. Great article!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.