Guides · PowerShell

Shared Mailbox Permission Reconciliation with PowerShell: Architecture, AutoMapping and Offboarding

Granting one permission is a single cmdlet. Keeping that permission correct as group membership, disabled accounts and existing delegation all move underneath it is the part that turns into a real piece of software.

Updated Aug 2026 · 11 min read

The source-of-truth group

Everything in this architecture starts from one decision: an AD or Entra security group represents who should have access to a shared mailbox. Membership in that group is the input; every downstream Exchange permission is a derived artifact of it, not a second place where access decisions get made independently.

Resolving desired state means reading that group's membership recursively —Get-ADGroupMember -Recursive for on-prem AD — so nested groups flatten into the individual accounts that actually need the mailbox.

Reading current Exchange state

The other half of the comparison is what Exchange Online currently believes.Get-MailboxPermission returns Full Access grants, andGet-RecipientPermission returns Send As grants; Send on Behalf lives on the mailbox object itself as GrantSendOnBehalfTo. Each needs its own read, its own comparison, and its own add/remove logic — they are not interchangeable and a script that only checks one will miss drift in the others.

Filter out what Exchange adds on its own — inherited and system entries that aren't part of anyone's delegation model — before comparing. Otherwise every reconciliation run treats Exchange's own bookkeeping as unexpected drift.

Explicit permissions and AutoMapping

Outlook's AutoMapping feature depends on msExchDelegateListLink being written for an individual mailbox object at the moment Full Access is granted. Granting Full Access to a security group does not populate that attribute for the group's individual members — the permission is real, but the mailbox never appears in their Outlook profile automatically. A reconciliation script has to grant Full Access directly, per user, with AutoMapping left enabled, and use the group only to compute who that list of users should be.

Additions

For every account in desired state but missing from actual state, runAdd-MailboxPermission for Full Access,Add-RecipientPermission for Send As, or append toGrantSendOnBehalfTo for Send on Behalf. This direction is the one most manual scripts already get right, because it's the one someone notices immediately when it doesn't happen.

Removals

For every account in actual state but no longer in desired state, the mirror-image cmdlets apply: Remove-MailboxPermission,Remove-RecipientPermission, or removing the entry fromGrantSendOnBehalfTo. This direction is the one that gets skipped, and skipping it is what turns a reconciliation script into an add-only script — permissions that only ever accumulate. Removing Full Access also clears the AutoMapping entry, so the mailbox drops out of Outlook, though the client-side update rides on the next Autodiscover refresh rather than happening instantly.

Permission ownership

The hardest architectural decision in the whole system: before removing anything, the script must know whether it was the one that granted it. A naive reconciliation treats "not in the group" as "should be removed," which is only safe if every permission on the mailbox came from this exact script in the first place. In any real tenant, some permissions were granted manually, by a different admin, for a different reason, and removing them automatically is a production incident waiting to happen. The fix is to maintain an explicit ownership record — a log, a tag, a separate tracking store — of which grants the script itself created, and only ever remove permissions that appear in that record.

Existing access and adoption

A mailbox almost never starts empty. Before reconciliation can safely run against it, the script needs an adoption pass: read current direct delegations, decide which ones should become managed going forward, and record that decision — without revoking anything in the process. Skipping this step means either rebuilding every mailbox's permissions from scratch before automation can begin, or letting reconciliation immediately misinterpret every pre-existing grant as drift.

Logging, retries and scheduled execution

A reconciliation script that runs unattended on a schedule needs three things a one-off script doesn't: a log of every grant and revoke it actually performed (for audit and for debugging when something looks wrong later), retry handling for the Exchange Online cmdlets that transiently throttle or time out under load, and a scheduling mechanism — Windows Task Scheduler or an Azure Automation runbook — with monitoring that notices when a run fails silently rather than assuming no news is good news.

Concurrency is worth guarding against directly: two overlapping runs against the same mailbox can race each other and leave inconsistent state, so a lock or mutex around each reconciliation pass is cheap insurance.

Audit as a separate, read-only pass

Reconciliation only protects the mailboxes it actively manages, and only catches drift the next time it runs. A separate audit pass — one that never writes anything — should periodically check for disabled accounts still holding access, delegates that no longer resolve to any account, mapped groups that have gone missing or lost their security flag, and direct access that exists outside the managed group entirely. Keeping audit read-only and separate from reconciliation means a finding never turns into an unreviewed automatic change.

When DIY PowerShell becomes operationally expensive

None of the pieces above are individually hard. What makes this expensive over time is maintaining all of them together, correctly, as the one engineer who understands the script changes roles, as Exchange Online's cmdlet behavior shifts across tenant updates, and as the number of mailboxes and admins making manual exceptions grows. The tipping point is usually not a single mailbox — it's the point where nobody can confidently answer "if I run this script right now, what will it change?" without reading the code first.

If you no longer want to maintain the reconciliation framework yourself, Tenvero packages that workflow into a managed Windows administration tool.

Skip maintaining the reconciliation framework

Tenvero runs this model — desired state, explicit AutoMapping-aware grants, two-way reconciliation, ownership-scoped removals and audit — on your own tenant. 30-day trial, no credit card.

Start 30-day trial