
Introduction:
In this tutorial, we’ll walk through the process of creating a script to send emails using the Microsoft Graph API. We’ll cover all the necessary steps to set up the script and utilize the Graph API effectively. The methods we’ll use for creating the email body can also be applied to other Graph API requests, offering versatility and flexibility.
Step 1 : Define Parameters
To define the parameters for our script, we’ll refer to the Microsoft Documentation, where we can find all the necessary information. This documentation provides comprehensive details on the parameters available for interacting with the Microsoft Graph API.
https://learn.microsoft.com/en-us/graph/api/resources/message?view=graph-rest-1.0
We will use the following properties:
- MailSender (used in the Request URL)
- toRecipients
- ccRecipients
- bccRecipients
- subject
- body
- flag
- importance
- attachments
Based on these properties, we will now construct our input parameters. These parameters will allow us to specify various aspects of the email, such as recipients, sender, subject, HTML body, importance, and flag status. Additionally, you can provide attachment paths to send attachments if needed.
param (
[Parameter(Mandatory = $true)]
[string[]]$Recipients,
[Parameter(Mandatory = $true)]
[string]$MailSender,
[Parameter(Mandatory = $false)]
[string[]]$CCRecipients = $null,
[Parameter(Mandatory = $false)]
[string[]]$BCCRecipients = $null,
[Parameter(Mandatory = $true)]
[string]$Subject = "Test Email from Graph Mail",
[Parameter(Mandatory = $true)]
[string]$HtmlBody,
[Parameter(Mandatory = $false)]
[string[]]$AttachmentPath = @(),
[Parameter(Mandatory = $false)]
[ValidateSet("low", "normal", "high")]
[string]$Importance = "normal",
[Parameter(Mandatory = $false)]
[ValidateSet("notFlagged", "flagged", "complete")]
[string]$FlagStatus = "notFlagged"
)
Step 2: Setting Up Authentication:
To initiate the process, we’ll establish authentication to acquire an access token for the Microsoft Graph API. This token will grant us the authorization needed to authenticate and execute requests to send emails. We’ll opt for the client credentials grant flow for authentication.
Before proceeding to obtain the access token, it’s imperative to register an App Registration with permissions specifically tailored for sending mails. Detailed instructions for this process can be found in the Microsoft Documentation.
https://learn.microsoft.com/en-us/graph/auth-register-app-v2
After setting up the App Registration, the next step is to assign the appropriate permission to the app, enabling it to send emails.
In our case, the required permission is Mail.Send. This permission can be found under the “Send Mail” resource in the Microsoft Graph API.
For further details about the Mail.Send resource, you can refer to the Permission References in the Microsoft Graph API documentation.

Additionally, it’s crucial to implement an Application access policy to restrict app access to specific mailboxes. Without this policy in place, the app would have the ability to send emails from all mailboxes, which could lead to security concerns.
Refer to the provided documentation for instructions on configuring an Application access policy for your app to ensure proper access control and security measures are in place.
Configure Application Access Policy for an Azure App
Here is a code snippet for the authentication process:
$TENANTID = "<Tenant ID>"
$CLIENTID = "<Application Client ID>"
$CLIENTSECRET = "<Application Secret>"
try {
$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 -ErrorAction Stop -TimeoutSec 20
}
catch {
Write-Error "Failed to obtain access token. Error details: $_"
return
}
3. Create Request URL & Header
Now we need to determine how our request URL should be formatted. This information can be found in the documentation provided by Microsoft.. Send Mail Resource
POST /users/{id | userPrincipalName}/sendMail
So, to send emails, we’ll utilize a POST request. We’ll need to specify the id or userPrincipalName of the user from whom we want to send the emails. In our script, we’ll use the MailSender parameter to provide the userPrincipalName.
$GraphAPIUrl = "https://graph.microsoft.com/v1.0/users/$($MailSender)/sendMail"
Then, we need to create a header. This header will include our Bearer token for authentication and the content type.
$headers = @{
"Authorization" = "Bearer $($tokenResponse.access_token)"
"Content-type" = "application/json"
}
4. Constructing the Email Body
Now, we need to assemble all the components and create the email body. We’ll use a PowerShell object for this purpose because it allows for easy addition of properties. First, we’ll create our main body construct.
$messageBody = @{
message = @{
subject = $Subject
body = @{
contentType = "HTML"
content = $HtmlBody
}
flag = @{ flagStatus = $FlagStatus }
importance = $Importance
}
}
Now, we need to add additional properties to our object: the recipients, CC recipients, and BCC recipients. Since we can have multiple recipients or CC recipients, we need to make this dynamically adjustable.
We will add the toRecipients, ccRecipients, and bccRecipients properties to our messageBody. These properties will contain arrays of recipients, CC recipients, and BCC recipients, respectively.
foreach ($Recipient in $Recipients) {
$messageBody.message.toRecipients += @(@{ emailAddress = @{ address = $Recipient } })
}
if ($CCRecipients) {
foreach ($CCRecipient in $CCRecipients) {
$messageBody.message.ccRecipients += @(@{ emailAddress = @{ address = $CCRecipient } })
}
}
if ($BCCRecipients) {
foreach ($BCCRecipient in $BCCRecipients) {
$messageBody.message.bccRecipients += @(@{ emailAddress = @{ address = $BCCRecipient } })
}
}
The next step is to include our attachments in the messageBody. Handling attachments can be a bit complex since we may have multiple attachments. Therefore, we need to ensure that we add each attachment to the attachments property of the messageBody.
We will convert each attachment to bytes and add it to the attachments property. Finally, we will add our list of attachments to the messageBody.
$attachmentList = @()
if ($AttachmentPath) {
foreach ($path in $AttachmentPath) {
$FileName = (Get-Item -Path $path).Name
$base64string = [Convert]::ToBase64String([IO.File]::ReadAllBytes($path))
$attachment = @{
"@odata.type" = "#microsoft.graph.fileAttachment"
Name = $FileName
ContentType = "text/plain"
ContentBytes = $base64string
}
$attachmentList += $attachment
}
$messageBody.message.attachments = $attachmentList
}
Now, we want to verify if our messageBody is correctly constructed. To achieve this, we’ll convert it into JSON format. This will allow us to inspect the structure and content of our messageBody object.
$messageBody | ConvertTo-Json -Depth 2
The result should resemble a code snippet like the following, with variations depending on the number of attachments and recipients specified:
{
"message": {
"flag": {
"flagStatus": "notflagged"
},
"toRecipients": [
"System.Collections.Hashtable",
"System.Collections.Hashtable"
],
"attachments": [
"System.Collections.Hashtable",
"System.Collections.Hashtable"
],
"subject": "Test Email Sent by Graph API",
"bccRecipients": [
"System.Collections.Hashtable"
],
"body": {
"content": "Email body",
"contentType": "HTML"
},
"importance": "normal",
"ccRecipients": [
"System.Collections.Hashtable"
]
}
}
At the ConvertTo-Json cmdlet, we’ve used a depth of ‘2’ to provide a glimpse of how the messageBody appears. To gain a comprehensive view, we can increase the depth to ‘4’. This will reveal the attachments as base64 strings and display the email addresses of the recipients.
Below is a snippet without the attachments and with a depth of 4:
{
"message": {
"flag": {
"flagStatus": "notflagged"
},
"toRecipients": [
{
"emailAddress": {
"address": "recipient@example.com"
}
},
{
"emailAddress": {
"address": "recipient2@example.com"
}
}
],
"subject": "Test Email Sent by Graph API",
"bccRecipients": [
{
"emailAddress": {
"address": "bccrecipient@example.com"
}
}
],
"body": {
"content": "Email body",
"contentType": "HTML"
},
"importance": "normal",
"ccRecipients": [
{
"emailAddress": {
"address": "ccrecipient@example.com"
}
}
]
}
}
5. Sending the Email
Now that we have all the components assembled, we can proceed to construct our Invoke request:
- We’ll use the POST method.
- The URI needs to be specified.
- The header must be defined.
- We’ll convert the body into JSON format with a depth of 4.
Let’s put it all together to build our Invoke request.
Invoke-RestMethod -Method POST -Uri $GraphAPIUrl -Headers $headers -Body ($messageBody | ConvertTo-Json -Depth 4) -ErrorAction Stop -TimeoutSec 30
The Result looks like this :

Conclusion
By following these steps, you’ll be equipped to create a robust script for sending emails via the Microsoft Graph API. This script can be tailored to meet specific requirements or seamlessly integrated into automation pipelines for streamlined email communication. Furthermore, the techniques learned here can be extended to other Graph API operations, broadening the scope of possibilities for leveraging Microsoft Graph.
You can find the final script, including error handling, below. While the script may not be perfect, it effectively accomplishes its intended purpose. If you have any recommendations for improving the script, please feel free to share them.
https://github.com/uzejnovicahmed/GraphAPI/tree/c60298d57e0359232019bf69e7460048841e5681/SendMail
HAPPY AUTOMATING !