Office 365: Purge/Delete Messages from Compliance Search

Modified on Mon, 10 Aug at 3:47 PM

Import-Module ExchangeOnlineManagement

# Connect using Search-Only Session mode
Connect-IPPSSession -EnableSearchOnlySession

# --- 1. Dynamic Variables & Interactive Prompts ---
$Today      = Get-Date
$BaseName   = "Phishing-$($Today.ToString('yyyy-MM-dd'))"
$SearchName = $BaseName
$Counter    = 1

# Check if a search with today's date already exists and increment suffix if needed
while (Get-ComplianceSearch -Identity $SearchName -ErrorAction SilentlyContinue) {
    $SearchName = "$BaseName-$Counter"
    $Counter++
}

# Prompt for From address (Required)
$Sender = Read-Host "Enter the sender email address (From)"
while ([string]::IsNullOrWhiteSpace($Sender)) {
    Write-Warning "Sender address cannot be blank."
    $Sender = Read-Host "Enter the sender email address (From)"
}

# Prompt for Subject (Optional)
$Subject = Read-Host "Enter the subject text to match (leave blank to skip subject filter)"

# Date padding for UTC index shifts (Yesterday to Tomorrow)
$StartDate = $Today.AddDays(-1).ToString("MM/dd/yyyy")
$EndDate   = $Today.AddDays(1).ToString("MM/dd/yyyy")

# --- 2. Build KQL Query Dynamically ---
$KqlParts = @("(Received:$StartDate..$EndDate)", "(From:`"$Sender`")")

if (-not [string]::IsNullOrWhiteSpace($Subject)) {
    # Escape inner quotes for KQL (" -> "") and apply wildcard substring match
    $EscapedSubject = $Subject.Replace('"', '""')
    $KqlParts += "(Subject:`"*$EscapedSubject*`")"
}

$KqlQuery = $KqlParts -join " AND "

Write-Host "`nSearch Name : $SearchName" -ForegroundColor Cyan
Write-Host "KQL Query   : $KqlQuery`n" -ForegroundColor Gray

# --- 3. Create & Execute Search ---
$s = New-ComplianceSearch -Name $SearchName -ExchangeLocation All -ContentMatchQuery $KqlQuery

if ($null -ne $s) {
    Start-ComplianceSearch -Identity $SearchName

    while ((Get-ComplianceSearch -Identity $SearchName).Status -ne "Completed") {
        Start-Sleep -Seconds 5
        Write-Host "Searching mailboxes across tenant..." -ForegroundColor Yellow
    }
} else {
    Write-Error "Failed to initialize compliance search."
    return
}

# --- 4. Retrieve & Display Item Count ---
$SearchStats = Get-ComplianceSearch -Identity $SearchName

Write-Host "`n========================================" -ForegroundColor Green
Write-Host " SEARCH RESULTS COUNT" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host "Search Name : $SearchName"
Write-Host "Items Found : $($SearchStats.Items)"
Write-Host "Total Size  : $([math]::Round($SearchStats.Size / 1MB, 2)) MB"
Write-Host "========================================`n"

if ($SearchStats.Items -eq 0) {
    Write-Warning "0 matching items found. Purge process aborted."
    return
}

# --- 5. User Confirmation ---
$Confirmation = Read-Host "Do you want to proceed with SoftDelete for $($SearchStats.Items) item(s)? Type 'YES' to purge"

# --- 6. SoftDelete Purge Loop ---
if ($Confirmation -eq 'YES' -or $Confirmation -eq 'Y') {
    $RemainingItems = $SearchStats.Items
    $RunNumber = 1

    while ($RemainingItems -gt 0) {
        Write-Host "`nStarting Purge Action (Pass $RunNumber)..." -ForegroundColor Cyan
        
        $PurgeAction = New-ComplianceSearchAction -SearchName $SearchName -Purge -PurgeType SoftDelete
        
        while ((Get-ComplianceSearchAction -Identity $PurgeAction.Identity).Status -ne "Completed") {
            Start-Sleep -Seconds 5
            Write-Host "Purging in progress..." -ForegroundColor Yellow
        }

        # Re-index search stats to handle the 10-item-per-mailbox cap
        Start-ComplianceSearch -Identity $SearchName
        while ((Get-ComplianceSearch -Identity $SearchName).Status -ne "Completed") {
            Start-Sleep -Seconds 5
        }
        
        $RemainingItems = (Get-ComplianceSearch -Identity $SearchName).Items
        Write-Host "Pass $RunNumber complete. Remaining items: $RemainingItems" -ForegroundColor Green
        $RunNumber++
    }

    Write-Host "`nAll matching items successfully soft-deleted!" -ForegroundColor Green
} else {
    Write-Host "Purge action canceled by user." -ForegroundColor Yellow
}

Was this article helpful?

That’s Great!

Thank you for your feedback

Sorry! We couldn't be helpful

Thank you for your feedback

Let us know how can we improve this article!

Select at least one of the reasons
CAPTCHA verification is required.

Feedback sent

We appreciate your effort and will try to fix the article