Current version

This commit is contained in:
Kumi 2023-08-26 15:08:29 +02:00
commit 0a59e028c9
Signed by: kumi
GPG key ID: ECBCC9082395383F
3 changed files with 183 additions and 0 deletions

19
LICENSE Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2020-2023 Kumi Mitterer <office365-mail-purger@kumi.email>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

28
README.md Normal file
View file

@ -0,0 +1,28 @@
# Office 365 email purger
This PowerShell script will use Compliance Search to purge all content older than a specified date involving one or more specified users from all mailboxes in an Office 365 tenant.
## Usage
1. Download the script and save it to your computer.
2. Edit the script and change the following lines to match your environment:
```powershell
$Addresses = @("user@example.com", "user2@example.com")
$MaxDate = "2016-01-01"
```
3. Run the script. You will be prompted for your Office 365 credentials.
4. Get a cup of coffee, and then another. This script will take a while to run. As in, hours or days, depending on the size of your organization.
## Notes
* This script will delete all content older than the specified date, including items in the user's mailbox that were sent to or received from other users in the organization.
* The larger your organization, the longer this script will take to run.
* This is quite a dangerous script. Use it at your own risk.
* This script may delete more or less than you expect.
* The script is provided as-is. I am not responsible for any damage caused by running it.
## License
This script is licensed under the [MIT License](LICENSE).

136
purger.ps1 Normal file
View file

@ -0,0 +1,136 @@
#!/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"