7 min read

Enriching Mail Filtering Rules by Logic Apps

Justin Yoo

While using either Outlook.com or Office 365 Mail, I always want to have a better email filtering rule engines shipped. Apparently, Gmail has a better spam-mail filtering engine. Personally, as soon as I receive emails, I prefer to moving them into designated folders based on my manual filtering rule. I've set many filtering rules in my account options, but that's not enough. About half of my incoming mails are filtered, and the rest simply go into the spam mail folder or stay inbox.

The good news is that the MS Flow service is offered to both Outlook.com users and Office 365 users, free of charge. That Free plan offers free 750 execution times, which is convenient. If you have an Azure subscription, Logic App is an alternative as both MS Flow and Logic App shares the same connectors and workflows. Therefore, with these services, you reinforce your mail filtering rules with satisfaction. In this post, I'm going to build an Azure Logic App to filter out emails and move them to designated folders.

Use Case Scenario

As a Microsoft MVP, I have a lot of emails sending to and receiving from Microsoft product groups and other MVPs, by sharing pieces of information and feedback. As there are many mailing lists for this purpose, depending on the mailing list, I want to move them into their corresponding folders. For example, Azure related mails go into the Azure folder, and .NET related mails go into the .NET folder, etc.

Assumptions

There are a few assumptions to achieve this scenario.

  1. I use a free Outlook.com email address.
  2. I have mailing lists.
  3. I have folders corresponding to the mailing lists.
  4. All emails come into Inbox.

Finding Unique Folder ID in Outlook.com with Graph Explorer

Each folder in Outlook.com has its own/unique ID. As it is an encrypted string, it's not easy to remember. Fortunately, Microsoft Graph provides API endpoints to get those folder IDs. Even easier, there is a web-based Graph Explorer tool so that we can easily find out all folder IDs.

When we go to Graph Explorer, you can see the following screen. Login to your Outlook.com account through the Sign In with Microsoft button at the left-hand side.

Once logged in, you'll be asked to register an application and grant permissions, followed by the logged-in screen with your profile picture.

The current version of Microsoft Graph is 1.0 and beta. As either version is okay for us to use, let's use the 1.0 version of API. Then use this document to find out all folders under the Inbox folder.

NOTE: This API only returns the list of folders under the Inbox folder. If you want to get all sub-folders, it will require several recursive calls.

Although I only need the id attribute from the result, for the sake of convenience, I take both id and displayName attributes. Here are my returns, which I slightly masked those folder IDs.

[
{
"id": "AQMkADAwAT****************AAAA==",
"displayName": ".NET"
},
{
"id": "AQMkADAwAT****************AAAA==",
"displayName": "ASP.NET-IIS"
},
{
"id": "AQMkADAwAT****************AAAA==",
"displayName": "Azure"
},
{
"id": "AQMkADAwAT****************AAAA==",
"displayName": "DevOps"
},
{
"id": "AQMkADAwAT****************AAAA==",
"displayName": "Integration"
},
{
"id": "AQMkADAwAT****************AAAA==",
"displayName": "Office365"
},
{
"id": "AQMkADAwAT****************AAAA==",
"displayName": "Windows"
},
{
"id": "AQMkADAwAT****************AAAA==",
"displayName": "Xamarin"
}
]

Let's add an email address to the email attribute for each array item, and change the id attribute to folderId for better readability.

[
{
"email": "mvp-dotnet@mailing-list.net",
"folderId": "AQMkADAwAT****************AAAA==",
"displayName": ".NET"
},
{
"email": "mvp-asp.net@mailing-list.net",
"folderId": "AQMkADAwAT****************AAAA==",
"displayName": "ASP.NET-IIS"
},
{
"email": "mvp-azure@mailing-list.net",
"folderId": "AQMkADAwAT****************AAAA==",
"displayName": "Azure"
},
{
"email": "mvp-devops@mailing-list.net",
"folderId": "AQMkADAwAT****************AAAA==",
"displayName": "DevOps"
},
{
"email": "mvp-integration@mailing-list.net",
"folderId": "AQMkADAwAT****************AAAA==",
"displayName": "Integration"
},
{
"email": "mvp-office365@mailing-list.net",
"folderId": "AQMkADAwAT****************AAAA==",
"displayName": "Office365"
},
{
"email": "mvp-windows@mailing-list.net",
"folderId": "AQMkADAwAT****************AAAA==",
"displayName": "Windows"
},
{
"email": "mvp-xamarin@mailing-list.net",
"folderId": "AQMkADAwAT****************AAAA==",
"displayName": "Xamarin"
}
]

NOTE: The email address is for the demo purpose only, not the real ones.

I've got ready for folder lookup reference. Let's build the Logic App.

Building Logic App for Email Filtering

First of all, let's create a blank Logic App instance with your preferred location. Then add outlook.com trigger to be invoked when a new email arrives. In the trigger, set the folder to Inbox and leave others as default.

NOTE: This time, I just use UI for authoring, not through an ARM template, as I want to show this would bring the same experience to anyone who uses Flow.

Now, with this trigger, all emails hitting Inbox will invoke this Logic App. Let's add workflows with actions. As I've created the lookup array above, put that array as a reference into the variable with an Initialize Variable action. It's called FolderLookupReferences.

An email can have multiple recipients, so they need to be converted to an array. I put both recipients (To) and carbon-copied (Cc) into one array. Add a Compose action for this.

The action in JSON may look like this. I'm not explaining each function used in this action, but it is worth having a look at Workflow Definition Language when you have time. As soon as you see those functions, you'll understand straight away.

"Get_All_Recipients": {
"type": "Compose",
"runAfter": {
"Initialise_Folder_Lookup_References": [
"Succeeded"
]
},
"inputs": "@union(coalesce(split(triggerBody()?['Cc'], json('[]')), ';'), split(coalesce(triggerBody()?['To'], json('[]')), ';'))"
},

This action is to find out who the recipient is and folder. Use the Query action for it. Put the reference array into the From field. Then add the query to check whether the recipient array contains any of reference email or not.

Here's the JSON bit.

"Filter_Recipients": {
"type": "Query",
"runAfter": {
"Get_All_Recipients": [
"Succeeded"
]
},
"inputs": {
"from": "@variables('FolderLookupReferences')",
"where": "@contains(outputs('Get_All_Recipients'), item()?.email)"
}
}

If the filter query returns nothing or an empty array, this workflow doesn't need to proceed. It can be done with a combination of the If action and Terminate action.

Its corresponding JSON code looks like this:

"Stop_Processing_If_Filter_Returns_Empty": {
"type": "If",
"runAfter": {
"Filter_Recipients": [
"Succeeded"
]
},
"expression": {
"and": [
{
"equals": [
"@length(coalesce(body('Filter_Recipients'), json('[]')))",
0
]
}
]
},
"actions": {
"Cancel_Processing_Due_to_No_Recipient_Found": {
"type": "Terminate",
"runAfter": {},
"inputs": {
"runStatus": "Cancelled"
}
}
}
}

If this condition is NOT met, the email belongs to at least one mailing list. As there is a chance that it has multiple reference array items, this Compose action takes the first item.

Here's the JSON code.

"Take_the_First_Recipient": {
"type": "Compose",
"runAfter": {
"Stop_Processing_If_Filter_Returns_Empty": [
"Succeeded"
]
},
"inputs": "@first(body('Filter_Recipients'))"
}

I've finally got the folder ID for the email to be sent. As the last step, this API Connection action moves the email to the designated folder. Put the message ID and folder ID into their corresponding fields.

This action can be expressed in JSON like:

"Move_Email_to_Designated_Folder": {
"type": "ApiConnection",
"runAfter": {
"Take_the_First_Recipient": [
"Succeeded"
]
},
"inputs": {
"method": "post",
"host": {
"connection": {
"name": "@parameters('$connections')['outlook']['connectionId']"
}
},
"path": "/Mail/Move/@{encodeURIComponent(triggerBody()?['Id'])}",
"queries": {
"folderPath": "Id::@{outputs('Take_the_First_Recipient')?['folderId']}"
}
}
}

Now, we've completed the entire workflow orchestration. As soon as it's deployed, this Logic App watches my Inbox on outlook.com. When an email touches the Inbox, it invokes the Logic App. If processed, the email goes into the designated folder; otherwise, it will stay in Inbox. Here's the result.

After all, my Inbox looks cleaner than ever!


So far, I've built a Logic App to enrich email filtering rules of Outlook.com. Based on the Logic App invocation result, emails are moved to their own folders. This activity may seem to be a tiny thing, but it gets your productivity improved a lot, actually. What I showed in this post is Logic App, but this can be done in Flow exactly in the same way.

If you feel like adding some automation for your email sorting, why not trying this?