Blog
IT Automation 7 min read

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.

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.Send under 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:

  1. Shared mailbox: the “technical” mailbox the notifications will be sent from (e.g. alert@azienda.it).
  2. App Registration in Entra ID: the application identity the script will use to authenticate.
  3. Microsoft Graph permissions (Mail.Send – Application): these allow the app to send email.
  4. 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_credentials grant type;
  • calls the https://graph.microsoft.com/v1.0/users/{sharedMailbox}/sendMail endpoint;
  • 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:

  1. Sign in to the Exchange admin center.
  2. Go to Recipients → Shared.
  3. Click Add a shared mailbox.
  4. Fill in:
    • Name: e.g. Alert Backup.
    • Email: e.g. alert@azienda.it.
  5. Complete the mailbox creation.

This will be the identity Graph sends the emails “from” in your script:

Shared


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.

  1. Go to the Entra ID portal → App registrations.
  2. Click New registration.
  3. 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.
  4. Click Register.

Once it has been created, make a note of:

  • Application (client) ID → which you will use as clientId in 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).

  1. On the app page, go to Certificates & secrets.
  2. In the Client secrets section, click New client secret.
  3. Enter a description (e.g. AlertBackup-Prod) and choose a suitable expiry.
  4. Click Add.
  5. 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.

  1. On the app page, go to API permissions.
  2. Click Add a permission → Microsoft Graph.
  3. Select Application permissions (not Delegated).
  4. Search for and add:
    • Mail.Send (Application).
  5. Click Add permissions.
  6. 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 RestrictAccess policy.

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:

PowerShell
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 example alert@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

PowerShell
<# 
********************************************************************************************************************
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: mail

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.