Exchange scheduled message removal using search.

Recently i had a task to delete obsolete antispam email notification in our environment.

I made a bit of researching on the matter, and, alas, the tool MS suggested to use, ComplianceSearchAction -Purge, only supported deleting 10 messages per mailbox, though i had quite a lot of daily spam notification incoming on some of our accounts.

So, i decided i’ll use ComplianceSearch to purge mailboxes where 10 or less messages matched the criteria, and for others i use Search-Mailbox -DeleteContent commandlet.

So, below script will allow to do that on a regular basis.

To run this script seamlessly using Task Scheduler, i use the following syntax in Actions

https://pastebin.com/S86PMxc9

Command: powershell.exe

Arguments: -command “. ‘C:\Program Files\Microsoft\Exchange Server\V15\bin\RemoteExchange.ps1’; Connect-ExchangeServer -auto; . ‘”E:\Scripts\Remove-SpamNotificationMessages.ps1″‘”

https://pastebin.com/J7jVAgb0

$exceptions = "mail1@contoso.com","mail2@contoso.com"
$Sender = "sender@contoso.com" # Used in Search query
$Name = "ComplianceSearchNameHere" # Used in Search query
$date = (get-date).AddDays(-7).tostring("MM/dd/yyyy") # Format can vary, this works fine for me when running on Exchange Server locally
$msgTreshold = 10 # Hardcoded treshold for New-ComplianceSearchAction -Purge https://docs.microsoft.com/en-us/microsoft-365/compliance/search-for-and-delete-messages-in-your-organization?view=o365-worldwide
$logpath = "E:\Scripts\logs\"
$debug = $true # Enables Transcript to $($logpath)test.txt

filter timestamp {"$(Get-Date -Format G): $_"}
if ($debug) {Start-Transcript -path  "$($logpath)test.txt" -append}

"Starting Remove-SpamNotificationMessages" | timestamp | Out-File  -filepath "$($logpath)purge-log.txt" -Append -noClobber

# Start with ComplianceSearch to soft clean up to 10 messages per mailbox that match the cryteria.
Get-ComplianceSearch $Name
if (-not $?) {
    Write-Output "ComplianceSearch with name :$($Name) not found, creating new..." | timestamp
    "ComplianceSearch with name :$($Name) not found, creating new..." | timestamp | Out-File  -filepath "$($logpath)purge-log.txt" -Append -noClobber
    New-ComplianceSearch -Name $Name -ContentMatchQuery "Received<$date AND from:$Sender"
}
else {
    Write-Output "Modifying ComplianceSearch $($Name) settings..." | timestamp
    "Modifying ComplianceSearch $($Name) settings..." | timestamp | Out-File  -filepath "$($logpath)purge-log.txt" -Append -noClobber
    Set-ComplianceSearch -Identity $Name -ContentMatchQuery "Received<$date AND from:$Sender"
}

# Waiting for ComplianceSearch to complete
Start-ComplianceSearch -Identity $name
$state = "Running"
do {
    sleep -s 30
    $state = (Get-ComplianceSearch $Name).Status
    Write-Output "Waiting for ComplianceSearch $($Name) to finish" | timestamp
    "Waiting for ComplianceSearch $($Name) to finish" | timestamp | Out-File  -filepath "$($logpath)purge-log.txt" -Append -noClobber
} while ($state -ne "Completed")
Write-Output "ComplianceSearch $($Name) completed, processing results..." | timestamp
"ComplianceSearch $($Name) completed, processing results..." | timestamp | Out-File  -filepath "$($logpath)purge-log.txt" -Append -noClobber
$search = Get-ComplianceSearch $Name

# Processing ComplianceSearch results
$results = $search.SuccessResults;
$results | Out-File  -filepath "$($logpath)lastComplianceSearchResults.txt" -Force;
if (($search.Items -le 0) -or ([string]::IsNullOrWhiteSpace($results))) {
    "The compliance search " + $SearchName + " didn't return any useful results." | Out-File  -filepath "$($logpath)purge-log.txt" -Append -noClobber;
    break;
}
$mailboxes = @();
$lines = $results -split '[\r\n]+';
foreach ($line in $lines) {
    if ($line -match 'Location: (\S+),.+Item count: (\d+)' -and [int]$matches[2] -gt $msgTreshold) {
        $mailboxes += $matches[1];
    }
}
"Number of mailboxes that have search hits greater then $($msgTreshold): " + $mailboxes.Count | Out-File  -filepath "$($logpath)purge-log.txt" -Append -noClobber
New-ComplianceSearchAction -SearchName $Name -Purge -PurgeType SoftDelete -Confirm:$false

# Processing list of mailboxes that has matched item count greater than ComplianceSearchAction purge limit $msgTreshold
foreach ($mailbox in $mailboxes) {
    if (-not $exceptions.Contains($mailbox)) {
        Write-Output "Delete content process for mailbox $($mailbox) started..." | timestamp
        "Delete content process for mailbox $($mailbox) started..." | timestamp | Out-File  -filepath "$($logpath)purge-log.txt" -Append -noClobber
        $result = Search-Mailbox -Identity $mailbox -SearchQuery "Received<$date AND from:$Sender" -SearchDumpster -DeleteContent  -Confirm:$false -Force
        if (-not $?) {
            Write-Output "Delete content process for mailbox $($mailbox) FAILED..." | timestamp
            "Delete content process for mailbox $($mailbox) FAILED..." | timestamp | Out-File  -filepath "$($logpath)purge-log.txt" -Append -noClobber
        }
        else {
            Write-Output "Delete content process for mailbox $($mailbox) finished. Deleted $($result.ResultItemsCount) items" | timestamp
            "Delete content process for mailbox $($mailbox) finished. Deleted $($result.ResultItemsCount) items" | timestamp | Out-File  -filepath "$($logpath)purge-log.txt" -Append -noClobber
        }
    }
}
if ($debug) {Stop-Transcript}

Leave a comment