Send automated mail messages with PowerShell and Microsoft Graph

Use this method to securely automate the sending of mail messages from your PowerShell scripts, by authenticating to MS Graph with a certificate (instead of username and password) through a custom Entra app.

Create a mail-enabled security group Link to heading

This will limit which mailbox(es) can send automated mail messages from the Entra app. It’s technically not required, but recommended for security and privacy reasons.

The group will only be used to control which mailboxes will be allowed to send mail through the Entra app. The group’s mail address will not be used for anything.

Open Microsoft Exchange admin center, go to Recipients > Groups and create a new mail-enabled security group.

Give the group a reasonable name. An example could be MsGraph-SendMail with the mail address MsGraph-SendMail@mydomain.com.

Add the mailbox(es) that should be able to send automated mail messages.

Configure settings of your choice for the group, for example:

  • Hide the group from the global address book.
  • Allow only messages within the organization (if you do not wish to be able to send externally).
  • Require the owner to accept new members (to prevent unwanted mailboxes from being added to the group).

Create a new application in Entra Link to heading

Register the application Link to heading

Open Microsoft Entra admin center, go to Entra ID > App registrations and choose New registration.

Give your application a name. An example name could be MsGraph-SendMail.

Select Accounts in this organization directory only as the account type. Skip the redirect URI, as it won’t be needed.

Configure permissions Link to heading

Go to Entra ID > App registrations > All applications and open the new application.

Open the API permissions menu for the application and choose Add a permission. Add the following permission:

  • Microsoft Graph > Application permissions > Mail > Mail.Send

Choose Grant admin consent for My Tenant Name to be able to use the permission you added.

Don’t worry, no one will be able authenticate to the application unless they have a valid application certificate installed on their computer.

Add an application access policy (and connect the mail-enabled security group to it) Link to heading

The following PowerShell commands require the ExchangeOnlineManagement module.

Install-Module ExchangeOnlineManagement

Open your PowerShell terminal and connect to Exchange Online with your admin account.

Connect-ExchangeOnline -UserPrincipalName 'myuser@mydomain.com'

Create a new application policy for the Entra application.

Replace the AppId value with the app ID found in the application’s overview and the PolicyScopeGroupId value with the ID of the mail-enabled security group. Make sure you include a reasonable description of the policy.

New-ApplicationAccessPolicy -AppId 'InsertAppIdHere' -PolicyScopeGroupId 'MsGraph-SendMail@mydomain.com' -AccessRight RestrictAccess -Description 'Restrict this app to members of the mail-enabled security group MsGraph-SendMail.'

Check if the application policy is applied to a specific mailbox.

Test-ApplicationAccessPolicy -AppId 'InsertAppIdHere' -Identity 'MyMailServiceAccount@mydomain.com'

Generate an application certificate Link to heading

This will create a new certificate on your Windows machine.

Open your PowerShell terminal to create a certificate for the application.

Replace the DnsName value with your domain name.

$cert = New-SelfSignedCertificate -DnsName 'mydomain.com' -CertStoreLocation 'cert:\CurrentUser\My'

Export the certificate to be able to upload it to the Entra application.

$cert | Export-Certificate -FilePath "$($env:USERPROFILE)\Downloads\MyMailAutomationCertificate.cer"

Open Microsoft Entra admin center, go to Entra ID > App registrations and open your application.

Open the Certificates & secrets menu and upload your newly created certificate. It’s recommended to enter a description so you and other admins know who is using this certificate.

You may upload any number of certificates so you can authenticate to the application from any number of machines (if multiple users/machines need to automate mail-sending).

Automate mail-sending with PowerShell Link to heading

Connect to MS Graph.

Import-Module -Name Microsoft.Graph.Users.Actions

# Connect to Microsoft Graph.
$params = @{
    TenantId = 'InsertTenantIdHere' # Your tenant ID can be found in the Microsoft Entra overview.
    ClientId = 'InsertAppIdHere' # Your application ID can be found in the Entra app's overview.
    CertificateThumbrint = 'InsertCertThumbprintHere' # Your certificate thumbprint can be found in the certificate list of your Entra app.
    NoWelcome = $true
}
Connect-MgGraph @params

Send mail without attachments.

# Prepare message properties.
$senderMailAddress = 'MyMailServiceAccount@mydomain.com'
$recipientMailAddress = 'recipient@mydomain.com'
$saveToSentItems = 'false'
$subject = 'Test mail'
$bodyContent = @'
<h1>Test mail</h1>
This is a test mail.
'@

# Send the message.
$messageProperties = @{
    Message = @{
        Subject = $subject
        Body = @{
            ContentType = 'HTML'
            Content = $bodyContent
        }
        ToRecipients = @(
            @{ EmailAddress = @{ Address = $recipientMailAddress } }
        )
    }
    SaveToSentItems = $saveToSentItems
}
Send-MgUserMail -UserId $senderMailAddress -BodyParameter $messageProperties

Send mail with an attachment.

# Prepare message properties.
$senderMailAddress = 'MyMailServiceAccount@mydomain.com'
$recipientMailAddress = 'recipient@mydomain.com'
$saveToSentItems = 'false'
$subject = 'Test mail'
$bodyContent = @'
<h1>Test mail</h1>
This is a test mail.
'@

# Prepare the attachment file.
$attachmentPath = 'C:\MyAttachmentFile.pdf'
$attachmentContentByteStream = Get-Content -Path $attachmentPath -AsByteStream
$encodedAttachmentContent = [Convert]::ToBase64String($attachmentContentByteStream)

# Send the message.
$messageProperties = @{
    Message = @{
        Subject = $subject
        Body = @{
            ContentType = 'HTML'
            Content = $bodyContent
        }
        ToRecipients = @(
            @{ EmailAddress = @{ Address = $recipientMailAddress } }
        )
        Attachments = @(
            @{
                "@odata.type" = "#microsoft.graph.fileAttachment"
                Name = (Split-Path -Path $attachmentPath -Leaf)
                ContentBytes = $encodedAttachmentContent
            }
        )
    }
    SaveToSentItems = $saveToSentItems
}
Send-MgUserMail -UserId $senderMailAddress -BodyParameter $messageProperties