Entra / Microsoft 365 · Exchange Online
Purge messages with search mailbox
A script to purge messages from Exchange Online using the famous Search-Mailbox cmdlet. The script can either delete items or report an estimate.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-ExchangeOnline -SkipLoadingCmdletHelp
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
param([string] $StartDate = "1-Jan-2019",[string] $EndDate = "13-Sep-2024")If ($DeleteItems -ne $True -and $DeleteItems -ne $False) { Write-Host "No mode specified - please rerun" ; break }$Status = Get-ConnectionInformation -ErrorAction SilentlyContinueIf (!($Status)) {Connect-ExchangeOnline -SkipLoadingCmdletHelp}Clear-HostSwitch ($DeleteItems) {$True {$Mode = "delete"$SearchMessage = "Searching mailbox to delete items" }$False {$Mode = "estimate"$SearchMessage = "Searching mailbox to estimate items" }} #EndSwitch# Some information to identify the messages we want to purge$BadSender = "no-reply@microsoft.com"# Date range for the search - make this as precise as possible$BodyText = "Your month in review"$Subject = "MyAnalytics"$SearchQuery = "From:" + $BadSender + " Sent:" + $StartDate + ".." + $EndDateIf ([string]::IsNullOrWhiteSpace($BodyText) -eq $False) { $SearchQuery = $SearchQuery + ' Body: "*' + $BodyText + '*"' }If ([string]::IsNullOrWhiteSpace($Subject) -eq $False) { $SearchQuery = $SearchQuery + ' Subject:"*' + $Subject + '*"' }Write-Host ("Finding user and shared mailboxes to run query {0} against in (1) mode" -f $SearchQuery, $Mode)[array]$Mbx = Get-ExoMailbox -RecipientTypeDetails UserMailbox, SharedMailbox -ResultSize UnlimitedClear-Host$CSVOutput = "C:\temp\ExoSearchRemovals.CSV"$Report = [System.Collections.Generic.List[Object]]::new();$Successes=0$Failures=0$TotalItems=0$ProgDelta = 100/($Mbx.Count); $CheckCount = 0; $MbxNumber = 0ForEach ($M in $Mbx) {$MbxNumber++$MbxStatus = $M.DisplayName + " ["+ $MbxNumber +"/" + $Mbx.Count + "]"Write-Progress -Activity $SearchMessage -Status $MbxStatus -PercentComplete $CheckCount$CheckCount += $ProgDeltaIf ($DeleteItems -eq $True) { #Go ahead and delete items$SearchOutput = Search-Mailbox -Identity $M.ExternalDirectoryObjectId -SearchQuery $SearchQuery -DeleteContent -Force -WarningAction SilentlyContinue -SearchDumpster}ElseIf ($DeleteItems -eq $False) { # Estimate Search$SearchOutput = Search-Mailbox -Identity $M.ExternalDirectoryObjectId -SearchQuery $SearchQuery -EstimateResultOnly -Force -WarningAction SilentlyContinue -SearchDumpster}Switch ($SearchOutput.Success) {"True" { # Success$Successes++$TotalItems = $TotalItems + $SearchOutput.ResultItemsCount$ReportLine = [PSCustomObject][Ordered]@{Name = $M.DisplayNameUPN = $M.UserPrincipalNameObjectId = $M.ExternalDirectoryObjectIdItems = $SearchOutput.ResultItemsCountItemSize = $SearchOutput.ResultItemsSize.Substring(0,$SearchOutput.ResultItemsSize.IndexOf("("))Status = "Success"Mode = $ModeDate = (Get-Date -format g)}$Report.Add($ReportLine) }"False" { # Whoops!$Failures++$ReportLine = [PSCustomObject][Ordered]@{Name = $M.DisplayNameUPN = $M.UserPrincipalNameObjectId = $M.ExternalDirectoryObjectIdItems = 0ItemSize = 0Status = "Failure"Mode = $ModeDate = (Get-Date -format g)}$Report.Add($ReportLine) }} # End Switch} # End ForEachSwitch ($DeleteItems) {$True { Write-Host ("{0} mailboxes processed and {1} items removed." -f $Mbx.Count, $TotalItems) }$False { Write-Host ("{0} mailboxes processed and {1} items found. " -f $Mbx.Count, $TotalItems) }}$Report | Where-Object {$_.Items -gt 0} | Out-GridViewExport-CSV -Notypeinformation $CSVOutput
Parameters
ParameterDefaultNotes
-StartDate1-Jan-2019Start of the reporting window.-EndDate13-Sep-2024End of the reporting window.Attribution
Author
Office365itpros