Shared mailboxes are the forgotten citizens of your tenant. Everybody uses them, nobody pays attention to them, and that is exactly the problem: every shared mailbox has a full-blown user account in Entra ID. And that account can simply be enabled.
Why this is a real risk
A shared mailbox is supposed to be a mailbox without an owner: people access it through their own account with delegated permissions. The underlying Entra account is a technical by-product and should be blocked for sign-in. In practice, this goes wrong in two ways:
- Converted mailboxes. An employee leaves, the mailbox is converted to shared "so we can still get to it". The account stays enabled, the password stays valid, and sometimes even the license lingers. You now have a sign-in-capable account that nobody is watching anymore, often without MFA registration from an active owner.
- Manually created mailboxes where the "block sign-in" step was skipped. The password is system-generated and unknown, sure, but unknown is not the same as unusable: whoever has the rights to reset passwords has an inconspicuous way in.
For those who think in framework terms: this directly touches the management of inactive accounts and least privilege. An auditor who asks about this does not want to see a policy, but a list. We are going to make that list now.
The measurement
The nasty part is that you cannot see this in a single console. Exchange knows the mailbox, Entra knows the account and the licenses. So: pull the shared mailboxes from Exchange Online, per mailbox do the crosswalk to Entra via the ExternalDirectoryObjectId, and check AccountEnabled and the licenses there.
# Required: ExchangeOnlineManagement and Microsoft.Graph.Users
Connect-ExchangeOnline
Connect-MgGraph -Scopes "User.Read.All","Directory.Read.All"
# 1) All shared mailboxes from Exchange
$shared = Get-EXOMailbox -RecipientTypeDetails SharedMailbox -ResultSize Unlimited |
Select-Object DisplayName, PrimarySmtpAddress, ExternalDirectoryObjectId
# 2) Crosswalk to Entra: is the account enabled, and are there licenses attached?
$report = foreach ($m in $shared) {
try {
$u = Get-MgUser -UserId $m.ExternalDirectoryObjectId `
-Property Id, DisplayName, AccountEnabled, UserType, AssignedLicenses
[pscustomobject]@{
Mailbox = $m.DisplayName
Address = $m.PrimarySmtpAddress
AccountEnabled = $u.AccountEnabled
Licenses = @($u.AssignedLicenses).Count
Verdict = if ($u.AccountEnabled) { "SIGN-IN ENABLED" }
elseif (@($u.AssignedLicenses).Count) { "LICENSED" }
else { "OK" }
}
}
catch {
[pscustomobject]@{
Mailbox = $m.DisplayName; Address = $m.PrimarySmtpAddress
AccountEnabled = $null; Licenses = $null
Verdict = "Not found in Graph"
}
}
}
# 3) Worst cases on top, and record the results
$report | Sort-Object Verdict | Format-Table -AutoSize
$report | Export-Csv .\shared-mailbox-measurement.csv -NoTypeInformation -Encoding UTF8
Reading the result
The Verdict column tells you where the work is:
| Verdict | Meaning | Action |
|---|---|---|
| SIGN-IN ENABLED | The account is enabled and accepts authentication | Block it, today |
| LICENSED | Account disabled, but a license is attached | Reclaim the license (shared mailboxes up to 50 GB without an archive do not need one) |
| OK | Disabled and license-free | Nothing, this is how it should be |
Blocking is a single line:
Update-MgUser -UserId <objectId> -AccountEnabled:$false
And to those who think "we catch this with Conditional Access": maybe. But a blocked account is a guarantee, a CA policy is a configuration that can change. For accounts that should never sign in, the block is the only correct layer, with CA as the safety net on top of it, not the other way around.
From measurement to assurance
Running this script once gives you a cleanup action. The real gain is in repetition: every mailbox converted after today reintroduces the risk. So schedule the measurement periodically, or better: make blocking the account a fixed step in your offboarding and conversion process, and use the measurement as a check on that. Policy says what should happen; the export shows what has happened.
Questions about this script, or want to know how something like this fits into a broader baseline assessment? Let me know.