If you have some form of PowerShell automation in your infrastructure, like mailbox or SharePoint site monitoring, you probably send emails via PowerShell. If you’re still using the Send-MailMessage cmdlet to do authenticated SMTP relay with Office 365, sad to say, the future looks bleak.
Although The Exchange Team said in their May 2022 update that “We are not turning off SMTP AUTH”, the fact remains that using basic authentication is unsecured. Moving forward, you must consider switching to a more secure email, like Microsoft Graph API.
Honestly, switching to Microsoft Graph API to send mail can be daunting at first because you’re technically leaving behind the usual SMTP and moving to REST API calls via HTTPS with JSON. This article will help you get started on that journey.
Requirements
To send mail using Graph API, you must have access to the following.
- A Microsoft 365 Enterprise tenant with Exchange Online.
- Global administrator access to the tenant.
- An Exchange Online mailbox to use as the email sender ID. A Shared Mailbox without a license should suffice.
- The Microsoft Graph PowerShell SDK must be installed on your computer.
- Your computer must have Windows PowerShell 5.1 or PowerShell 7+.
Register the Mailer App in Azure AD
Using the Graph API send mail function, you must first create an application in Azure AD, whose identity you’ll use to authenticate and authorize the send email action.
The application will have the following components.
- Application ID (aka. Client ID)
- Client Certificate
- Microsoft Graph API Permission (Mail.Send)
Note. You’ll only need to register the application once, which will be reusable.
Generate a Self-Signed Certificate
Authenticating your app in Azure AD requires a certificate. This certificate can be from a known certificate provider, such as Digicert and Verisign, or self-generated.
You do not have to pay for the SSL certificate just for this purpose, so in this example, you’ll generate a self-signed certificate instead.
First, open PowerShell as an administrator and run the following code. This code generates and installs a self-signed certificate in the Cert:\CurrentUser\My certificate store with a validity of five years.
# Generate a self-signed certificate $certSplat = @{ Subject = 'PSGraphMailApp' NotBefore = ((Get-Date).AddDays(-1)) NotAfter = ((Get-Date).AddYears(5)) CertStoreLocation = "Cert:\CurrentUser\My" Provider = "Microsoft Enhanced RSA and AES Cryptographic Provider" HashAlgorithm = "SHA256" } $selfSignedCertificate = New-SelfSignedCertificate @certSplat
Confirm that the certificate was created by displaying its properties, as shown below.
# Display the certificate details $selfSignedCertificate | Format-List PSParentPath, ThumbPrint, Subject, NotAfter
Don’t forget to copy the Thumbprint value because you’ll need this during the authentication process.
Finally, export the certificate without its public key to C:\Cert\PSGraphMailApp.cer. You will upload this certificate file to Azure AD later.
# Create the directory to save the certificate mkdir C:\cert -Force # Export the public certificate (no private key) Export-Certificate -Cert $selfSignedCertificate -FilePath 'C:\Cert\PSGraphMailApp.cer'
Create a New App
Follow these steps to register the app.
- Log in to the Azure Active Directory Portal.
- Open the App registrations blade.
- Click New Registration.
On the Register an application page:
- Enter PSGraphMailApp as the name of the application.
- Choose Accounts in this organizational directory only.
- Click Register.
- Once the new application is registered, copy the values of Application (client) ID and Directory (tenant) ID. You’ll need these values and the certificate thumbprint you copied earlier for authentication.
Upload the Certificate
Time to add the certificate credential to the app. Remember that the certificate you are uploading is the same one you created earlier.
- Click Certificates & secrets → Go to Certificates → Click on Upload certificate.
On the Upload certificate fly-out page that appears:
- Click the browse button → Locate the certificate file you exported earlier → Click Open to upload the certificate.
- Click Add to finish adding the certificate credential.
- You should now see the certificate in the list.
Note. The Thumbprint value should be the same one you copied earlier when you created the self-signed certificate.
Add Permissions and Grant Consent
Now you need to add the Microsoft Graph API to send mail permission to the app.
- Click API permissions → Add a permission.
- Next, click Microsoft Graph under the Microsoft APIs tab.
- Select Application Permissions → Search for and check the Mail.Send permission → Click Add permissions.
- Once the permission is added, click Grant admin consent for → Yes.
Connect to Microsoft Graph
Before issuing commands to Microsoft Graph, you must authenticate using your application and certificate credentials.
Run the Connect-MgGraph cmdlet to connect to Microsoft Graph. replace the -ClientId, -TenantID, and -CertificateThumbprint values with your own.
# Authenticate and connect to Microsoft Graph Connect-MgGraph ` -ClientId 'f39202b0-b720-4e88-b1e4-690c92d35705' ` -TenantId 'd397a279-89ca-42f6-9c8b-d481982c652f' ` -CertificateThumbprint '243B29B4DEAB5758D2C2ACB96A0C3A3211BE2784'
That’s it! You’ve authentications with Microsoft Graph in one command. But for good measure, run the below command to inspect your application’s permissions.
Get-MgContext
As you can see below, the current authorization only has Mail.Send permission, which is what we added to the application.
Send Mail Using Graph API PowerShell
You now have the necessary items to send emails via Microsoft Graph API. At this point, you should have the Client ID, Tenant ID, and Certificate Thumbprint that you’ll use to authenticate with the Azure AD application.
Instead of manually constructing the REST API calls to authenticate and send emails, the following steps will use the cmdlets included in the Microsoft Graph PowerShell SDK, specifically the Send-MgUserMail.
Compose and Send a Plain Text Email
The minimum details that you need to supply in a message when sending an email are the Subject, Body, and one To recipient. You must compose this payload in a nested hashtable format; below is an example.
$mailParams = @{ Message = @{ Subject = "Server Disk Space on COMPUTER_A is below the 30% Threshold." Body = @{ ContentType = "Text" Content = "The remaining available diskspace on [C:] is now at 5GB. Please do something." } ToRecipients = @( @{ EmailAddress = @{ Address = "itops@lzex.ga" } } ) } }
To send this message, you’ll use the Send-MgUserMail cmdlet. Specify the sender address to the -UserId parameter and the $mailParams variable to the -BodyParameter parameter.
Send-MgUserMail -UserId 'mailer365@lzex.ga' -BodyParameter $mailParams
No output means all good.
The screenshot below shows that the email was sent to one recipient in the To field, which is expected.
Add Recipients
What if you need to add more recipients? And not just to the To field but to the CC and possibly the BCC fields too?
To add additional To recipients, create another hashtable entry and insert it into the ToRecipients = @() block. For example, the below code is one email address recipient entry.
@{ EmailAddress = @{ Address = "monitoring@lzex.ga" } }
How about CC and BCC? Insert the CcRecipients and BccRecipients entries in one level inside the Message block.
CcRecipients = @( @{ EmailAddress = @{ Address = "june@lzex.ga" } } ) BccRecipients = @( @{ EmailAddress = @{ Address = "supremeleader@lzex.ga" } } )
The email object code would now look like the image below.
And when you resend the email, you’ll confirm that the new recipients you added also received the email.
Add Attachments
Attaching files to emails is common practice, and Microsoft Graph API lets you attach files to a message. But you are not attaching the files directly. Instead, you must convert the file into a Base64 string. That Base64 string is what you’ll include in the message object.
For example, to convert the file C:\itbro\LoremIpsum.txt into a Base64 string:
# Windows PowerShell 5.1 [convert]::ToBase64String((Get-Content 'C:\itbro\LoremIpsum.txt' -Raw -Encoding Byte)) # PowerShell 6.0+ [convert]::ToBase64String((Get-Content 'C:\itbro\LoremIpsum.txt' -AsByteStream))
First, insert the Attachments=@() block anywhere inside the Message block. You’ll specify the attachment details inside the Attachment block, including the attachment name as it appears in the email and the Base64 content.
The example below inserts the file C:\itbro\LoremIpsum.txt attachment into the message.
$mailParams = @{ Message = @{ Subject = "Server Disk Space on COMPUTER_A is below the 30% Threshold." Body = @{ ContentType = "Text" Content = "The remaining available disk space on [C:] is now at 5GB. Please do something." } ToRecipients = @( @{ EmailAddress = @{ Address = "itops@lzex.ga" } } ) Attachments = @( @{ "@odata.type" = "#microsoft.graph.fileAttachment" "name" = "LoremIpsum.txt" "contentBytes" = $( [convert]::ToBase64String((Get-Content 'C:\itbro\LoremIpsum.txt' -AsByteStream)) ) } ) } }
Now let’s send the email again with the attachment.
Send-MgUserMail -UserId 'mailer365@lzex.ga' -BodyParameter $mailParams
As you can see below, the email was received with a file attachment named LoremIpsum.txt.
Open the attachment to confirm that it is intact.
Compose and Send an HTML Email
You can also send HTML messages via Microsoft Graph API if you want more control over your email’s appearance.
First, compose your HTML email body and save it in the $htmlBody variable.
$htmlBody = @' <html> <body> <H2>Server Disk Space on COMPUTER_A is below the 30% Threshold.</H2> <p><i>The remaining available disk space on <u>[C:] is now at 5GB</u>. Please do something.</i></p> </body> </html> '@
In your email object, change the following Body values.
- ContentType = “text” to ContentType = “html”.
- Content = “The remaining available disk space on [C:] is now at 5GB. Please do something” to Content = $htmlBody.
$mailParams = @{ Message = @{ Subject = "Server Disk Space on COMPUTER_A is below the 30% Threshold." Body = @{ ContentType = "HTML" Content = $htmlBody } ToRecipients = @( @{ EmailAddress = @{ Address = "itops@lzex.ga" } } ) } } Send-MgUserMail -UserId 'mailer365@lzex.ga' -BodyParameter $mailParams
The email body now shows the HTML format message.
You’ve reached the end of this post, and hopefully, you’ve learned enough to help you get started with sending emails using Microsoft Graph API and PowerShell.