
Remove Stale Exchange Mailbox Delegates and Restore Security
Before you run this
This guide finds and removes delegate access that a former or reassigned user still holds on other people's mailboxes in Exchange Online — the four kinds that actually let one account read or send as another: Full Access, Send As, Send on Behalf, and folder-level (calendar) delegate permissions. Its purpose is offboarding hygiene: when someone leaves or changes roles, their standing access to shared and personal mailboxes should go with them.
Privileges: You need an Exchange Online account with rights to manage recipient permissions — an Exchange Administrator role, or a custom RBAC role holding Mail Recipients and Mail Recipient Creation. It runs from a normal (non-elevated) PowerShell session; you do not need local admin on your workstation. You do need the ExchangeOnlineManagement module (v3 or later) installed.
This changes access control, so treat it carefully. Removing a permission is reversible — you can re-grant it — but if you strip access from a live delegate (an assistant who genuinely still needs a shared mailbox), you break their workflow until you notice. Orphaned SID entries (permissions left behind by a fully deleted account) cannot be re-granted once removed, because the account no longer exists; that removal is effectively permanent, though it is exactly what you want to clean up.
Test first. Read the script before running it. Run every removal with -WhatIf first and read the output. Then run it for real against one test mailbox and one test delegate before you point it at production mailboxes. Do not paste-and-run a bulk loop blind across your tenant.
I assume Exchange Online, the ExchangeOnlineManagement v3 module, and PowerShell 5.1 or 7. On-premises Exchange differs in two places I flag below (Send As uses Get-ADPermission/Remove-ADPermission, not Get-RecipientPermission).
Connect
# One-time install if you don't have it
Install-Module ExchangeOnlineManagement -Scope CurrentUser
Connect-ExchangeOnline -UserPrincipalName admin@example.com
Replace admin@example.com with your admin account. This uses modern auth and will prompt for MFA.
Audit one mailbox before you touch anything
Start by seeing what a specific mailbox hands out. Point it at the mailbox you're cleaning, not the departed user.
$Mailbox = "shared-sales@example.com" # the mailbox being cleaned
# Full Access — exclude self, system accounts, and inherited rights
Get-MailboxPermission -Identity $Mailbox |
Where-Object { $_.IsInherited -eq $false -and
$_.User -notlike "NT AUTHORITY\*" } |
Select-Object User, AccessRights, Deny
# Send As
Get-RecipientPermission -Identity $Mailbox |
Where-Object { $_.Trustee -notlike "NT AUTHORITY\*" } |
Select-Object Trustee, AccessRights
# Send on Behalf
(Get-Mailbox -Identity $Mailbox).GrantSendOnBehalfTo
# Calendar delegate permissions (folder-level)
Get-MailboxFolderPermission -Identity "$($Mailbox):\Calendar" |
Where-Object { $_.User.DisplayName -notin @("Default","Anonymous") } |
Select-Object User, AccessRights
Look for two things: named delegates who have left or changed roles, and orphaned SIDs — entries where User shows as a raw string like S-1-5-21-... instead of a display name. Those are permissions left by deleted accounts, and they're the classic stale artefact.
On-premises Exchange: replace the
Get-RecipientPermissionblock withGet-ADPermission -Identity $Mailbox | Where-Object { $_.ExtendedRights -like "*Send-As*" }.
Remove access for a departed delegate
This function removes all four permission types for one delegate on one mailbox. It runs a dry run by default — nothing is removed unless you pass -Execute. Read the -WhatIf output first.
function Remove-MailboxDelegate {
[CmdletBinding()]
param(
[Parameter(Mandatory)] [string]$Mailbox, # mailbox to clean
[Parameter(Mandatory)] [string]$Delegate, # user/SID losing access
[switch]$Execute # omit for a dry run
)
# -WhatIf on a dry run, -Confirm:$false on the real run
$common = if ($Execute) { @{ Confirm = $false } } else { @{ WhatIf = $true } }
Write-Host "Full Access:" -ForegroundColor Cyan
Remove-MailboxPermission -Identity $Mailbox -User $Delegate `
-AccessRights FullAccess @common -ErrorAction SilentlyContinue
Write-Host "Send As:" -ForegroundColor Cyan
Remove-RecipientPermission -Identity $Mailbox -Trustee $Delegate `
-AccessRights SendAs @common -ErrorAction SilentlyContinue
Write-Host "Send on Behalf:" -ForegroundColor Cyan
if ($Execute) {
Set-Mailbox -Identity $Mailbox `
-GrantSendOnBehalfTo @{ Remove = $Delegate } -Confirm:$false `
-ErrorAction SilentlyContinue
} else {
Write-Host " (dry run) would remove $Delegate from GrantSendOnBehalfTo"
}
Write-Host "Calendar delegate:" -ForegroundColor Cyan
Remove-MailboxFolderPermission -Identity "$($Mailbox):\Calendar" `
-User $Delegate @common -ErrorAction SilentlyContinue
}
I use -ErrorAction SilentlyContinue deliberately: not every delegate holds every permission type, and a "permission doesn't exist" error on one type shouldn't stop the others. That's the trade-off — read the audit output so you know what you expect to remove.
Dry run, then execute:
# 1) See what would change
Remove-MailboxDelegate -Mailbox "shared-sales@example.com" -Delegate "jsmith@example.com"
# 2) Do it for real
Remove-MailboxDelegate -Mailbox "shared-sales@example.com" -Delegate "jsmith@example.com" -Execute
Set-Mailbox -GrantSendOnBehalfTo @{Remove=...} has no -WhatIf here that reports cleanly per-entry, which is why I gate the whole Send-on-Behalf action behind -Execute and just print intent on the dry run.
Cleaning orphaned SIDs
For a permission left by a deleted account, pass the SID string straight through as the delegate. Copy the exact User value from the audit output:
Remove-MailboxDelegate -Mailbox "shared-sales@example.com" `
-Delegate "S-1-5-21-1234567890-0987654321-1122334455-5001"
# add -Execute once the dry run looks right
Send As and Send on Behalf are unlikely to hold an orphaned SID, so expect those two lines to report nothing — that's normal.
Finding every mailbox a departed user can touch
To sweep a whole tenant for one departed person's Full Access rights, loop the mailboxes. This is read-only and only reports; feed the results into the removal function once you've reviewed them.
$Departed = "jsmith@example.com"
Get-Mailbox -ResultSize Unlimited | ForEach-Object {
$mbx = $_.PrimarySmtpAddress
Get-MailboxPermission -Identity $mbx |
Where-Object { $_.User -eq $Departed -and $_.IsInherited -eq $false } |
Select-Object @{n="Mailbox";e={$mbx}}, User, AccessRights
}
This is slow on a large tenant — it queries every mailbox — so run it off-hours and let it finish. It intentionally does not remove anything.
Verify
Re-run the audit block from the top against the same mailbox. The departed delegate or orphaned SID should no longer appear in any of the four listings:
Get-MailboxPermission -Identity "shared-sales@example.com" |
Where-Object { $_.User -eq "jsmith@example.com" } # expect no output
Get-RecipientPermission -Identity "shared-sales@example.com" |
Where-Object { $_.Trustee -eq "jsmith@example.com" } # expect no output
Note that Full Access auto-mapping (the automatic appearance of a mailbox in Outlook) can take time to clear from clients even after the permission is gone; the permission removal above is what matters for security.
Undo
Removals of a still-valid account are reversible with the matching Add- cmdlets. Re-grant only what you actually need back:
Add-MailboxPermission -Identity "shared-sales@example.com" -User "jsmith@example.com" -AccessRights FullAccess -AutoMapping:$true
Add-RecipientPermission -Identity "shared-sales@example.com" -Trustee "jsmith@example.com" -AccessRights SendAs
Set-Mailbox -Identity "shared-sales@example.com" -GrantSendOnBehalfTo @{ Add = "jsmith@example.com" }
Add-MailboxFolderPermission -Identity "shared-sales@example.com:\Calendar" -User "jsmith@example.com" -AccessRights Editor
An orphaned-SID removal cannot be undone — the account behind that SID is gone, so there is nothing to re-grant, and that is the point of clearing it.
For exact parameters and access-right values, see Microsoft Learn for Remove-MailboxPermission, Remove-RecipientPermission, Set-Mailbox, and Remove-MailboxFolderPermission. Confirm any value there before running it in production rather than trusting it from memory.
Runs enterprise networks and security for a living, and writes Shore Up to turn two decades of hands-on Linux, Windows and mail-server work into guides you can actually use.
More about the author →Was this article helpful?
Tap a star — no sign-in needed.
Be the first to rate this article.
Related guides
Bulk-Create Shared Mailboxes and Permissions in Exchange Online with PowerShell
This guide reads a CSV file and, for each row, creates a shared mailbox in Exchange Online and grants the people you list Full Access and Send As on it. The purpose is to onboard many shared mailboxes (team@, support@, invoices@) in one pass instead of clicking through the admin center dozens of times.
Bulk-Set Out-of-Office Auto-Replies with Exchange PowerShell
This guide sets the Automatic Replies (out-of-office) configuration on multiple mailboxes at once, using Set-MailboxAutoReplyConfiguration in Exchange Online PowerShell. It turns auto-reply on (or schedules it), and writes the internal and external message text. It does not delete mail, move anything, or change mailbox permissions — but it does overwrite whatever auto-reply text and state each affected mailbox currently has, and there is no built-in "undo" that restores the previous message. If a user had their own carefully worded reply set, this replaces it. So capture the current state first (I show how below) and treat the change list as production data.
Automate a Network Share Permissions Audit with PowerShell
This guide builds a read-only report . The script enumerates the SMB shares on a Windows file server, then lists two things for each one: the share-level permissions (the "who can connect" layer) and the NTFS permissions on the folder behind it (the "who can touch the files" layer). It writes both to CSV so you can review access in a spreadsheet instead of clicking through the Security tab share by share.
Automate User Offboarding in Active Directory with PowerShell
This script offboards one leaving user in a single pass: it disables their AD account, records and removes their group memberships (except the primary group), and moves the account into a disabled-users OU. A separate, clearly marked step sets mail forwarding on their mailbox. The point is a consistent, logged procedure so nothing gets missed and you can reconstruct exactly what changed.




