Orchestration of Exchange online mailbox size reports using Azure automation & Microsoft Graph API

6 minute read

Reporting in M365 has come a long way. We all know that anyone with the proper access can view the M365 reports in the admin portal with a splendid graphical view. What if the operations team wants to get the notifications to a mailbox if the mailbox size reaches a specific limit (example use case)? My initial thoughts were to use SendGrid to send emails, but I thought we could use graph API and an Azure AD application to send emails rather than relying on a third-party email solution. I planned to use Azure automation to query the mailbox size from the Exchange online & use Microsoft Graph API to send emails.

Upon researching how to automate the mailbox size reports using Azure automation & Microsoft Graph API, I came to the following solution.

Solution Overview

Solution Overview

The Azure run as account will extract Exchange online mailbox statistics using Azure automation in the solution. Suppose we find any mailboxes that are above a specific limit. In that case, the automation account will query the Azure key vault to retrieve credentials of an Azure AD application to send email alerts using Microsoft Graph API.

You can learn more about Microsoft Graph API on how to send emails.

Microsoft Graph API documentation

We need the following to achieve the above solution.

  • Azure Automation Account
  • Exchange online module
  • Microsoft Graph API
  • Azure Key Vault

Azure Automation Account

You need to have an Azure automation account to run Azure automation jobs. Upon automation account & new runbook creation, you need to assign the Exchange administrator permission to run as account service principal. You can give the permissions via PowerShell or from GUI. You need to install the Exchange online module PowerShell in the automation account. You can get that from the modules gallery, as shown below.

Assigning Exchange administrator role to service principal Exchange Administrator role in Azure AD roles & responsibilites.

Assigning Exchange administrator role to service principal Assigning Exchange administrator role to run as account service principal.

Install Exchange online module Exchange online powershell module

Install Exchange online module Exchange online powershell module in the gallery.

App registration in Azure AD

Create and register a new application in Azure AD to send emails using Microsoft Graph API. Upon creating and registering the application, you need to assign Microsoft Graph API permissions to send emails on behalf of users. You need to choose application permissions in the Microsoft Graph API.

Microsoft Graph API Permissions Microsoft Graph API permissions.

Microsoft Graph API Permissions Assigning Microsoft Graph API application permissions.

Microsoft Graph API Permissions Microsoft Graph API permissions on the Azure AD Application.

Now, we need to authenticate the application from the custom code in the runbook. I will do this by configuring a client secret in the application. Make sure to copy the secret upon creation as you can’t see the client secret again.

Azure AD Application Client Secret Client secret in Azure AD Application.

Azure key Vault

We will leverage Azure Key Vault to securely store the client secret we created for the Azure AD Application. We manually generate a new secret in the Azure key vault. Upon holding the client secret, we will assign “Get” permissions to the “run as account” in the key vault access policies to retrieve the secret. We don’t need any other permissions as we are just retrieving the secret.

Azure Key Vault Secret Generate secret in Azure key Vault.

Azure Key Vault Secret

Run the below code to send secure emails from the Azure automation runbook using Graph API.

I categorized the code into three parts.

  • Import modules & Azure Automation account connection
  • Mailbox size report from Exchange online
  • Microsoft Graph API to send emails

Import modules & Azure Automation account connection

You need to import the Azure, Azure key vault and Exchange online modules and connect to the Azure automation account from the runbook as shown below.

Import-module AZ.Accounts
Import-module Az.Keyvault
Import-module Exchangeonlinemanagement

### Connecting to Azure Automation Account ###
try {

    $connection = Get-AutomationConnection -Name AzureRunAsConnection
    $tenantName = "test.onmicrosoft.com"
    $Azureaccount = Connect-AzAccount `
        -ServicePrincipal `
        -TenantId $Connection.TenantId `
        -ApplicationId $Connection.ApplicationId `
        -CertificateThumbprint $Connection.CertificateThumbprint 
}
catch
{
    if (!$Connection)

    {
        ## Run as Account not configured ##
        $error = "Connecting to $connection failed"
        throw $error
    }
    else
    {
        ## Something else went wrong ##
        write-error -message $_.Exception.message
        throw $_.Exception
    }
}

Mailbox size report from Exchange online

Authentication to Exchange online will be done using the “run as account”. Run the below PowerShell code to extract the mailbox size report for mailboxes greater than a specific limit.


### Connecting to Exchange online ###
Connect-ExchangeOnline -CertificateThumbprint $connection.CertificateThumbprint -AppId $connection.ApplicationID -Organization $tenantName -Verbose
# Date Format
$CurrentDate = (get-date).ToString('yyyy-MM-dd')
$CurrentDate
#Mailboxes
$Mailboxes = Get-Mailbox -ResultSize Unlimited | Select-Object Name, Alias, ExchangeGuid
$Output = @()
$i = 1
Foreach ($Mailbox in $Mailboxes)
{
    $Mailboxsize = Get-MailboxStatistics -Identity "$($mailbox.ExchangeGuid)" | Select-Object DisplayName, MailboxTypeDetail, @{name=TotalItemSize; expression={[math]::Round(($_.TotalItemSize.ToString().Split(()[1].Split( )[0].Replace(,,””)/1GB),2)}}, IsArchiveMailbox

    if ($mailboxsize.TotalItemSize -gt "90") {
        
        $Mailboxesabovelimit = New-Object -TypeName psobject -Property @{

            DisplayName = $Mailboxsize.DisplayName
            Emailaddress = $mailbox.PrimarySmtpAddress
            MailboxType = $Mailboxsize.MailboxTypeDetail
            MailboxSize = $Mailboxsize.TotalItemSize
            ArchiveStatus = $Mailboxsize.IsArchiveMailbox
            
        }
        $Output += $Mailboxesabovelimit
    }
    $i++
}

$Output

$filename = "MailboxSizes"+"$currentdate"+"."+"csv"

$filename

$path = "$Env:temp\$filename"

$path

$Output | Export-Csv -Path $path -NoTypeInformation

Microsoft Graph API to send emails

Run the below code to send the emails using Graph API securely. You can modify the email template as per your requirement. I used a shared mailbox to send notification emails.


if ($Output)
{

$base64string = [convert]::ToBase64String((Get-Content -path $path -Encoding byte))

#### key vault Name ###

$keyvault = "Enter your Keyvault name"

### Secret Name in the key Vault ###

$keyvaultSecretName = "Enter your secret name from the Keyvault"

### Application ID of the Azure AD App registration ###

$Application_ID = "xxxx-xxxx-xxxxx-12345"

### Tenant ID of the Azure AD App registration ###

$TenantID = $connection.TenantId

### From Address of the Email from Exchange online ###

$from = "[email protected]"

### To Address of the email ###

$to = "[email protected]"

### Getting the secret from Azure Keyvault ###

$secret = Get-AzKeyVaultSecret -VaultName $KeyVault -Name $KeyVaultSecretName


$Securestringpointer = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secret.SecretValue)


try {

   $secretValueText = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($Securestringpointer)

} 

finally {
  
   [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($Securestringpointer)

}


$client_secret = $secretValueText

$request = @{
        Method = 'POST'
        URI    = "https://login.microsoftonline.com/xxxxxxxxxx/oauth2/v2.0/token"
        body   = @{
            grant_type    = "client_credentials"
            scope         = "https://graph.microsoft.com/.default"
            client_id     = $Application_ID
            client_secret = $client_secret
        }
    }
$token = (Invoke-RestMethod @request).access_token

#$token

## Build the Microsoft Graph API request

## Header ##

$headers = @{
    "Authorization" = "Bearer $($token)"
    "Content-type"  = "application/json"
}

## Sending email ##

$sendURL = "https://graph.microsoft.com/v1.0/users/$from/sendMail"

$mailbody = @"
                    {
                        "message": {
                          "subject": "MailboxSize Report - $CurrentDate",
                          "body": {
                            "contentType": "HTML",
                            "content": "Email content."
                          },
                          
                          "toRecipients": [
                            {
                              "emailAddress": {
                                "address": "$to"
                              }
                            }
                          ]
                          ,"attachments": [
                            {
                              "@odata.type": "#microsoft.graph.fileAttachment",
                              "name": "$FileName",
                              "contentType": "text/csv",
                              "contentBytes": "$base64string"
                            }
                          ]
                        },
                        "saveToSentItems": "false"
                      }
"@

Invoke-RestMethod -Method POST -Uri $sendURL -Headers $headers -Body $mailbody

}

else
{
    Write-Verbose -Verbose "Customized error message"
}

You can schedule the runbook as per your requirement.

I hope you enjoy the blog!

Chiru