Sending Email Using Microsoft Graph
The legacy command "send-mailmessage" has been deprecated in PowerShell 7. The command does not support the modern authentication methods that are now required by Microsoft Office 365.
The new recommended method for sending email is to use the Microsoft Graph API. The Microsoft Graph API is a RESTful web API that allows you to interact with Exchange Online services. This includes sending email, managing calendars, and changing settings such as rules and categories.
These script examples will allow you to send an email using the Microsoft Graph API. The first example uses the Invoke-RestMethod command to send an email. The second example uses the Microsoft.Graph Powershell module to send an email. Both examples require an Azure Application Registration and mail.send api permission.
The code examples are heavily commented, use these comments to understand the code and modify it to fit your needs. Backticks are used to break lines for readability. These backticks are not required in your code, but they can be used to make long lines easier to read.
Sending Email Using Invoke-RestMethod
Using Invoke-RestMethod, you can manually send a POST request to the Microsoft Graph API endpoint to send an email. This method has no dependencies on any modules or other software besides Powershell itself.
Step 1: to send an email using the Microsoft Graph API, you must authenticate with an Azure Application Registration and get a token.
Save your Application information to use in the token request:
# Client Secret is generated in the Certificates and Secrets section of the Azure AD application.
$ClientSecret= "<insert App API secret>"
# AppID is the Application ID of the Azure AD application.
$AppID = "<insert app id>"
# TenantID is the Tenant ID of the Azure AD application.
$tenantID = "<insert Tenant id>"
The Application ID and TenantID are found on the Overview page of the Azure AD application.
Step 2: As mentioned in "How to use Invoke-Webrequest to access Graph API", the next step in the process is to get a token from the Microsoft Graph API.
# Web requests have a body that is sent to the server.
# This body will ask for a token from the Microsoft Graph API.
$tokenBody = @{
# The grant type is the type of token that is being requested.
# In this case, it is a appid+apikey token, called client_credentials.
Grant_Type = "client_credentials"
# The scope is the permission that is being requested.
# In this case, it is the default permission for the application.
# If the application has many permissions, you can specify just those instead.
Scope = "https://graph.microsoft.com/.default"
# Scope = "https://graph.microsoft.com/mail.send" would be an example of a specific permission for sending emails.
# The client ID and Secret are the credentials saved in the first step.
Client_Id = $AppID
Client_Secret = $ClientSecret
}
# Now the request body is sent to the Microsoft OAuth token endpoint.
# The response is saved in the $tokenResponse variable.
$tokenResponse = Invoke-RestMethod `
-Method POST `
-Body $tokenBody `
-Uri "https://login.microsoftonline.com/$tenantID/oauth2/v2.0/token"
# With the token response saved, the token can be used in the headers of api requests.
$headers = @{
# Authorization is the type of authentication being used.
# In this case, it is a bearer, or token, authentication (I come bearing tokens)
"Authorization" = "Bearer $($tokenResponse.access_token)" # The token saved to the $tokenResponse variable is used here.
# The content type is the type of data that is being sent to the server. In this case, it is JSON.
"Content-type" = "application/json" # Most REST APIs use JSON as the data type.
}
You are now ready to send an email! Just kidding, you have to build it first. 🙃
Step 3: Save your email parameters to variables.
$SendURI = "https://graph.microsoft.com/v1.0/users/$MailFrom/sendMail"
# This is the endpoint for sending the email.
# Notice that the sender is in the URL. This requires the sender to be a valid email address in the tenant.
$MailFrom = "<insert sender address>"
$MailTo = "<insert recipient address>"
$MailSubject = "<insert email subject>" # The subject of the email.
$MailBody = "<insert message body>" # The body of the email. This can be plain text or HTML. (<br> is an HTML line break)
$ContentType = "HTML" # The content type of the email. This can be plain text or HTML.
$SaveToSentItems = "true" # An upside to using the Graph API is that you can save the email to the sent items folder.
Step 4: Create the email object in JSON format.
$JSONBody = @"
{
"message": {
"subject": "$MailSubject",
"body": {
"contentType": "$ContentType",
"content": "$MailBody"
},
"toRecipients": [
{
"emailAddress": {
"address": "$MailTo"
}
}
]
},
"saveToSentItems": "$SaveToSentItems"
}
"@
# Always end the JSON with a closing double quote and an @ symbol.
# This must be the first two and only two characters on a new line.
Step 5: Send the email using the Microsoft Graph API.
Now that the email object is created, it can be sent to the Microsoft Graph API as the body of a POST message.
Invoke-RestMethod -Method POST -Uri $SendURI -Headers $headers -Body $JSONBody
Sending email using the Microsoft.Graph Powershell Module
The Send-MGUserMail command is a wrapper that simplifies the process of sending an email using the Microsoft Graph API. This makes authentication using certificates and sending an attachment much easier.
Step 1: Import the Microsoft.Graph module and save Graph Application variables
# If the Microsoft.Graph module is not installed, it can be installed from the PowerShell Gallery.
Install-Module Microsoft.Graph
Import-Module Microsoft.Graph
# AppID is the Application ID of the Azure AD application.
$AppID = "<insert app id>"
# TenantID is the Tenant ID of the Azure AD application.
$TenantID = "<insert Tenant id>"
Step 2: Certificate authentication is easily supported in the Graph module. We will connect using certificates instead of secrets.
If you have not already created a certificate, you can do so using the following command:
# Create a certificate, this will be saved to the Cert:\CurrentUser\My PS drive. (CD Cert:\CurrentUser\My to see it)
$CertificateName = "<insert certificate name>"
$Certificate = New-SelfSignedCertificate `
-Subject "CN=$CertificateName" `
-CertStoreLocation "Cert:\CurrentUser\My" `
-KeyExportPolicy Exportable `
-KeySpec Signature `
-KeyLength 2048 `
-KeyAlgorithm RSA `
-HashAlgorithm SHA256
# Export the certificate public key to a file. This file will be uploaded to the Azure AD application.
Export-Certificate -Cert $Certificate -FilePath "<insert file path>"
$CertificateThumbprint = $certificate.Thumbprint
Upload the certificate to the Azure AD App Registration, found in the Certificates and Secrets section. Save the thumbprint of the certificate for later. If you do not know the thumbprint of the certificate, you can list all certificates in the certificates folder with the thumbprint using "Get-ChildItem Cert:\CurrentUser\My".
Exporting the certificate to a PFX and installing that certificate on multiple computers is supported. More information can be found here.
Step 3: Connect to the Microsoft Graph API using the certificate and the Azure AD App Registration.
# The TenantID, ClientID, and CertificateThumbprint were saved in the previous steps.
Connect-MgGraph -TenantId $tenantID -ClientId $AppID -CertificateThumbprint $Certificatethumbprint
Step 4: Save the email parameters to variables.
$MailFrom = "<insert sender address>"
# The sender of the email. This must be a valid email address in the tenant.
$MailTo = "<insert recipient address>"
# The recipient of the email.
$MailSubject = "<insert email subject>"
# The subject of the email.
$MailBody = "<insert body of email>"
# The body of the email. This can be plain text or HTML. (<br> is an HTML line break)
$Attachment = "<insert attachment file path>"
# In the Graph module, attachments are base64 encoded. This is the path to the attachment file.
$AttachmentContentBytes = [System.IO.File]::ReadAllBytes($Attachment)
# Now the attachment file is read into memory.
$AttachmentBase64 = [System.Convert]::ToBase64String($AttachmentContentBytes)
# Now the attachment is converted to base64.
$ContentType = "HTML"
# The content type of the email. This can be plain text or HTML.
Step 5: Create the email object in Powershell object format.
Instead of JSON, the Graph Powershell module uses a Powershell custom object to create the email body.
# Create the message HashTable. "@{}" is a HashTable in Powershell.
$Message = @{
Subject = $MailSubject
Body = @{
ContentType = $ContentType
Content = $msgBody
}
ToRecipients = @(
@{
EmailAddress = @{
Address = $MailTo
}
}
)
Attachments = @(
@{
# Odate is a standard data access protocol for REST APIs.
# The type used is a specific Microsoft Graph type for attachments.
"@odata.type" = "# microsoft.graph.fileAttachment"
# The name of the attachment is the file name by itself, do not include path.
# In this example, the file name is extracted from the $Attachment path.
Name = (Split-Path $Attachment -Leaf)
# The content type is the type of data that is being sent.
# In this case, it is an octet stream, or binary data.
ContentType = "application/octet-stream"
# The content bytes are the base64 encoded attachment.
ContentBytes = $AttachmentBase64
}
)
}
Step 6: Send the email using the Send-MgUserMail command.
Now with the email object created, the email can be sent using the Send-MgUserMail command.
(If this command gives a “not found” or “not authorized” error, check the API permissions to the Application Registration. This command requires ‘Mail.Send' application permissions, not delegated.)
Send-MgUserMail -UserId $MailFrom -Message $Message
Conclusion
The Microsoft Graph API is a powerful tool for interacting with Exchange Online services. Sending mail is a common task in many scripts, and the Graph API gives an alternative to the deprecated send-mailmessage command.
Using the Invoke-RestMethod command, you can send an email without any dependencies other than Powershell itself. Use this in scripts that will be used in many environments that may not have the Graph module installed. Note that sending attachments and using certificate authentication is more difficult with this method.
Using the Microsoft.Graph module simplifies certificate authentication and sending attachments. This is the recommended method advised by Microsoft. The Microsoft Graph module is a supported SDK from Microsoft and is updated regularly. This method is recommended for scripts that will be used in a controlled environment where the Graph module can be installed.

