Sending email from a shared mailbox with Microsoft Graph (app-only)
How to configure an app in Entra ID and the permissions needed to send email from a shared mailbox through Microsoft Graph in app-only mode.
In this article15sections
- Introduction
- Scenario: automatic notifications from a shared mailbox
- Why use Microsoft Graph app-only?
- Prerequisites
- Solution overview
- Step-by-step procedure
- 1. Create (or identify) the shared mailbox
- 2. Register the application in Entra ID
- 3. Create the client secret
- 4. Add the Microsoft Graph permissions (Mail.Send – Application)
- 5. Restrict the app to the shared mailbox only (Application Access Policy)
- 6. Gather the parameters for the PowerShell script
- Once the script has been scheduled and run, the email will arrive at the specified addresses:
- Critical points and good practice
- Conclusion
Introduction
In everyday automation (backups, scheduled jobs, monitoring scripts) you often need to send a notification email from a shared mailbox in the tenant, for example alert@azienda.it. In the past this was handled with SMTP and basic authentication; today, with basic auth retired in Exchange Online, the recommended approach is to use Microsoft Graph in app-only mode (client credentials flow).
In this article we will look at how to prepare the tenant to send email from a shared mailbox using Graph. In particular we will focus on:
- registering the app in Entra ID;
- configuring the Microsoft Graph permissions;
- restricting the app to the shared mailbox only through an Application Access Policy;
- gathering the parameters to use in the PowerShell script (which you will add in the final section).
Scenario: automatic notifications from a shared mailbox
The typical scenario is as follows:
- a PowerShell script runs on a server (on-prem or a VM in the cloud);
- at the end of an operation (e.g. a successful or failed backup) the script needs to send a notification email;
- the email is sent from a shared technical mailbox, e.g.
alert@azienda.it, to one or more internal/external recipients.
Instead of using a real user’s credentials, the script uses an application identity (App Registration) to obtain an access token and call the Graph sendMail endpoint on the shared mailbox.
Why use Microsoft Graph app-only?
Using Graph in app-only mode has several advantages:
- No user credentials: the script does not depend on a human account that could be disabled, expire or require MFA.
- A clear permission model: permissions are defined at application level (
Mail.Sendunder Application Permissions) and can be further restricted to a specific set of mailboxes. - Compliance with Microsoft best practice: it is in line with the security recommendations for Exchange Online and M365.
At the same time, an app with application-level Mail.Send could in theory send email from any mailbox in the tenant. That is why it is essential to add a further layer of control through an Application Access Policy, which we will look at later.
Prerequisites
Before you start, make sure you have:
- An existing shared mailbox (e.g.
alert@azienda.it) or the ability to create a new one. - Adequate permissions:
- the Application Administrator or Global Administrator role for the Entra ID / Graph part;
- the Exchange Administrator role to configure Application Access Policies.
- Access to the Entra ID portal (formerly Azure AD) and the Exchange admin center.
- PowerShell with the required modules installed, available on the machine the scripts will run from.
Solution overview
The solution is built on four key elements:
- Shared mailbox: the “technical” mailbox the notifications will be sent from (e.g.
alert@azienda.it). - App Registration in Entra ID: the application identity the script will use to authenticate.
- Microsoft Graph permissions (Mail.Send – Application): these allow the app to send email.
- Application Access Policy: restricts the app to the designated shared mailbox only, preventing misuse of other mailboxes.
The PowerShell script:
- requests an OAuth2 token with the
client_credentialsgrant type; - calls the
https://graph.microsoft.com/v1.0/users/{sharedMailbox}/sendMailendpoint; - sends the notification email using the shared mailbox as the sender.
Step-by-step procedure
1. Create (or identify) the shared mailbox
If you already have a shared mailbox to use as the sender, you can simply refer to that address. Otherwise:
- Sign in to the Exchange admin center.
- Go to Recipients → Shared.
- Click Add a shared mailbox.
- Fill in:
- Name: e.g.
Alert Backup. - Email: e.g.
alert@azienda.it.
- Name: e.g.
- Complete the mailbox creation.
This will be the identity Graph sends the emails “from” in your script:

2. Register the application in Entra ID
The app you register in Entra ID is the technical identity the script uses to authenticate against Microsoft Graph.
- Go to the Entra ID portal → App registrations.
- Click New registration.
- Set:
- Name: for example
Graph-SharedMailbox-Alert. - Supported account types: Single tenant is usually enough (your tenant only).
- Redirect URI: not needed for the client credentials flow, you can leave it empty.
- Name: for example
- Click Register.
Once it has been created, make a note of:
- Application (client) ID → which you will use as
clientIdin the script. - Directory (tenant) ID → which you will use as
tenantId.
3. Create the client secret
To authenticate in app-only mode, the script will need a client secret (or a certificate; here we focus on the secret).
- On the app page, go to Certificates & secrets.
- In the Client secrets section, click New client secret.
- Enter a description (e.g.
AlertBackup-Prod) and choose a suitable expiry. - Click Add.
- Copy the secret value straight away: this is what you will use in the script as
clientSecret.
If you lose the secret value, you will not be able to retrieve it: you will have to create a new one.
4. Add the Microsoft Graph permissions (Mail.Send – Application)
Now you need to grant the app permission to send email through Graph.
- On the app page, go to API permissions.
- Click Add a permission → Microsoft Graph.
- Select Application permissions (not Delegated).
- Search for and add:
Mail.Send(Application).
- Click Add permissions.
- Once it has been added, click Grant admin consent for the tenant.
Without admin consent, the app might still obtain a token, but calls to sendMail would fail for lack of effective authorisation.
5. Restrict the app to the shared mailbox only (Application Access Policy)
So far, the app could potentially send email from any mailbox in the tenant. To reduce the risk, it is good practice to explicitly limit its scope through an Application Access Policy in Exchange Online.
The idea is to:
- create a group that contains only the mailboxes the app is authorised to work with (for example just
alert@azienda.it); - bind the app to that group with a
RestrictAccesspolicy.
5.1 Create the scope group
You can use a mail-enabled security group or a dedicated Microsoft 365 group, for example:
- Name:
Graph-Alert-Mailboxes - Alias:
Graph-Alert-Mailboxes@azienda.it
Add the shared mailbox alert@azienda.it as a member.
5.2 Create the Application Access Policy
From a PowerShell session with the Exchange Online module:
Install-Module ExchangeOnlineManagement -Scope AllUsers
Import-Module ExchangeOnlineManagement
Connect-ExchangeOnline -UserPrincipalName admin@azienda.it
# Application (client) ID dell’app registrata in Entra ID
$appId = "INSERISCI-CLIENT-ID-APP"
# Gruppo che contiene le mailbox autorizzate (mail-enabled security group)
$policyScopeGroup = "Graph-Alert-Mailboxes@azienda.it"
New-ApplicationAccessPolicy `
-AppId $appId `
-PolicyScopeGroupId $policyScopeGroup `
-AccessRight RestrictAccess `
-Description "Consente all'app di usare solo le mailbox del gruppo Graph-Alert-Mailboxes"
# Per verificare il comportamento corretto:
Test-ApplicationAccessPolicy -AppId $appId -Identity alert@azienda.it # atteso: Granted
Test-ApplicationAccessPolicy -AppId $appId -Identity ceo@azienda.it # atteso: Denied
6. Gather the parameters for the PowerShell script
At this point you have all the values needed for the script that will send the email via Graph:
clientId→ the app’s Application (client) ID.tenantId→ the M365 tenant’s Directory (tenant) ID.clientSecret→ the client secret created earlier.MailSender→ the shared mailbox address, for examplealert@azienda.it.- Token URL →
https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token - Scope →
https://graph.microsoft.com/.default(to use the Application permissions configured on the app). - Send mail endpoint →
https://graph.microsoft.com/v1.0/users/{MailSender}/sendMail
Example PowerShell script
<#
********************************************************************************************************************
INFO
Author : Bianchi Davide
UserMail : *****@dbianchi.it
Company :
Current Version : 1.2
Created : 08/06/2023
Last Updated : 13/06/2023
********************************************************************************************************************
DESCRIPTION
This script connects to Microsoft Graph and sends an email using a Shared Mailbox. In this specific case,
it simulates sending an email to notify of the successful execution of a backup.
********************************************************************************************************************
#>
#ID for Graph Token Generation
$clientID = "inserire credenziali"
$Clientsecret = "inserire credenziali"
$tenantID = "inserire credenziali"
#SharedMailBox (from)
$MailSender = "inserire indirizzo della shared"
#Connect to GRAPH API
$tokenBody = @{
Grant_Type = "client_credentials"
Scope = "https://graph.microsoft.com/.default"
Client_Id = $clientId
Client_Secret = $clientSecret
}
$tokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$tenantID/oauth2/v2.0/token" -Method POST -Body $tokenBody
$headers = @{
"Authorization" = "Bearer $($tokenResponse.access_token)"
"Content-type" = "application/json"
}
#Send Mail
$URLsend = "https://graph.microsoft.com/v1.0/users/$MailSender/sendMail"
$BodyJsonsend = @"
{
"message": {
"subject": "Stato esecuzione Backup",
"body": {
"contentType": "HTML",
"content": "Backup avvenuto con successo, <br>
Buona Giornata <br>
"
},
"toRecipients": [
{
"emailAddress": {
"address": "indirizzo mail destinatario 1"
}
},
{
"emailAddress": {
"address": "indirizzo mail destinatario 2"
}
},
{
"emailAddress": {
"address": "indirizzo mail destinatario 3"
}
}
]
},
"saveToSentItems": "false"
}
"@
Invoke-RestMethod -Method POST -Uri $URLsend -Headers $headers -Body $BodyJsonsend
Once the script has been scheduled and run, the email will arrive at the specified addresses:

Critical points and good practice
Managing the secret
Avoid leaving the clientSecret in plain text in script files. Where possible, consider using a certificate or a secret stored in Key Vault, in a password manager or in a file protected at system level.
Permission scope
Application-level Mail.Send is a powerful permission: if you forget to configure the Application Access Policy correctly, the app can work with far more mailboxes than intended. It is worth checking with Test-ApplicationAccessPolicy and with targeted tests before going into production.
Logging and traceability
Even though the mailbox is a technical one, keep logs of the calls and, if needed, enable retention of sent items (saveToSentItems), at least in test environments or for the first production releases.
Conclusion
Configuring email sending from a shared mailbox through Microsoft Graph in app-only mode lets you automate notifications and alerts in a secure, modern way, without depending on user accounts or legacy protocols.
With a proper App Registration, well-defined Graph permissions and a restrictive Application Access Policy, you can build a reliable, governed notification channel in line with Microsoft 365 security best practice. You can then plug in the full PowerShell script that uses these parameters to send emails at the end of your backup or monitoring jobs.