Send an email with Attachment using Graph API and Powershell

I recently worked on a task to send an email with an attachment using the Microsoft Graph API. I included the Microsoft Graph API script code at the end of my PowerShell script. The script sends an email to a specified email address with an attachment and also includes a table of contents in the body of the email.

I would be using a service principal in Entra ID to send an email using the Graph API. However, you can modify the script according to your own requirements and use a user account instead of a service principal.

Prerequisites

  • A service principal in Entra ID with mail.send permissions
  • Client ID of the application
  • Client secret
  • Tenant ID

Step 1 – Create a Service Principal in Entra ID with mail.send permission

You will need to create a Service Principal in Entra ID. Please follow the steps below:

  • Login to Microsoft Entra admin center
  • On the left-hand side Expand Applications and click on App Registrations.
  • Click on + New registration.
  • When registering an application, provide a name for the application and select the supported account types. Typically, you may choose “Accounts in this organizational directory only (XXXX only – single tenant)“. Then, click on Register.
  • After registering the application, you can find it under “App registrations“. Click on the application to open it, and then click on “API permissions” on the left-hand side. Next, click on the “+ Add a permission” link.
API Permissions mail.send
Create a Service Principal in Entra ID with mail.send permission
  • Click on “Microsoft Graph” -> “Application Permissions” -> search for “mail.send“. Select “Mail.send” and click on “Add permission“.
Microsoft Graph API permission Azure mail.send
Create a Service Principal in Entra ID with mail.send permission
  • Click on “Grant admin consent for <your org name>” to grant admin consent for this application permission.
Grant admin consent for Azure App registration mail.send
Create a Service Principal in Entra ID with mail.send permission

Step 2 – Copy Client ID, Client Secret, and Tenant ID

We also need the client ID and client secret information for the script. Therefore, you need to click on “Certificates & Secrets” from the application and generate a new client secret. Please follow the steps below to generate a client secret for this application.

  • Login to Microsoft Entra admin center
  • On the left-hand side Expand Applications and click on App Registrations.
  • Find and click the application created in the previous step.
  • Click on Certificates & secrets and then click on + New client secret.

Provide a description and expiry date for the client secret. Copy the value of the client secret to a notepad. This value is visible only for a short period, after which it will be hidden.

Therefore, make sure to copy the value now and keep it secure. If you lose the client secret value, you will need to create a new client secret, which will generate a new value for the client secret.

Generate a client secret in Azure App registration
Copy Client ID, Client Secret, and Tenant ID
  • For client ID and tenant ID information, you will need to click on the Overview tab of the application and copy the Application (client) ID and Directory (tenant) ID information from there.
Copy Cliend ID and Tenant ID from Azure Application
Copy Client ID, Client Secret, and Tenant ID

Step 3 – Powershell Code to Send Email Using Graph API

First, you need to store the client ID, client secret, and tenant ID in variables.

1. Store the Client ID, Client secret, and Tenant ID in variables

$clientid = "30ccc130-1613-4a09-8c6f-78hhsjbjekk1"
$clientsecret = "TWr8Q~yMNFKsobLjPNZ2mKo-egt8U49Hhbjdsk"
$tenantid = "97659d97-8dab-4122-80bd-caddddddd7"

2. Connect to Graph API

Use the code below to connect to the Graph API. Please note that this will require client ID and client secret information, as you have them stored in the $clientid and $clientsecret PowerShell variables.

Connect to Graph API

#Connection to MS 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"
}

3. Email Attachment variable

Next, create a variable called $attachment and provide the path of the attachment you want to send with the email. For example: $attachment = "c:\temp\listofusers.csv". You can modify the location according to where your email attachment is stored.

Email Attachment variable

$Attachment="C:\temp\ListofUsers.csv"

4. Get File name and contents and Store them in variables $FileName and $base64string

Get File name and contents

#Get File Name and Base64 string
$FileName=(Get-Item -Path $Attachment).name
$base64string = [Convert]::ToBase64String([IO.File]::ReadAllBytes($Attachment))

Retrieve the message parameters and convert them to JSON. $htmlobject is a PowerShell object. You can create a PowerShell object and pipe it to the ConvertTo-Html cmdlet, then use that object in the content.

The content of $htmlobject will create the email body. Also, ensure you have a sender email address stored in the $FromAddress PowerShell variable. For example: $FromAddress = "[email protected]"

Get the message parameters and covert it to JSON

$MessageParams = @{
    "URI"         = "https://graph.microsoft.com/v1.0/users/$FromAddress/sendMail"
    "Headers"     = $headers
    "Method"      = "POST"
    "ContentType" = 'application/json'
    "Body" = (@{
          "message" = @{
          "subject" = "Userlist Report"
          "body"    = @{
               "contentType" = 'HTML' 
               "content"     = $htmloutput} 
          "toRecipients" = @(
          @{
          "emailAddress" = @{"address" = "[email protected]" }
        } )
        "attachments" = @(      
              @{      
                "@odata.type" = "#microsoft.graph.fileAttachment"
                "name" = $FileName
                "contentType" = "text/plain"
                "contentBytes" = "$base64string"          
                }        
        )
}
}) | ConvertTo-JSON -Depth 6
}

5. Send the message

The final command is to send an email using the Invoke-RestMethod command.

Invoke-RestMethod

Invoke-RestMethod @Messageparams

6. Complete Script

Please find the complete script below for sending an email using the Graph API and PowerShell.

Complete Script

#Connection to MS 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"
}
$Attachment=".\SPONotCompliantFileList.csv"
#Get File Name and Base64 string
$FileName=(Get-Item -Path $Attachment).name
$base64string = [Convert]::ToBase64String([IO.File]::ReadAllBytes($Attachment))
$MessageParams = @{
    "URI"         = "https://graph.microsoft.com/v1.0/users/$FromAddress/sendMail"
    "Headers"     = $headers
    "Method"      = "POST"
    "ContentType" = 'application/json'
    "Body" = (@{
          "message" = @{
          "subject" = "Userlist Report"
          "body"    = @{
               "contentType" = 'HTML' 
               "content"     = $htmloutput} 
          "toRecipients" = @(
          @{
          "emailAddress" = @{"address" = "[email protected]" }
        } )
        "attachments" = @(      
              @{      
                "@odata.type" = "#microsoft.graph.fileAttachment"
                "name" = $FileName
                "contentType" = "text/plain"
                "contentBytes" = "$base64string"          
                }        
        )
}
}) | ConvertTo-JSON -Depth 6
}
# Send the message
Invoke-RestMethod @Messageparams

Conclusion

In this blog post, we have covered how to send an email using the Microsoft Graph API with a Service principal created in Entra ID. Ensure that the “mail.send” permission is added to the service principal, and have the client ID, client secret, and tenant ID values ready for use in the script. I have tested this code and successfully incorporated it into several of my PowerShell scripts, enabling them to send emails with locally saved attachments.

Leave a Comment