• Skip to main content
  • Skip to secondary menu
  • Skip to primary sidebar
  • Skip to footer
  • Home
  • Disclaimer & Policy

Elan Shudnow's Blog

MVP Logo
  • Azure
  • Exchange
  • Lync

Azure Runbooks Connecting to Exchange Online and Microsoft Graph

July 22, 2022 by Elan Shudnow 9 Comments

The purpose of this article is to demonstrate the following two capabilities:

  • Leverage an Azure Runbook to connect to Exchange Online via an Azure Active Directory Application to run Exchange Online PowerShell Commands
  • Leverage an Azure Runbook to connect to Microsoft Graph via an Azure Active Directory Application to Send Email via Exchange Online

We will walk through the following steps to accomplish the above 2 capabilities:

  1. Create an Automation Account leveraging a Managed Identity
  2. Create an Azure AD Application with the necessary Exchange Online API Permissions
  3. Create a certificate and assign the certificate to the Azure AD Application
  4. Assign Exchange Online Permissions to our Azure AD Application
  5. Configure our Automation Account to Connect to Exchange Online
  6. Configure our Automation Account to Connect to Microsoft Graph to Send an E-Mail

Create an Automation Account leveraging a Managed Identity

Create an Automation Account and specify the Resource Group, Automation Account Name, and Region the Automation Account will be created in.

Create a System assigned Managed Identity. Click Review + Create and create the Automation Account.

Assign Reader Permissions against your Subscription for your Automation Account’s Managed Identity so the Runbook can login to Azure to obtain and execute the Runbook. I am choosing to do this against my Tenant Root Management Group. You can choose to scope this to any Management Group, Subscription, or Resource Group. The scope selected must include the Automation Account.

In the Scope you select, choose Access control (IAM), click Add and choose Add role assignment.

Select Reader.

Click Next. Select Managed Identity.

Click Select members, choose Automation Account for Managed Identity, and select your Automation Account.

Verify the permissions are correct and click Review + assign.

Create an Azure AD Application with the necessary Exchange Online API Permissions

In Azure Active Directory, create an Application.

Assign a name for the application and specify the application will only leverage accounts in your own tenant. Click Register.

In the Application, go to Manifest, and modify the resourceAppID, as well as the id and type within resourceAccess to match the below. Choose Save.

Here is a snippet of the code:

"requiredResourceAccess": [
   {
      "resourceAppId": "00000002-0000-0ff1-ce00-000000000000",
      "resourceAccess": [
         {
            "id": "dc50a0fb-09a3-484d-be87-e023b12c6440",
            "type": "Role"
         }
      ]
   }
],

Verify Exchange.ManageAsApp API permissions are present.

Grant Consent by clicking “Grant admin consent for <TenantName>”

Verify “Granted for Shudnow” is displayed under Status.

Create a certificate and assign the certificate to the Azure AD Application

Create a Self-Signed Certificate on your local machine (or any machine) using the following code and be sure to change the DnsName:

# Create certificate
$mycert = New-SelfSignedCertificate -DnsName "shudnow.io" -CertStoreLocation "cert:\CurrentUser\My" -NotAfter (Get-Date).AddYears(1) -KeySpec KeyExchange

# Export certificate to .pfx file
$mycert | Export-PfxCertificate -FilePath mycert.pfx -Password $(ConvertTo-SecureString -String "P@ssw0rd1234" -AsPlainText -Force)

# Export certificate to .cer file
$mycert | Export-Certificate -FilePath mycert.cer

Let’s take the certificate and attach it to our Azure AD Application. In your Azure AD Application, choose Certificates & secrets, select Certificates, and choose Upload certificate.

Select the .cer file that was exported. Click Add.

Verify the certificate was added.

Assign Exchange Online Permissions to our Azure AD Application

In Azure Active Directory, open Roles and administrators and search for Exchange administrator.

Click Exchange administrator and then Add assignments.

In “Select member(s)”, click No member selected and choose your Azure AD Application.

If you’re like me and you named your Automation Account with a Managed Identity and your Azure AD Application the same name, you may be wondering which is which. In Azure AD, if you go to App Registrations, find your Azure AD Application, you will see the Application (client) ID. Even if you named them differently, you will still need the Application (client) ID for a later step for the Runbook code itself. Please copy the ID for use later.

Choose Select and finish assigning the permission. Verify the assignment has been added.

Configure our Automation Account to Connect to Exchange Online

In our Automation Account, go to Certificates, and upload the PFX of our self-signed certificate.

Verify the certificate has been added.

In your Automation Account, click Modules, and then click Add Module.

Choose “Browse from gallery” and then click “Click here to browse from gallery.”

Search for the ExchangeOnlineManagement Module.

Select the Module and choose your Runtime version and click Import.

Create a Runbook by providing a Name, PowerShell as the Runbook Type, and your Runtime version. Click Create.

We will, for now, use the following Runbook Code.

# Ensures you do not inherit an AzContext in your runbook
Disable-AzContextAutosave -Scope Process

# Connect to Azure with system-assigned managed identity
$AzureContext = (Connect-AzAccount -Identity).context

# set and store context
$AzureContext = Set-AzContext -SubscriptionName $AzureContext.Subscription -DefaultProfile $AzureContext

# Obtain a copy of the certificate
$ExchangeOnlineCertThumbPrint = (Get-AzAutomationCertificate -ResourceGroupName "ExchangeOnlineAA" -AutomationAccountName "ExchangeOnlineAA" -Name "ExchangeOnlineCert").Thumbprint

# Import the ExchangeOnlineManagement Module we imported into the Automation Account
Import-Module ExchangeOnlineManagement

# Connect to Exchange Online using the Certificate Thumbprint of the Certificate imported into the Automation Account
Connect-ExchangeOnline -CertificateThumbPrint $ExchangeOnlineCertThumbPrint -AppID "<AppIDHere>" -Organization "shudnow.onmicrosoft.com"

# Get the Mailbox for Elan Shudnow
$Mailboxes = Get-Mailbox -Identity "Elan Shudnow"

# Output the Results for the Mailbox
Write-Output $Mailboxes

Click on Test pane so that we can test the Runbook.

sdfd

Voila, we have a successful Runbook Execution!

Leverage Microsoft Graph to send an e-mail message.

Now we’ll want to leverage Microsoft Graph to send an e-mail.

The first thing we’ll need to do is import the following three Modules into the Automation Account:

  • Microsoft.Graph.Auth
  • Microsoft.Graph.Mail
  • Microsoft.Graph.Users.Actions

I am not going to show again how to import Modules from the Gallery as I demonstrated this earlier with the ExchangeOnlineManagement Module.

We’ll want to add a couple new Azure AD Application API Permissions. For this, within API Permissions, select Microsoft Graph.

Choose Application Permissions.

Scroll down to Mail and choose Mail.ReadWrite and Mail.Send and click Add.

Click Grant admin consent and verify you permissions look as follows.

The Runbook Code we will initially test with is as follows just to ensure we are successfully connecting to Microsoft Graph via the Azure AD Application.

# Ensures you do not inherit an AzContext in your runbook
Disable-AzContextAutosave -Scope Process

# Connect to Azure with system-assigned managed identity
$AzureContext = (Connect-AzAccount -Identity).context

# set and store context
$AzureContext = Set-AzContext -SubscriptionName $AzureContext.Subscription -DefaultProfile $AzureContext

# Obtain a copy of the certificate
$ExchangeOnlineCertThumbPrint = (Get-AzAutomationCertificate -ResourceGroupName "ExchangeOnlineAA" -AutomationAccountName "ExchangeOnlineAA" -Name "ExchangeOnlineCert").Thumbprint

# Import the Microsoft.Graph.Authentication Module we imported into the Automation Account
Import-Module Microsoft.Graph.Authentication

# Import the Microsoft.Graph.Mail Module we imported into the Automation Account
Import-Module Microsoft.Graph.Mail

# Connect to Microsoft Graph using the Certificate Thumbprint of the Certificate imported into the Automation Account
Connect-MgGraph -ClientId <Client ID> -TenantId <Tenant ID> -CertificateThumbprint $ExchangeOnlineCertThumbPrint
$Details = Get-MgContext

Write-Output $Details 

The Client ID is the Azure AD Application’s ID you recorded earlier. Be sure to update the Client ID and the Tenant ID. To get the Tenant ID, go to Azure Active Directory, click Properties, and record the Tenant ID.

Let’s test the execution of our Runbook to ensure we are able to connect to Microsoft Graph and get our Tenant Name back as the Output.

Voila, we have another successful execution. This time via Microsoft Graph and we can see we have Mail.ReadWrite and Mail.Send.

Now let’s modify our code to actually send an e-mail. Thank you to Mike Crowley for publishing some code to send e-mail via Microsoft Graph. His article is here: Sending Email with Send-MgUserMail (Microsoft Graph PowerShell) | Mike Crowley’s Whiteboard.

Our code is as follows:

# Ensures you do not inherit an AzContext in your runbook
Disable-AzContextAutosave -Scope Process

# Connect to Azure with system-assigned managed identity
$AzureContext = (Connect-AzAccount -Identity).context

# set and store context
$AzureContext = Set-AzContext -SubscriptionName $AzureContext.Subscription -DefaultProfile $AzureContext

# Obtain a copy of the certificate
$ExchangeOnlineCertThumbPrint = (Get-AzAutomationCertificate -ResourceGroupName "ExchangeOnlineAA" -AutomationAccountName "ExchangeOnlineAA" -Name "ExchangeOnlineCert").Thumbprint

# Import the Microsoft.Graph.Authentication Module we imported into the Automation Account
Import-Module Microsoft.Graph.Authentication

# Import the Microsoft.Graph.Mail Module we imported into the Automation Account
Import-Module Microsoft.Graph.Mail

# Connect to Microsoft Graph using the Certificate Thumbprint of the Certificate imported into the Automation Account
Connect-MgGraph -ClientId <Client ID> -TenantId <Tenant ID> -CertificateThumbprint $ExchangeOnlineCertThumbPrint

# E-mail Recipient
$emailRecipients   = @(
	'[email protected]'
)

# E-mail Sender
$emailSender  = '[email protected]'

# E-mail Subject
$emailSubject = 'Sample Email'

# Function to Convert Recipient E-mail into Object needed for Send-MgUserMail
Function ConvertTo-IMicrosoftGraphRecipient {
	[cmdletbinding()]
	Param(
		[array]$SmtpAddresses        
	)
	foreach ($address in $SmtpAddresses) {
		@{
			emailAddress = @{address = $address}
		}    
	}    
}

# Convert Recipient E-mail into Object needed for Send-MgUserMail
[array]$toRecipients = ConvertTo-IMicrosoftGraphRecipient -SmtpAddresses $emailRecipients 

# Customize the message 
$emailbody  = @{
    content = '<html>hello <b>world</b></html>'
    ContentType = 'html'
}

$body += @{subject      = $emailSubject}
$body += @{toRecipients = $toRecipients}    
$body += @{body         = $emailBody}

$bodyParameter += @{'message'         = $body}
$bodyParameter += @{'saveToSentItems' = $false}

# Send E-mail
Send-MgUserMail -UserId $emailSender -BodyParameter $bodyParameter

Let’s execute our Runbook again with these latest code changes.

No errors. This is a good sign. Let’s go check hotmail…

Voila! Everything worked as expected.

Conclusion

In this article, we learned how to leverage an Azure Automation Account for the purpose of executing a Runbook to leverage an Azure Active Directory application to connect to Exchange Online using a certificate. We then looked at how to leverage a Runbook to execute Microsoft Graph to send e-mails via Exchange Online.

If you have any questions, feel free to comment.

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on Reddit (Opens in new window) Reddit

Filed Under: Azure, Exchange Tagged With: Automation, Azure, Exchange, Microsoft Graph, PowerShell, Runbook

Reader Interactions

Comments

  1. Jack says

    July 20, 2023 at 6:27 am

    Why do you need a managed identity in this scenario?

    Reply
    • Elan Shudnow says

      August 1, 2023 at 4:14 pm

      Hi Jack, in the article, it mentions, “Assign Reader Permissions against your Subscription for your Automation Account’s Managed Identity so the Runbook can login to Azure to obtain and execute the Runbook. I am choosing to do this against my Tenant Root Management Group. You can choose to scope this to any Management Group, Subscription, or Resource Group. The scope selected must include the Automation Account.”

      Hope this answers your question.

      Reply
  2. Andrew Gaskell says

    February 28, 2023 at 1:31 pm

    Hi Elan
    I have this working with powershell 5.1 runtime but the connection to exchange keeps resetting. I tried 7.1 and get newtonsoft error, looks like a library conflict as it wants newtonsoft v13.0.0 but can’t find it. Powershell 7.2 runtime gives me a different issue. This time I get: “no certificate found for the given certificatethumbprint” when connecting to exchange.
    Have you seen any of these issues? Any suggestions?

    Thanks
    Andrew

    Reply
    • Elan Shudnow says

      March 1, 2023 at 6:52 am

      Hi Andrew, I’ve heard of others having issues with 7.1. This seems to only work with 5.1. I personally haven’t tried with 7.1 as 7.1 is in Preview for Runbooks. As far as the connection being reset, I’ve had a few people run this on 5.1 without issue, including myself, so I’m not sure what the connection resetting problem is.

      Reply
      • Andrew Gaskell says

        March 1, 2023 at 8:22 am

        Hi Elan
        thanks for the response. I just got this working on 7.2! There is a github thread here: https://github.com/microsoftgraph/msgraph-sdk-powershell/issues/1439 that describes the module conflict issue with Newtonsoft in 7.1 and recommends to use 7.2.

        Now the difference with 7.2 is that the thumprint switch is not working (at least for me) with Connect-ExchangeOnline. This worked OK in 5.1 but not 7.2 With 7.2 I was able to retrieve the thumbprint for the cert, but when I pass the thumprint as an argument to Connect-ExchangeOnline cmdlet, it complains that it cannot find a valid certificate for the thumbprint. I finally figured out that I can retrieve the entire cert rather than thumprint and pass this instead to Connect-ExchangeOnline. This works every time and no resets for me.

        Here is what I did. I hope it helps you or someone else reading this in the future:

        $ExchangeOnlineCert = (Get-AutomationCertificate -Name “MyCertName”)

        Connect-ExchangeOnline -Certificate $ExchangeOnlineCert -AppID “” -Organization “xyz.onmicrosoft.com”

        Notice that you have to use Get-AutomationCertificate to get the cert. Get-AzAutomationCertificate gets information about the cert e.g. the thumbprint but not the cert itself.

        The only doubt I have after all this is: What is the point? Managed Identities were supposed to remove the need to worry about certificates and their expiry. Yes I know I can get a full cert that is not self-signed and will last a lot longer, but it will eventually expire. and the job will not run until it gets a new certificate.

        Reply
  3. Andrew Gaskell says

    February 22, 2023 at 9:41 am

    Hi Elan
    I just worked my way through this and it worked. I initially tried the 7.1 runtime but I got a Newstonsoft error from the exchangeonline module. I figured there must be some dependency issue with the 7.1 runtime, so I switched to 5.1 and this works OK.

    I have a question on the certificate that is attached to the Azure Applicatioin. Does a new certificate have to be created and uploaded every year? It looks like it, which is a shame as I will have to build that requirement into the app maintenance.

    Thanks for sharing.
    – Andrew

    Reply
    • Elan Shudnow says

      February 22, 2023 at 10:07 am

      Yes, the certificate does need to be renewed and updated in the Azure AD App and the Automation Account.

      Reply
  4. Huy Than says

    January 21, 2023 at 3:24 am

    Thank you so much for this tutorial. This automation save me 10 minutes everyday in my job. A year would be a lot!

    There is only 1 thing that I was confused is the way you name the same “ExchangeOnlineAA” for ResourceGroup, App and Automation Account. It took me 4 hours and I gave up then came back and finally understood that the ResourceGroup in my environment has different name. I know this is so obvious but because this is totally new to me so after reading steps my mind became ineffective. Maybe this just happens to me.

    Anyway, I make some small changes to your code here, hopefully it would help someone ‘newbie’ like me :)

    Again, thank you very much for the tutorial, it helps my job a lot!

    # Ensures you do not inherit an AzContext in your runbook
    Disable-AzContextAutosave -Scope Process

    # Connect to Azure with system-assigned managed identity
    $AzureContext = (Connect-AzAccount -Identity).context

    # set and store context
    $AzureContext = Set-AzContext -SubscriptionName $AzureContext.Subscription -DefaultProfile $AzureContext

    # Obtain a copy of the certificate
    $ExchangeOnlineCertThumbPrint = (Get-AzAutomationCertificate -ResourceGroupName “” -AutomationAccountName “AutomationAccountName” -Name “ExchangeOnlineCert”).Thumbprint

    # Import the ExchangeOnlineManagement Module we imported into the Automation Account
    Import-Module ExchangeOnlineManagement

    # Connect to Exchange Online using the Certificate Thumbprint of the Certificate imported into the Automation Account
    Connect-ExchangeOnline -CertificateThumbPrint $ExchangeOnlineCertThumbPrint -AppID “” -Organization “”

    # Get the Mailbox for Elan Shudnow
    $Mailboxes = Get-Mailbox -Identity “Elan Shudnow”

    # Output the Results for the Mailbox
    Write-Output $Mailboxes

    Reply
    • Elan Shudnow says

      January 26, 2023 at 9:14 am

      Happy it helped! Thank you for commenting.

      Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Primary Sidebar

  • GitHub
  • LinkedIn
  • RSS
  • YouTube

More to See

Azure AD User Settings

Pre-creating Azure AD App for Azure Migrate

January 24, 2023 By Elan Shudnow

Azure Runbooks Connecting to Exchange Online and Microsoft Graph

July 22, 2022 By Elan Shudnow

Using Python 3.8.0 Azure Runbooks with Python Packages

July 11, 2022 By Elan Shudnow

Preserving UNC Path after Azure Files Migration using DFS-N

April 10, 2022 By Elan Shudnow

Tags

ACR Always Encrypted Ansible Automation Availability Sets Availability Zones Azure Azure Active Directory Azure Application Gateway Azure Files Azure Firewall Azure Key Vault Azure Load Balancer Azure Migrate Azure Monitor Azure Web App CDN Cluster DevOps DFS Docker DPM Event Grid Exchange Exchange 2010 Exchange Online Function App ISA iSCSI Log Analytics Logic App Lync Microsoft Graph OCS Office Personal PowerShell Proximity Placement Groups Runbook SCOM Storage Accounts Symantec Virtual Machines Windows Server 2008 Windows Server 2008 R2

Footer

About Me

Microsoft Cloud Solution Architect focused on Azure IaaS, PaaS, DevOps, Ansible, Terraform, ARM and PowerShell.

Previously a 6x Microsoft MVP in Exchange Server and Lync Server.

My hobbies include watching sports (Baseball, Football and Hockey) as well as Aviation.

Recent

  • GRS Storage and BCDR Considerations
  • Pre-creating Azure AD App for Azure Migrate
  • Azure Runbooks Connecting to Exchange Online and Microsoft Graph
  • Using Python 3.8.0 Azure Runbooks with Python Packages
  • Preserving UNC Path after Azure Files Migration using DFS-N

Search

Tags

ACR Always Encrypted Ansible Automation Availability Sets Availability Zones Azure Azure Active Directory Azure Application Gateway Azure Files Azure Firewall Azure Key Vault Azure Load Balancer Azure Migrate Azure Monitor Azure Web App CDN Cluster DevOps DFS Docker DPM Event Grid Exchange Exchange 2010 Exchange Online Function App ISA iSCSI Log Analytics Logic App Lync Microsoft Graph OCS Office Personal PowerShell Proximity Placement Groups Runbook SCOM Storage Accounts Symantec Virtual Machines Windows Server 2008 Windows Server 2008 R2

Copyright © 2025 · Magazine Pro on Genesis Framework · WordPress · Log in