SharePointVault ~/gotchas/graph ⌕ Search 404 pages, error messages, scripts…Ctrl K ★ Star
M↓ README.mdM↓ application-access-policy-rejects-shared-mailbox.md
sharepoint-vault›gotchas›graph›application-access-policy-rejects-shared-mailbox.mdhistoryedit on GitHub
● GOTCHAgraph· 4 min read

ApplicationAccessPolicy won't scope to a shared mailbox

applies to: Exchange Online, Microsoft Graph (application permissions), ExchangeOnlineManagement PowerShellreviewed: 2026-10-09#graph#exchange-online#mail-send#permissions#powershell#shared-mailbox

After granting an app the application-level Mail.Send permission it can send as anyone in the tenant, so you fence it in with New-ApplicationAccessPolicy. Passing the shared mailbox as -PolicyScopeGroupId fails with The identity of the policy scope is not a security principal. — a shared mailbox has a disabled account and isn't one. Put the mailbox in a mail-enabled security group and scope the policy to that group instead.

Když aplikaci udělíte aplikační oprávnění Mail.Send, může odesílat za kohokoli v tenantu – proto se rozsah omezuje přes New-ApplicationAccessPolicy. Předání sdílené schránky jako -PolicyScopeGroupId skončí chybou The identity of the policy scope is not a security principal., protože sdílená schránka má zakázaný účet a principal to není. Vložte schránku do mail-enabled security group a policy nasměrujte na tu skupinu.

Symptom

You created a shared mailbox for an unattended sender (support@, noreply@) and want the app restricted to it:

New-ApplicationAccessPolicy -AppId <appId> `
  -PolicyScopeGroupId support@contoso.com `
  -AccessRight RestrictAccess -Description "Mailer - support mailbox only"
New-ApplicationAccessPolicy: ||The identity of the policy scope is not a security principal.

Using the mailbox GUID (ExternalDirectoryObjectId) instead of the SMTP address fails the same way.

Cause

-PolicyScopeGroupId accepts a security principal: a mail-enabled security group, a Microsoft 365 group, or a regular user mailbox. A shared mailbox is backed by a disabled user account, so Exchange refuses it as a policy scope.

Waiting doesn't help either. A freshly created mailbox also emits An error occurred while trying to prepopulate newly created mailbox … Error: 0x8004010F warnings, which look like a replication delay and tempt you into retrying for a while — but the rejection is by design, not a timing issue.

Fix

Wrap the mailbox in a mail-enabled security group and scope the policy to the group:

$smtp  = "support@contoso.com"
$appId = "<application (client) id>"
$group = "Mailer Scope"

# 1) mail-enabled security group holding every mailbox the app may send as
New-DistributionGroup -Name $group -Type Security `
  -PrimarySmtpAddress "mailer-scope@contoso.com"
Add-DistributionGroupMember -Identity $group -Member $smtp

# 2) scope the app to that group
New-ApplicationAccessPolicy -AppId $appId `
  -PolicyScopeGroupId "mailer-scope@contoso.com" `
  -AccessRight RestrictAccess -Description "Mailer - scoped senders only"

# 3) verify BOTH directions - a policy you never tested is a policy you don't have
Test-ApplicationAccessPolicy -Identity $smtp -AppId $appId              # Granted
Test-ApplicationAccessPolicy -Identity ceo@contoso.com -AppId $appId    # Denied

Adding another allowed sender later is then group membership, not a second policy.

Propagation takes up to about an hour. Right after the change, a 403 from sendMail is expected — wait before you debug it.

Related traps in the same job

A shared mailbox needs no licence. New-Mailbox -Shared gives you up to 50 GB for free and app-only Mail.Send works against it normally — the right home for an unattended support@ sender.

Exchange sessions don't survive between separate shell invocations. Connect-ExchangeOnline lives in process memory only. If your automation runs each command in a fresh process, connect and run the commands in one invocation; Connect-AzAccount behaves differently because it persists its context to disk.

You may not need a second interactive sign-in for Graph. If you're already signed in with Azure PowerShell as an administrator, Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com" often returns a token whose scp already covers Application.ReadWrite.All and AppRoleAssignment.ReadWrite.All — enough to create the app registration and grant admin consent over REST. Decode the payload and check scp before relying on it. The token comes back as a SecureString; unwrap it with [System.Net.NetworkCredential]::new("", $token).Password.

Function App settings are written as a whole set. PUT …/config/appsettings replaces everything. Read the current settings, merge your keys in, then write — and afterwards verify the pre-existing keys are still present. Writing only the new keys wipes the rest.

Adding a second sender later

The sender must be a mailbox. /users/{address}/sendMail works for user and shared mailboxes only. A distribution list such as info@ cannot be the sender; check Get-Recipient <address> | Select RecipientTypeDetails before you put the address into configuration.

Extend the scope by group membership, not by a new policy. Add the new mailbox to the mail-enabled security group the policy points at. If the *-ApplicationAccessPolicy cmdlets are missing from your Exchange Online session (they were absent for a Global Administrator in ExchangeOnlineManagement 3.10 while the policy was still enforced), find the group by its members: list Get-DistributionGroup -RecipientTypeDetails MailUniversalSecurityGroup and pick the one that already contains the working sender mailbox.

Until the change propagates, Graph keeps answering 403 with Access to OData is disabled: [RAOP] : Blocked by tenant configured AppOnly AccessPolicy settings. In our case it still failed after 15 minutes and passed after about an hour. Test once after an hour instead of in a loop, especially when each test creates real records or e-mails.

Was this helpful?ask or add your experience on GitHub →
Saved you an hour? A star on GitHub helps other SharePoint people find the vault.★ Star on GitHub
last reviewed: 2026-10-09last updated: 2026-10-09historyedit on GitHubview source