Automating GrantSendOnBehalfTo at Scale in Hybrid Exchange
If you manage a hybrid Exchange environment long enough, you'll eventually
run into a scenario where Set-Mailbox -GrantSendOnBehalfTo throws
an error that makes no immediate sense — particularly when the mailbox in
question appears perfectly healthy. The culprit is almost always an orphaned
entry lurking in the permission list.
What Is an Orphaned Entry?
When a user or group is granted Send on Behalf access and that object is
later deleted from Active Directory without the permission being explicitly
removed, the GrantSendOnBehalfTo attribute retains a dangling
reference — a Distinguished Name that points to nothing. Exchange Online
tolerates these silently until you attempt a full list replacement,
at which point the write operation fails entirely.
Get-Mailbox output looks clean,
but any attempt to update the list throws a resolution error.
Detecting the Problem
The first step is to identify which entries in the list are no longer
resolvable. We can do this by fetching the raw list and cross-referencing
each entry against Get-EORecipient:
# Requires: Connect-ExchangeOnline -Prefix "EO" function Get-OrphanedSendOnBehalf { [CmdletBinding()] param ( [Parameter(Mandatory)] [string] $MailboxIdentity ) $mailbox = Get-EOMailbox -Identity $MailboxIdentity -ErrorAction Stop $currentList = $mailbox.GrantSendOnBehalfTo if (-not $currentList) { Write-Host "No Send on Behalf entries found." -ForegroundColor Yellow return } $orphaned = @() $valid = @() foreach ($entry in $currentList) { try { $resolved = Get-EORecipient -Identity $entry -ErrorAction Stop $valid += $resolved.PrimarySmtpAddress } catch { $orphaned += $entry Write-Warning "Orphaned entry detected: $entry" } } [PSCustomObject] @{ Mailbox = $mailbox.PrimarySmtpAddress ValidEntries = $valid OrphanedEntries = $orphaned } }
Remediating Automatically
Once you've identified the orphaned entries, remediation is straightforward —
rebuild the list using only the valid, resolvable entries and write it back
in a single operation. Using -WhatIf first is strongly recommended
in production.
function Repair-SendOnBehalf { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory)] [string] $MailboxIdentity ) $result = Get-OrphanedSendOnBehalf -MailboxIdentity $MailboxIdentity if ($result.OrphanedEntries.Count -eq 0) { Write-Host "No orphaned entries found. No action required." -ForegroundColor Green return } Write-Host "`nOrphaned entries to remove:" -ForegroundColor Yellow $result.OrphanedEntries | ForEach-Object { Write-Host " - $_" } if ($PSCmdlet.ShouldProcess($MailboxIdentity, "Remove orphaned SendOnBehalf entries")) { Set-EOMailbox -Identity $MailboxIdentity ` -GrantSendOnBehalfTo $result.ValidEntries Write-Host "Done. List updated successfully." -ForegroundColor Green } } # Usage Repair-SendOnBehalf -MailboxIdentity "shared-finance@corp.com" -WhatIf Repair-SendOnBehalf -MailboxIdentity "shared-finance@corp.com"
Why Not Just Clear the Entire List?
You might wonder why we don't simply clear GrantSendOnBehalfTo
and start fresh. In many cases that's acceptable, but in environments where
permissions were set historically and not tracked elsewhere, clearing the entire
list risks losing legitimate delegations. The filtered replacement approach
above preserves all valid entries while surgically removing the broken ones.
Bulk Remediation Across All Mailboxes
In large environments, running this against every shared mailbox is essential. Pipe all shared mailboxes into the repair function and log the output:
Get-EOMailbox -RecipientTypeDetails SharedMailbox -ResultSize Unlimited | ForEach-Object { Repair-SendOnBehalf -MailboxIdentity $_.PrimarySmtpAddress }
GrantSendOnBehalfTo
must ultimately be set on-premises via Set-RemoteMailbox for AD
Connect to sync it upstream. The EXO approach above is correct for cloud-only
mailboxes; for synced mailboxes, apply the same logic against your on-premises
Exchange and let AD Connect handle the writeback.
Key Takeaways
- Orphaned
GrantSendOnBehalfToentries are invisible during reads but break write operations silently. - Use
Get-EORecipientto validate each entry before any full-list replacement. - Always use
-WhatIfand preview output before committing changes in production. - In hybrid environments, the authoritative write point is on-premises via
Set-RemoteMailbox.