Managing DLs through Azure runbooks

I will try to go through the pain points of managing Distribution lists in Microsoft.

Anish P R
5 min readApr 3, 2024

It has been a long outstanding issue in Microsoft that, they don't allow managing their exchange module via graph apis or any other apis for that matter. So you have to manage it via their UI or Powershell.

https://techcommunity.microsoft.com/t5/microsoft-365-developer-platform/allow-addition-of-members-to-mail-enabled-security-groups-via/idi-p/2981455

Coming to how you can potentially automate exchange management or any such use cases where Microsoft doesn't have apis but allows you to manage via Powershell. We can create runbooks on Azure automation accounts which can do these management actions and call it via a webhook to trigger it whenever required.

For eg: When you want to add a user to a DL, just call the webhook with the DL name and user principal.

There are numerous tutorials on this all around the web, but you might get stuck on what kind of identity to use, how to give permissions, and how to call it remotely.

Sharing a few here in case it helps

  1. https://www.damobird365.com/add-user-to-distribution-list/
  2. https://learn.microsoft.com/en-us/powershell/exchange/connect-exo-powershell-managed-identity?view=exchange-ps
  3. https://www.shudnow.io/2022/07/22/azure-automation-runbooks-and-connecting-to-exchange-online/
  4. https://practical365.com/use-azure-automation-exchange-online/
  5. https://stackoverflow.com/questions/74213208/connect-exchangeonline-unauthorized

The setup

Step 1 (Creating an automation account)

Create an Azure automation account (Microsoft gives you 200$ free credits)

Choose your resource group, give a suitable name, and choose the region click next……
Make it a system-assigned identity (for the example I am sharing we will give the automation an identity and make it access Azure Exchange using that identity)
Make it public access (required for calling the automation from remote server or client)….
Click Create and you are done

Step 2 (Creating a Runbook)

Next, you need to configure a Powershell (7.2 version) based runbook that has ExchangeOnlineManagement & ExchangePowerShell modules.

First Create a runbook for writing the script

Click Create a Runbook then choose powershell, with version 7.2 (preferably)
param(
[object]$WebhookData
)
try {
"Logging in to Exchange..."
Connect-ExchangeOnline -ManagedIdentity -Organization atomicwork.onmicrosoft.com
"Adding user..."
$webhookBodyObject = (ConvertFrom-Json -InputObject $WebhookData.RequestBody)
Write-Output "Hello $webhookBodyObject"
Write-Output $webhookBodyObject.email
Write-Output $webhookBodyObject.distribution_list_id
Add-DistributionGroupMember -Identity $webhookBodyObject.distribution_list_id -Member $webhookBodyObject.email
"User Added"
} catch {
Write-Error -Message $_.Exception
throw $_.Exception
}

You can use this code if your end goal is to add user to a DL.

Install 2 modules ExchangePowerShell & ExchangeOnlineManagement
Go to Automation account -> Modules -> Add module -> Browse from gallery -> Search ExchangePowerShell and hit select.
Repeat for ExchangeOnlineManagement

Click add a module
Browse from gallery
Select and done

Now you have the script and runbook read. Now all you need is to give access for the automation accounts identity to access your exchange.

Step 3 (Giving access)

Assigning a role to automation account

Go to Microsoft Entra Id -> Roles and Administrators -> search for Exchange administrator -> Click on it -> Add assignments -> search for the automation account you created and assign it.

Pre final step : Microsoft also expects the service principal of the automation account to have certain permission on top of the role assigned, this can be done with powershell(if you are using mac you can still use powershell — https://formulae.brew.sh/cask/powershell)

  • Get the managed identity id (go to enterprise apps, uncheck Application type = Enterprise Applications), search for your automation account to get its object id. managedIdentityObjectId in the upcoming script
  • Similarly search for Office 365 Exchange Online and copy the object id for serverServicePrincipalObjectId in the upcoming script or run Get-Get-MgServicePrincipal -Filter “DisplayName eq ‘Office 365 Exchange Online’”

Get the App role ID for the Exchange.ManageAsApp permission (either by api or through powershell)

https://graph.microsoft.com/v1.0/servicePrincipals/{{theObjectId you got in prev step for office 365}} -> grab the id Exchange.ManageAsApp
currAppRoleId in the upcoming script

Once installed poweshell and have the following values run the following commands to assign Office 365 Exchange Online, Exchange.ManageAsApp permission

Install-Module -Name Microsoft.Graph.Authentication -Repository PSGallery -Force                                                                                                                   
Install-Module -Name Az.Accounts -Repository PSGallery -Force
Install-Module -Name Microsoft.Graph.Applications -Repository PSGallery -Force

Connect-MgGraph -Scopes Application.Read.All, Application.ReadWrite.All,AppRoleAssignment.ReadWrite.All,Directory.ReadWrite.All

New-MgServicePrincipalAppRoleAssignment `
-ServicePrincipalId $managedIdentityObjectId `
-PrincipalId $managedIdentityObjectId `
-ResourceId $serverServicePrincipalObjectId `
-AppRoleId $currAppRoleId

End result would be something like

Thats it, Now you have an automation account, runbook with a script and required modules, permission for the automation accounts to perform actions on Exchange.

Step 4(the final step — webhooks)

Expose a webhook for the runbook script that was created, so that it can be invoked from other places.

Click on create new webhook
Give a name & copy the url
Leave the parameters empty (this will be passed when you call the webhook)

This seems like a cumbersome setup, but once you have done this initial setup, life becomes easier, All you need is to create a webhook

Time to try it out

Go to your favourite api tester app (postman) or terminal and run the curl

curl --location 'yourwebhookurl' \
--header 'Content-Type: application/json' \
--data-raw '{
"email": "youruserPrincipal",
"distribution_list_id": "your dl id"
}'

Run this, and you can see that in few seconds the runbook runs and the user gets added to dl, Runbook UI gives you visibility of the jobs that ran (look at the jobs tab, click on the runs and see the output, errors etc)

Now u can manage any such action by creating multiple such scripts and calling it via webhooks

  1. Adding someone to DL
  2. Removing someone from DL
  3. Changing owner of DL etc

--

--