office365-mail-purger/purger.ps1
2023-08-26 15:08:29 +02:00

136 lines
4.5 KiB
PowerShell

#!/usr/bin/env pwsh
# Add any email addresses that should be purged to the $Addresses array
$Addresses = @()
$MaxDate = "2023-06-30"
# No modifications below this point.
function Write-Log {
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[string]$Message
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Write-Output "${timestamp}: $Message"
}
# Build query for ComplianceSearch
if (!$Addresses) {
Write-Output "Search query is empty - make sure that Addresses array is not empty - exiting"
exit 1
}
$From = "from:" + ($Addresses -join " OR from:")
$To = "to:" + ($Addresses -join " OR to:")
$Participants = "$From OR $To"
$Query = "($Participants) AND date<=$MaxDate AND (kind:email)"
# Ask user for admin credentials
$UserCredential = Get-Credential -Title "Office 365 Credentials" -Message "Enter your credentials for Office 365"
# Load PowerShell session for Microsoft Exchange
Import-Module ExchangeOnlineManagement
Connect-IPPSSession -Credential $UserCredential
Connect-ExchangeOnline -Credential $UserCredential
# Define folders that should be ignored by ComplianceSearch
$folders = "/Recoverable Items", "/Deletions", "/Purges", "/Versions"
Write-Output "Finding folders to exclude, this may take a while..."
$mailboxes = Get-Mailbox
$folderQueries = @()
foreach ($mailbox in $mailboxes) {
$emailAddress = $mailbox.PrimarySmtpAddress
$folderStatistics = Get-EXOMailboxFolderStatistics $emailAddress
foreach ($folderStatistic in $folderStatistics) {
$folderId = $folderStatistic.FolderId;
$folderPath = $folderStatistic.FolderPath;
if ($folders -contains $folderPath) {
# Based on / shamelessly stolen from:
# https://practical365.com/targeted-collection-content-search/
$encoding = [System.Text.Encoding]::GetEncoding("us-ascii")
$nibbler = $encoding.GetBytes("0123456789ABCDEF");
$folderIdBytes = [Convert]::FromBase64String($folderId);
$indexIdBytes = New-Object byte[] 48;
$indexIdIdx = 0;
$folderIdBytes | Select-Object -skip 23 -First 24 | % { $indexIdBytes[$indexIdIdx++] = $nibbler[$_ -shr 4];
$indexIdBytes[$indexIdIdx++] = $nibbler[$_ -band 0xF] }
$folderQuery = "folderid:$($encoding.GetString($indexIdBytes))";
$folderStat = New-Object PSObject
Add-Member -InputObject $folderStat -MemberType NoteProperty -Name Mailbox -Value $emailAddress
Add-Member -InputObject $folderStat -MemberType NoteProperty -Name FolderPath -Value $folderPath
Add-Member -InputObject $folderStat -MemberType NoteProperty -Name FolderQuery -Value $folderQuery
$folderQueries += $folderStat
}
}
}
$Excluded = ""
foreach ($folder in $folderQueries) {
$q = $folder.folderQuery
$Excluded += " NOT $q"
}
$Query += $Excluded
$Looper = $true
while ($Looper) {
try {
Write-Output "Creating new search"
New-ComplianceSearch -Name "WAT Remover" -ContentMatchQuery $Query -ExchangeLocation all -AllowNotFoundExchangeLocationsEnabled $true -Force | Out-Null
Start-ComplianceSearch -Identity "WAT Remover" | Out-Null
Write-Output "Waiting for search to complete"
while (!(Get-ComplianceSearch -Identity "WAT Remover" | Format-List -Property Status | Out-String | Select-String -Pattern "Completed")) {
Start-Sleep -s 1
}
$Results = (Get-ComplianceSearch "WAT Remover").Items
if ($Results) {
Write-Output "$Results results found, deleting"
for ($i = 0; $i -lt 10; $i++) {
Write-Output "Starting deletion run $i"
New-ComplianceSearchAction -SearchName "WAT Remover" -Purge -PurgeType HardDelete -Force -Confirm:$false | Out-Null
while (!(Get-ComplianceSearchAction -Identity "WAT Remover_Purge" | Out-String | Select-String -Pattern "Completed")) {
Start-Sleep -s 1
}
Write-Output "Results deleted"
Remove-ComplianceSearchAction -Confirm:$false -Identity "WAT Remover_Purge"
}
}
else {
Write-Output "No results found, exiting after this run"
$Looper = $false
}
}
finally {
Write-Output "Cleaning up"
Remove-ComplianceSearchAction -Confirm:$false -Identity "WAT Remover_Purge"
Remove-ComplianceSearch -Identity "WAT Remover" -Confirm:$false
}
}
Write-Output "Done - exiting"