When it comes to automating Exchange Online operations, IT administrators can find many examples that use Azure Automation. Most Azure Automation scenarios use PowerShell Runbooks. A complete walkthrough of using Azure Automation with Exchange Online can be found in this article. 

But what about Azure Functions? 

Introducing Azure Functions 

Azure Functions is a serverless computing platform, which can be triggered by different events: 

  • Queue 
  • Timer 
  • Event Grid 
  • HTTP 

The full list of triggers is described in the list of supported bindings. At first glance, the number of triggers seems to be very limited, but don’t underestimate the capabilities of the Event Grid trigger. This trigger is a service that allows an app to receive messages from Azure services, which makes it very flexible. One example is to use a configured alert from a Log Analytics workspace. 

Why use Azure Functions for Exchange Online? 

For years customers have asked Microsoft to implement some sort of recipient management in Microsoft Graph. However, nothing is currently available. Yes, it’s possible to work with Microsoft 365 groups through the Graph Groups API, but you cannot perform Exchange Online tasks like assigning mailbox or mailbox folder permissions. This means that any system that wants to interact with Exchange Online must use the Exchange Online PowerShell V3 module

In some cases, it’s not realistic to use Exchange Online PowerShell. Many systems like ServiceNow and Ping Identity prefer a JSON-formatted API. But it’s not all about these systems. Think about self-service scenarios where a user wants to check permissions for a mailbox or enable an archive mailbox. These and other operations could be performed through a web portal or a developed app supported by a variety of platforms like Windows, Mac, Android, or iOS. 

Another reason to use Azure functions to manage Exchange Online is the fact that HTTP requests can trigger PowerShell Runbooks but will not return any data to a client. An end-user or an app expects a response with the requested data. The failure to receive a response leads to a poor user experience. 

This article describes an example of how to build an Azure function to retrieve mailbox permissions. End users cannot see or set mailbox permissions. Only an administrator has the necessary privileges to maintain permissions on the mailbox level: The most common scenario when users need administrator intervention to set mailbox permissions is to update permissions for a shared mailbox. 

Creating a Function App 

As a prerequisite to creating an Azure Function, you must have an Azure subscription for your tenant. 

In the following steps, we create a function to return the mailbox permission for a given mailbox. The client, which could be a mobile app or any self-service portal, is unimportant. Briefly, we use PowerShell to send a request to our Azure Function. The response is a JSON-formatted payload containing mailbox permissions that our app can interpret and display to the user. 

Create app 

To start, create a Function app (to contain the functions) by following the steps outlined in Microsoft’s documentation. Make sure to select the correct runtime stack (PowerShell Core). It’s up to you to define the storage account for the app to use. Figure 1 shows the basic properties of the function app, while Figure 2 shows a summary of the app before creation. 

Using Azure Functions for Exchange Online 
Figure 1: Basic properties of a Function App using PowerShell Core 
Using Azure Functions for Exchange Online 
Figure 2: Summary of the Function app before creation 

After creating the function app, for best performance, make sure that the app uses the 64-bit platform (Figure 3). 

Using Azure Functions for Exchange Online 
Figure 3: Updating app properties to use the 64-bit platform 

Use System Assigned Managed Identity 

The function app must be able to authenticate before it can run any functions. It uses a system-assigned managed identity for this purpose (Figure 4). The functions run in the context of the managed identity. For more information about using managed identities, read this article and this article about securing managed identities

Using Azure Functions for Exchange Online 
Figure 4: Enable managed identity for the app 

Assign Administrative Role to the Managed Identity 

After enabling the app to use a managed identity, you need to assign the necessary role to the service principal of the managed identity. The easiest way (from my perspective) is to go to Roles and Administrators, select Exchange Recipient, then Administrator Active assignments, and add the service principal of the managed identity used by the app (Figure 5). 

Using Azure Functions for Exchange Online 
Figure 5: Assign the Exchange Recipient Administrator role to the managed identity for the function app 

Note: You might ask why I selected the Exchange Recipients Administrator role rather than the Exchange Administrator role. The reason is simple: always use the role with the least privilege necessary to do the job. 

Grant Admin Consent to the Service Principal 

The next step grants the Manage Exchange as Application role to the service principal of the managed identity. If this is not done, the app cannot run PowerShell commands as an administrator, even with the assigned role. This article explains how to add the Manage Exchange as Application role to the managed identity using PowerShell (you can’t make the role assignment through the GUI). You’ll end up with a situation like that shown in Figure 6. 

Using Azure Functions for Exchange Online 
Figure 6: The effect of granting the Manage Exchange as Application role to the managed identity 

Add Exchange Online PowerShell V3 Module 

As we want to use PowerShell to connect to Exchange Online, we need to add the Exchange Online management module to our app. This is done by modifying the requirements.psd1 file (Figure 7). 

Using Azure Functions for Exchange Online 
Figure 7: Adding the Exchange Online management module to make it available to the function app 

Create the Function 

Now the Function app is configured, we can create a function itself. For our scenario, we chose the HTTP trigger template. Give the function a suitable name, like GetMailboxPermissions (Figure 8). 

Using Azure Functions for Exchange Online 
Figure 8: Creating a Function 

Whenever you create a function, the service add some demo code to illustrate how to use a function. Delete this code and replace it with the code shown below. 

using namespace System.Net 

# Input bindings are passed in via param block. 
param($Request, $TriggerMetadata) 

Write-Host "Connecting to EXO..." 
$paramsEXO = @{ 
    ManagedIdentity = $true 
    Organization = 'M365x….onmicrosoft.com'# replace with your tenant name 
    ShowBanner = $false 
    CommandName = @('Get-EXOMailboxPermission','Get-MailboxPermission') 
    ErrorAction = 'Stop' 
} 
try { 
    Connect-ExchangeOnline @paramsEXO 
} 
catch{ 
    # create response body in JSON format 
    $body = $_.Exception.Message | ConvertTo-Json -Compress -Depth 10 
    break 
} 
# get name from query parameter 
$name = $Request.Query.Name 
if (-not [System.String]::IsNullOrEmpty($name)) 
{ 
    try { 
        Write-Host "Retrieving mailbox permissions for:$name" 
        $paramsPerms = @{ 
            Identity = $name 
            ErrorAction = 'Stop' 
        } 
        $permEXO = Get-EXOMailboxPermission @paramsPerms 
        $body = $permEXO | ConvertTo-Json -Compress 
    } 
    catch{ 
        # create response body in JSON format 
        $body = $_.Exception.Message | ConvertTo-Json -Compress -Depth 10 
    } 
} 

# Associate values to output bindings by calling 'Push-OutputBinding'. 
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ 
    StatusCode = [HttpStatusCode]::OK 
    Body = $body 
})

Using the Function 

Now we have created our function, we can use it by sending it a request. In this example, I wanted to get the permissions set on the mailbox AdeleV@M365x16362668.OnMicrosoft.com. Here’s how to do the job with some simple PowerShell: 

$Uri = 'https://tecdemo.azurewebsites.net/api/GetMailboxPermissions?name=AdeleV@M365x16362668.OnMicrosoft.com' 

$paramsReq = @{ 
    Uri = $Uri 
    Method = 'GET' 
    Headers = @{'x-functions-key' = '8ejgOTwP2HSElWs5fo_hiwdcqjQQTPexylTRxmzUca9qAzFusF7j_g=='} 
} 
$Resp = Invoke-WebRequest @paramsReq

The Uri used in the function contains the following: 

  • tecdemo: the name of the Function App.
  • azurewebsites.net/api: the default namespace for Azure Function.
  • GetMailboxPermissions: the name of the function within the Function App. 

You might have spotted that the parameters include a header called x-functions-key. This header contains the key configured on the function (Figure 9). Azure creates this key. 

Using Azure Functions for Exchange Online 
Figure 9: The function keys 

This is one way of securing your Azure function. If you don’t pass the key in the request, the response is error 401 unauthorized. Other ways securing your function are documented here

The response to the function is JSON formatted and can be easily converted. Figure 10 shows the raw response. 

Using Azure Functions for Exchange Online 
Figure 10: The raw HTTP response from the Azure Function 

It’s easy to convert the JSON data in the response to something more like what an administrator sees when they interrogate mailbox permissions using the Exchange Online management module (Figure 11). 

Using Azure Functions for Exchange Online 
Figure 11: The request and response from the Azure Function 

Limitations 

The most important limitation you need to understand is a timeout (the time when the function must respond to the request). The default is 5 minutes and can be increased to a maximum of 10 minutes. If you use a different Azure plan, you can get an unlimited timeout, but it’s all a question of costs (plans can get expensive!). Other limits are described in Service limits

Conclusion 

If you have long-running administrative tasks, I recommend using PowerShell Runbooks. However, if you have the need to interact with Exchange Online without using PowerShell, you should consider Azure Functions. 

If you want you can meet me at The Experts Conference (TEC) 2023, where I’m speaking about this topic in more detail. 

Microsoft Platform Migration Planning and Consolidation

Simplify migration planning, overcome migration challenges, and finish projects faster while minimizing the costs, risks and disruptions to users.

About the Author

Ingo Gegenwarth

Ingo Gegenwarth is a Technical Consultant at a market-leading organization for enterprise application software. In his role, he is responsible for the Exchange infrastructure as well as Office 365. In 2003, he earned his first MCSE 2000, and since then he's earned Microsoft Certified Master (MCM) and Microsoft Certified Solutions Master (MCSM).

Comments

  1. Danny

    Great article!, it was helpful for me. Thanks.

  2. Ingo

    Hi Egor,
    I see where you coming from and what you’re aiming for. This was the regular and supported way. Let’s wait for other upcoming posts…
    Ciao,
    Ingo

  3. Egor

    This is interesting, but I believe the time of the Exchange management module has passed. As your colleague Michev described, you can use a language of your choice to hit the REST adminApi Exchange endpoints directly, that’s way more lightweight approach.

Leave a Reply