How to Create a Presence based Distribution List in Office 365
Have you ever wanted to send an Email (or maybe forward something you received but can’t action) to a Distribution list of just those people who are online and available to read and potentially action it, if so this maybe something for you.
Technically how to do it : Like everything in IT there are multiple approaches to solving a problem like this, eg the most obvious way of doing this would be a client side integration at send. However with the proliferation of different mobile clients this can be difficult so a server based solution is more advantageous and the approach I’ve taken.
Quick Tech Spec : For this server-less solution it will use a combination of different API’s in Office365 and Azure features working together to achieve the desired outcome. Here is a basic run down of how it works.
First the Distribution List is not an actual DL but an Office365 Unified Group (because 1990 called and wanted their method of distributing email to a group of people back). The members of the Group will be the users that should potentially receive any messages sent to the Distribution list if their Skype for Business (or Teams) status say they are online and available.
Second A Microsoft Graph web-hook that is Subscribed to the Unified Group’s Threads collection will trigger whenever a user sends a new Email to the Unified Group.
Third The Web-Hook will fire an Azure Automation Runbook Webhook that will then execute a Runbook that is written in PowerShell that will read the email that triggered it. The script will grab the members of the Unified Group using the Graph API, then using the UCWA API it will logon to Skype for Business Online and check the presence of each member to see if they are online and available, and if so they will be added to the Recipients of a new message. And finally the Runbook will send the new message from the original sender to the Group members who are available and online using the Graph API and an App Only token so it can send an impersonated email from the original sender(this ensures the reply chain is intact).
An Azure function intermediary is used in this step to proxy the Graph Web-Hook to the Azure Runbook Web-Hook because of a functionality gap in Azure Runbook Web-Hooks.
Fourth Because Web-hooks expire and need to be renewed the runbook will automatically renew or create the Graph Webhook. To ensure continued operation a schedule can be created on the runbook that will automatically maintain the subscription.
Putting it Together
Step 1. Creation of the Unified Group
This is the easiest step as it just involves going through the Exchange Administration Console (EAC) under recipients/Groups (you can also do this straight from Outlook) eg
The most important part of this step is to not check the “subscribe new members” box as we don’t want people to receive messages from this group unless they come from our Runbook once their presence is evaluated.
Step 2: Creating an Azure Function to act as an endpoint for our Graph web-hook and proxy for the Azure Automation web-hook
Because Azure Automation Web-hooks don’t provide the functionality of specifying what the response is (eg you just get an Ok or error) they can’t be used directly with Microsoft Graph Web-hooks because Microsoft Graph web-hooks require you to respond with a validation token. Therefore to workaround this issue we need to create a simple Azure WebFunction that can respond with the correct validation code and proxy the request on to our Azure Runbook’s webhook. For details of how to create an Azure Web Function see this which steps through everything you need to do and then you use the following code to proxy the request onto the Azure Automation Runbook Webhook.
You will need to customize the following line in the above code to use the WebHook url for your Azure Runbook which I’ll talk about creating below
string RunBookWebHook = "https://s8events.azure-o......";
Step 3: Create an Azure Automation Runbook
The Azure Automation Run-book is the heart of this application and will handle logging onto the Graph API and UCWA API to process the new Email when it arrives, process getting the Group members, handle Web-Hook Subscriptions and user presence. In the Runbook I’m using my Exch-Rest PowerShell Gallery Module which I started back in Feb 2017 which contains everything that is required for the Graph API ,Exchange and UCWA/Skype for business functions for the Run-book. For the Steps for Creating a Runbook please refer to this which will step through creating an Automation Account if necessary and then a PowerShell Runbook.
Import the Exch-Rest Module for use in the Runbook
Once you have the Runbook setup you need to import the Exch-Rest Module from the PowerShell Gallery so it can be used in the Runbook code. To do this from the Azure Portal in the Automation Account configuration select the Module Gallery link see
Then Search for the Exch-Rest Module and click Import see
Authentication : To logon and use the Microsoft Graph and UCWA Api’s the Azure Runbook is required to Authenticate using oAuth. For this application I’ve used a Service Account (which is just a normal account dedicated to this application) that will have a Office365 Mailbox and be enabled for Skype for Business and will be a member of the Unified Group where the messages are sent to. To allow for the ability to Resend the Message as the original sender the Runbook will use an AppOnly token that will require certificate authentication and a supporting Application registration. One of the Great features of Azure Runbooks is they allow you to securely store both a Credential object and certificate object securely and easily access them from within a Runbook Script so there are no insecure passwords in text (or encrypted files). (yes password expiry is still an issue but currently Application Permissions aren’t supported on Group thread operations so there is no way of getting rid of the Service Account requirement completely).
Application Registration: Like any Graph application the first thing that is required is an Application registration to use. For creating a standard application registration that will be required for the service account see this .
Service Account : For the service Account it will require Access to the Group operations in Graph, Access to the Directory for look-ups and the ability to send email.
App Only Token:
To give the Azure Runbook the ability to send an eMail as any user in the Tenant an App Only token is required. (Note this part could be skipped if you don’t mind the email coming from the service account you create). To create an Application registration in Azure see the Following, this registration will only need one application permission.
Once you have created this application registration you need to consent to it in the target tenant, the easiest way of doing this is using the following in a Web Browser with the application Id from your azure app registration in the clientId value
Once this is done you then need to create a self signed certificate and add that to the application manifest in Azure. There is some Microsoft documentation on doing this here ,
Or you can use the Exch-Rest module which now has a cmdlet that can do everything that is required if you pass in the objectId from the Azure Registration you just created eg
With this object Id if you execute the following it will create a Self signed certificate and add that to the manifest of your application
Invoke-EXRCreateAppTokenCertificate -CertName PDLCert -CertFileName c:\temp\PDLCert.cer -ObjectId 9319a335-6f8a-4049-89af-b43bb625239e
(as long as the appId you are running the Exch-Rest under has the following delegated permissions)
You can then assert the tenant admin rights of the user you have actually signed in as other wise you will just get a 403 error when trying to modify the application registration.
Creating the Credential and Certificate assets in Azure
For documentation that covers creating the credential and certificate assets in Azure see this. A tip for the credentials asset is use Powershell rather then the portal as the portal doesn’t allow you to have a password on the certificate file you try to upload.
The Code : Because most of the complexity is hidden away in the Exch-Rest module the actual Azure Runbook code is relatively simple here is a Gist of the Full Code for the Runbook
A basic run through of what happens in the code and what needs to be customized follows
- The surrounding script will be invoked through the Webhook on the Azure Runbook which will pass in the details of the Message that invoked it through the $WebhookData parameter. As most of our code is in a function the first line that will be executed is 133
- @Line 134 The ThreadURI is parsed out of the $WebhookData parameter (if it exists)
- @line 138 the $WebHookAddress variable needs to be customized with the endpoint for the Azure Function you create. This is used to check later that the WebHook exists and hasn’t expired on the Unified Group in Microsoft Graph. The Runbook is self healing so it will create the Webhook if it doesn’t exist or is expiring in the next 2 hours.
- @Line 139 this is the code necessary to get the PSCredential which is stored in Azure. The Name needs to be customized
- @Line 140 this the code necessary to get the certificate that will be used to get the AppOnly token
- @Line 141,142 are the variables that hold the Application Id’s for the Application registrations that will be used for the ServiceAccount and AppOnly Token (You should customize this to your own Application Id’s).
- @Line 143 is where the Invoke-PresenceDLCheck function is executed, the -ServiceAccount and -GroupEmailAddress parameters need to be customized to your own values.
- @Line 58 This is where the connection to the Mailbox is done in the code, what happens at this point is the Access Token is generated from the passed-in credentials and cached.
- @Line 59 We get the unified Group based on the email address of the Group that is passed in.
- @Line 61 There is a decision branch in the Runbook, if the script isn’t invoked by the Webhook it will scan the group for thread posts in the last 5 minutes that haven’t been action-ed. This is useful in testing and just adds another feature to the script. Whether if this is run manually or invoked by the WebHook with a ThreadId of the Thread that caused the Event to fire the result is the same which is a collection of the Threads which the next request will get the individual post from.
- @Line 68 and 71 to make sure a thread is never processed more then once it makes use of Open extensions in the Graph API. Open Extensions are a way in the Microsoft Graph of adding un-typed properties to a item. My use case in this instance is to ensure that an item doesn’t get processed more than once.
- @Line 73 this is where the connection to Skype for Business happens, SK4B has a discovery process that must be run through which also requires that multiple Tokens be generated. The Credentials come from the original mailbox connection in @Line 58 and we just use the Refresh Token to generate a new access token against the UCWA resource URI’s.
- From @Line 76 it starts the Group member enumeration and first we get the PresenceURI from SK4B using the mail address as the search parameter and then we can finally make a presence request. A collection of Addresses is then built for those users that are online and available
- @Line 91 if there aren’t any users available at the moment a message is sent back to the sender from the Service Account to say nobody can help out at the moment.
- @Line 97 if there are users available we start to build the message to resend to these online users. Because we can’t export or resend the message directly from the Group we have to rebuild the message from its components. Because there is a Bug currently in the Graph API around getting the html body of a Group conversation I have a workaround of using the HTML_Body extended mapi property to get the body contents.
- @Line 100 generates the App token from the certificate, there is some extra code here to allow for a certificate to be passed in by File but this is really only for testing and debugging. In Production you want to use the secure method that the Azure RunBook provides for storing certificates.
- @Line 107 It sends the Message using the App Token
- @Line 111 is where we start the Graph Webhook subscription management. If the WebHook doesn’t exist a webhook will be created, if one does exist we check the expiration and if this is in 2 hours we delete and create a new WebHook.
Installation and Starting everything off
Once you have the Function and Run-book setup to start everything off and also to keep it running on a continued basis you should setup a Scheduled job that runs the run-book at least once a day. See the following for documentation on how to do this. Once you have the schedule setup run the schedule manually the first time which will create the Webhook. You can also run the Runbook from within the Test pane in the Runbook editor in the Azure Portal which will allow you to see the output from the Runbook when it runs.
Improvements and Vnext
As with any code its never really done here are some of the things that still need a little work
- Convert it to just using Certificate authentication and App Only tokens only (so no need for a service Account), this would require using the Trusted API on the Skype side and for Application permissions to be available in Group Threads (which I’m sure is coming…).
- Process Attachments, currently it doesn’t process the Attachments on the original Message. This is the easiest thing to fix technically as the Library now has everything that is needed to do this. However it maybe better at that point to convert any attachments to reference attachments (meaning they will exist on OneDrive)which the library also has the ability to do
All the code from this article is available in GitHub from the following links
Azure Function :