Situatie: per ongeluk meerdere keren geïmporteerd
Tijdens een mailboxmigratie is er iets misgegaan: de mailbox van een gebruiker is meerdere keren geïmporteerd.
Het gevolg? Elke e-mail stond er twee of zelfs drie keer in. Vooral in de inbox was dit goed te merken.
Dat gebeurde niet vanwege een fout in de tooling, maar simpelweg door een menselijke vergissing: een importactie werd herhaald zonder te controleren of deze al succesvol was afgerond. Kan gebeuren helaas!
Voor de gebruiker was dit natuurlijk erg onhandig.
Niet alleen werd de mailbox onoverzichtelijk, ook het synchroniseren met Outlook werd merkbaar trager.
Waarom een script?
Handmatig opruimen was geen optie.
De mailbox bevatte duizenden berichten. Dat kost niet alleen veel tijd, het vergroot ook de kans op fouten. Daarnaast wilden we de gebruiker er zo min mogelijk mee belasten.
Daarom heb ik een PowerShell-script gemaakt dat:
- Dubbele e-mails automatisch opspoort,
- Alleen echte duplicaten verwijdert,
- En veilig uitgevoerd kan worden vanuit de Exchange Management Shell.
Het script is gemaakt voor Exchange 2016, maar de aanpak is breder toepasbaar.
Hoe detecteer je dubbele e-mails?
Om dubbele berichten op te sporen, heb ik gebruikgemaakt van het veld InternetMessageId.
Dit is een unieke waarde die aan elk e-mailbericht wordt meegegeven bij verzending. Ook als een bericht meerdere keren wordt geïmporteerd, blijft deze waarde hetzelfde.
Daarmee is dit veld zeer geschikt om duplicaten te herkennen, zelfs als de aanmaakdatum of interne ID per import verschilt.
Stap voor stap: wat doet het script?
Ik heb ervoor gekozen om het script te verdelen over twee bestanden. Waarom? Omdat een oud collega me ooit vertelde dat als iets complex wordt, je het dan beter kan opdelen om het overzicht te bewaren. Deze vuistregel heb ik eigenlijk altijd vastgehouden. Mocht je het makkelijker vinden, dan is samenvoegen natuurlijk ook mogelijk en niet heel complex.
Hieronder de stappen die in de twee scripts doorlopen worden:
- Rechten op mailbox zetten (stap 1)
Voordat er begonnen kan worden, zal het account dat je gebruikt eerst rechten moeten krijgen op de betreffende mailbox. - Verbindt met de mailbox (stap 2)
Het script opent een sessie als beheerder naar de mailbox die opgeschoond moet worden. - Laadt alle e-mailitems uit de opgegeven folder (stap 2)
In dit geval ging het om de Inbox, maar dit is eenvoudig aan te passen. - Bouwt een lijst van unieke berichten op (stap 2)
Door hetInternetMessageIdte vergelijken, wordt per bericht gecontroleerd of het al eerder is gezien. - Verwijdert de duplicaten (stap 2)
Alle e-mails die een duplicaat blijken te zijn, worden verwijderd metSoftDelete.
De rechten verwijderen die in stap 1 zijn toegepast heb ik bewust weggelaten. Na de actie wilde ik namelijk eerst zelf controleren wat het resultaat was, voordat ik de collega van een update voorzag.
Het script: Stap1-DubbeleMailsVerwijderen.ps1
Clear-Host
Write-Host "=== STAP 1: Machtiging toekennen aan mailbox ===" -ForegroundColor Cyan
# Gebruikersinvoer
$MailboxToAccess = Read-Host "Geef het e-mailadres van de mailbox waarop toegang moet worden verleend"
$DelegateAccount = "Exchange-Admin" #Deze kun je eventueel ook als Read-Host doen
try {
$existingPerm = Get-MailboxPermission -Identity $MailboxToAccess -User $DelegateAccount -ErrorAction SilentlyContinue
if (-not $existingPerm) {
Write-Host "Toekennen van FullAccess aan $DelegateAccount op mailbox $MailboxToAccess..."
Add-MailboxPermission -Identity $MailboxToAccess -User $DelegateAccount -AccessRights FullAccess -InheritanceType All
} else {
Write-Host "$DelegateAccount heeft al toegang tot $MailboxToAccess."
}
}
catch {
Write-Warning "Kon machtigingen niet instellen: $($_.Exception.Message)"
return
}
# Vraag of stap 2 gestart moet worden
$antwoord = Read-Host "Wil je nu doorgaan met het verwijderen van dubbele e-mails in deze mailbox? (j/n)"
if ($antwoord -eq "j") {
$scriptPad = ".\Stap2-DubbeleMailsVerwijderen.ps1"
if (Test-Path $scriptPad) {
Write-Host "Start script 2..."
Start-Process powershell -ArgumentList "-ExecutionPolicy Bypass -File `"$scriptPad`" -MailboxToProcess `"$MailboxToAccess`"" -Wait
} else {
Write-Warning "Script 2 niet gevonden: $scriptPad"
}
} else {
Write-Host "Stap 2 is niet uitgevoerd."
}
Het script: Stap2-DubbeleMailsVerwijderen.ps1
Clear-Host
Write-Host "=== STAP 2: Verwijderen van dubbele e-mails ===" -ForegroundColor Cyan
# Gebruikersinvoer
$MailboxToProcess = Read-Host "Geef het e-mailadres van de mailbox waarvan je dubbele e-mails wilt verwijderen"
Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"
$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2016
$ExchangeServer = "mail.domein.local" # Pas aan
function Get-MailboxIdentityFromEmail {
param($Email)
$Searcher = New-Object DirectoryServices.DirectorySearcher
$Searcher.Filter = "(&(objectClass=user)(mail=$Email))"
$Result = $Searcher.FindOne()
if ($Result) {
return $Result.Properties["mail"][0]
} else {
throw "Mailbox voor $Email niet gevonden in AD."
}
}
# EWS sessie opbouwen
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
$service.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
$service.Url = "https://$ExchangeServer/EWS/Exchange.asmx"
# Mailbox vertalen en impersoneren
$ResolvedEmail = Get-MailboxIdentityFromEmail -Email $MailboxToProcess
$service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId(
[Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $ResolvedEmail
)
# Inbox ophalen
$mailbox = New-Object Microsoft.Exchange.WebServices.Data.Mailbox($ResolvedEmail)
$inboxId = New-Object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox, $mailbox)
$inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service, $inboxId)
# Items zoeken
$view = New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)
$view.OrderBy.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::LastModifiedTime, "Descending")
$view.PropertySet = [Microsoft.Exchange.WebServices.Data.PropertySet]::FirstClassProperties
$items = $inbox.FindItems($view)
# Duplicaten verplaatsen
$seen = @{}
foreach ($item in $items) {
try {
$key = "$($item.Subject)|$($item.InternetMessageId)|$($item.DateTimeReceived.ToUniversalTime())"
if ($seen.ContainsKey($key)) {
Write-Host "Dubbele e-mail gevonden: $($item.Subject) - verplaatsen naar Deleted Items..."
$item.Move([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::DeletedItems)
} else {
$seen[$key] = $true
}
} catch {
Write-Warning "Fout bij item: $($_.Exception.Message)"
}
}
Wat heb ik hiervan geleerd?
- InternetMessageId is zeer betrouwbaar voor het herkennen van duplicaten.
- Met PowerShell kun je snel en veilig opschonen, zonder dat gebruikers er iets van merken.
- Een gestructureerde aanpak voorkomt herhaling van fouten: test het script eerst in een niet-productieomgeving. Als dit niet anders kan, gebruik dan eerst je eigen mailbox als test!
Ook belangrijk: documenteer dit soort scripts goed, zodat collega’s het eenvoudig kunnen hergebruiken. En zorg voor logging of rapportage als je het breder gaat inzetten.
Wat levert dit op voor anderen?
Misschien heb jij dit ook wel eens meegemaakt: een dubbele import, een foutje in een migratiescript of een restore die net even te vaak is uitgevoerd.
Het zijn menselijke fouten – maar wel fouten met grote gevolgen voor eindgebruikers.
Met dit script kun je dat soort situaties snel rechttrekken:
- Zonder handmatig werk,
- Zonder overlast voor de gebruiker,
- En zonder risico op gegevensverlies (de items worden verplaatst naar de verwijderde items).
Juist in complexe omgevingen is het fijn als je dit soort tools achter de hand hebt. Je kunt ze snel inzetten, aanpassen aan jouw scenario, of meenemen in je standaard migratie-aanpak.
Ik deel dit script en de aanpak dan ook graag, zodat anderen er hun voordeel mee kunnen doen. Werk je zelf in een Exchange-omgeving of voer je mailboxmigraties uit? Dan hoop ik dat dit artikel je tijd en frustratie bespaart.
💬 Loop je zelf tegen dit probleem aan of wil je het script aanpassen voor jouw situatie? Laat het vooral weten – ik denk graag met je mee. Bijvoorbeeld hoe je:
- meerdere mailboxen tegelijk behandelt,
- logging toevoegt voor rapportage,
- of een dry-run opzet voor extra controle.
🔧 Deze case maakte ik als onderdeel van mijn werk bij The.NextGen, waar ik organisaties help om moderne communicatie en samenwerkingstechnologie te verbeteren. Praktisch, veilig en toekomstgericht.
Wil je hier meer over weten? Dan kom ik graag in contact.