Bring Microsoft Teams incoming webhook security to the next level with Azure Logic App

Bring Microsoft Teams incoming webhook security to the next level with Azure Logic App

Webhooks are a very popular and simple integration mecanism that allow services to notify each others about different types of events. A typical use case is when you need to be notified that a task completed or get the execution results : the webhook represents the endpoint where you will receive this notification to take further actions (as triggering a workflow or notifying a group of users)

Microsoft Teams support incoming webhooks to send notification directly into a channel - This type of connector is configured per Teams channel - You can choose a connector from App Source or create your own custom webhooks. More info here:

Use Microsoft 365 and custom connectors - Microsoft Teams | Microsoft Docs

What are webhooks and connectors? - Teams | Microsoft Docs

Incoming webhooks are a simple integration solution by design: this is a pros of the solution but it also comes with cons raised on users voice forums. The objective of this article is to demonstrate how to get the simplicity of Teams incoming webhooks while addressing the most common asks from users:

  1. Better control over who can create and use a webhook
  2. More options to authenticate the event emitter
  3. Protect users from the content published into Teams

How will we achieve that? with Azure Logic App ! The general idea is to substitute the "incoming webhook" app in Teams by a workflow in Azure Logic App that exposes a webhook and leverage its "Post Adaptive Card in a chat or channel" Teams connector. The Azure Logic App workflow looks like this:

Azure Logic App workflow to replace Teams incoming webhooks

Azure Logic App is an interesting solution because it's a low-code service and cost efficient via a consumption-based pricing model - The typical costs for the above workflow (3 actions and 1 standard connector) that is triggered 10,000 times during the month is ~$2.00

Let's see how this solution covers the above user voice feedbacks.

Better control over who can create and use a webhook

Problem statement

Administrators want to control who in the organization can manage webhooks, where they can be deployed (which team channels) and who can access to the webhook URL. As of today, Teams admin portal allows to set application permission policies to specific users but you can't apply this policy to specific teams and/or channels. Also, to access the webhook URL, you need to be a member of the team where the connector is installed and this information is accessible to all owners of the team.

How Azure Logic App solves this problem ?

With the above workflow deployed in Azure, you can rely on Azure RBAC built-in roles for Azure Logic App to define your access policy. The configuration of the webhook is done in Azure and no configuration information is available from Microsoft Teams.

Also, the "Post adaptive card in a chat or channel" requires a connection that authenticates the user - This user needs to be a member of the team where the connection is established and also requires it's O365 Azure AD credentials to establish the connection. So, when a message is pushed into a team channel, you see who the owner of this connection is.

Last, a webhook is a secured string and you want to be able to manage the lifecycle of your SAS signature - This management is provided by Azure Logic App where you can regenerate the signature based on new encryption keys, choose between primary and secondary key or even set an expiration time after which the SAS sig is invalidated. More details on this link Secure access and data - Azure Logic Apps | Microsoft Docs

More options to authenticate the event emitter

Problem statement

When a chat message coming from an incoming webhook is published in a channel, you don't have any information about the emitter (i.e. about who used the webhook) - All you know is which application published the message, when it was published, its content and additional info that defines the chat message - This info is available via the MS Graph API. Users ask for a solution to control authorized emitters and identify them.

How Azure Logic App solves this problem ?

There are different ways to authenticate an emitter and Azure Logic App provides a wide range of options to do that - These methods come in addition to the SAS signature or can replace it. To name the most requested one in the context of incoming webhooks, we have:

  • Source IP white listing
  • Basic authentication / Http headers acces keys
  • Azure AD OAuth2.0 token

All these methods are available via Azure Logic App. The authentication solution you choose really depends on the capabilities of the emitter to authenticate itself to the webhook. You can also add an Azure API management in front of Azure Logic App to get more flexibility and capabilities to cover these scenarios.

Last, there are no extract costs to enable these authentication methods on Azure Logic App and APIM also has a consumption-based deployment and cost model (e.g. $4.20 per million calls in Central US)

Protect users from the content published into Teams

Problem statement

In addition to know when a webhook has been installed / added / updated / deleted into a team channel, administrators also want to analyze the content pushed via the webhook and log these events. With Office 365 audit logs enabled for Microsoft Teams, you get the required information related to the webhook configuration (who / when / where / which webhook was installed / added / updated / removed) but you don't have access to the payload. To get this information, you can use the MS Graph API and create a subscription to receive all Teams chat messages (at the tenant level or for specific chats or channel) or use a trigger in Azure Logic App / Power Automate called "When a new channel message is added" - The problem with these approaches is that you collect all messages and not only the ones sent via the webhook, adding complexity and costs to process this content.

How Azure Logic App solves this problem ?

With Azure Logic App, you will capture all requests to the webhook because you have control over the endpoint (versus incoming webhooks hosted and exposed via the O365 platform) - When the workflow is triggered, you can add actions steps to your workflow to:

  • Validate the schema of the payload (in case an inappropriate JSON content is pushed)
  • Log this request into an external system - e.g. you can push this payload or log this event into Azure Monitor and process the content in Azure Sentinel using Logic App built-in connectors.
  • Map / aggregate / curate / enrich / .... the incoming content and format the message to be pushed in Teams using Adaptive Cards.

The result is that all content pushed into Teams has been logged and checked before it is delivered into team channels and users.

Another very recommended service to use, not related to Azure Logic App and applicable to all chat conversations, is the enable Safe Links for Microsoft Teams.

"Safe Links is a feature in Defender for Office 365 that provides URL scanning and rewriting of inbound email messages in mail flow, and time-of-click verification of URLs and links in email messages and other locations. Safe Links scanning can help protect your organization from malicious links that are used in phishing and other attacks."

Other considerations

Some type of incoming webhooks, aka O365 Connectors, do not support Adaptive Cards. Adaptive Cards provide a greater user experience with richer formatting options. Azure Logic App connector for Teams support Adaptive Cards v1.2 format - You can use the Adaptive Card designer portal to create your own cards.

Azure Logic App is not seen as a standard application in Microsoft Teams. From a governance and deployment point of view, this approach is not self-serviced for the end-users and require access to an Azure Subscription.

You can send incoming webhooks to Azure Logic App using a private endpoint. This is very interesting if the system that needs to notify Microsoft Teams is a server hosted on an internal private network. Azure Logic App will act as a gateway between your private network and Microsoft Teams service on O365 public cloud.

Let's take and implement a real case scenario

We mentioned earlier that Jenkins provides a connector for Microsoft Teams. This connector generates an incoming webhook URL that needs to be configured on Jenkins side to send notifications to Microsoft Teams. Follow the steps described on Jenkins site to configure O365 notifications for Teams.

Let's now replace this webhook (in the format https://aad-tenant-name.webhook.office.com/webhookb2/XXX/JenkinsCI/YYYY) by the webhook provided by Azure Logic App.

1st, the Azure Logic App flow definition

This flow is very simple - All you need to do is to create these 3 actions:

  1. Create an Azure Logic App (consumption plan)
  2. Select the trigger "When a HTTP request is received", leave the rest empty - The webhook URL will be generated when you save the workflow
  3. Add the action "Parse JSON" - Select the Body of the trigger for the content and use this simple Schema {"properties": {},"type": "object"}
  4. Add the action "Post adaptive card in a chat or channel" - You'll be asked to create a connection and to provide your O365 credential - then provide the following information: Post as "Flow bot", Post in "Channel" and select your team and chanel location. For the Adaptive Card content, you can use the designer mentioned before - I provide a sample at the end of this article.

2nd, you need an instance of Azure API Management to rewrite the http response of the Azure Logic App

Note: this step is only required in the context of this example and connector. You may not need depending on the configuration of your emitter (Jenkins & O365 connector here)

When you submit a request to a webhook, the expected http response code is 202 to acknowledge the request. This code doesn't tell you anything about the result of these action, you usually get a specific URL to check on the result (or you can be notified to a call back URL) - The O365 Jenkins connector expects an http response code 200, otherwise it will retry and send multiple requests for the same event. APIM is used to front Azure Logic App and rewrite the response 202 into 200.

  1. Create an Azure APIM Management (consumption plan)
  2. Go to API and select "Create from Azure resource" > "Logic App"
  3. Browse to select your Logic App and enter "manual/paths/invoke" for the API URL suffix - Presse Create
  4. Select your newly created API definition and you POST "manual-invoke" to refresh the API policies - Then click on "</>" to open the advanced policy editor
  5. Delete the <rewrite-uri id="apim-generated-policy" ...> rule
  6. Add the following rule in the outbound policies section under <base />: <choose><when condition="@(context.Response.StatusCode == 202)"><set-status code="200" reason="OK" /></when></choose>
  7. Save the new policy definition
Aucun texte alternatif pour cette image

Last, enter the new webhook URL into the O365 connector in Jenkins

  1. Go to your Azure Logic App, edit your workflow and copy the value of the HTTP Post URL it will be something like https://webhook-jenkins.francecentral.logic.azure.com:443/workflows/XXX/triggers/manual/paths/invoke?api-version=2016-10-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=f2QjZY50uoRnX6PIpyPT3xk
  2. Go to your APIM and copy the Gateway URL - e.g. https://webhook-jenkins.azure-api.net
  3. Create the new webhook from these 2 URL - It should look like that: https://webhook-jenkins.azure-api.net/manual/paths/invoke?api-version=2016-10-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=f2QjZY50uoRnX6PIpyPT3xk

The result !

If everything has been properly configured, you should receive and adaptive card with this format in your team channel.

Aucun texte alternatif pour cette image

As a comparison, here is the original message using the Jenkins connector (incoming weghook) for Microsoft Teams.

Aucun texte alternatif pour cette image

Hope this article was useful and demonstrated once again how flexible the Microsoft platform can be when you combine the different cloud solutions.

Here is the JSON definition of the Adaptive Card pushed to Teams via the Logic App connector:

{

  "type": "AdaptiveCard",

  "$schema": "https://adaptivecards.io/schemas/adaptive-card.json",

  "version": "1.2",

  "body": [

    {

      "type": "TextBlock",

      "text": "@{body('Parse_JSON')?['sections']?[0]?['activityTitle']}",

      "wrap": true,

      "size": "large"

    },

{

   "type": "ColumnSet",

   "columns": [

    {

     "type": "Column",

     "width" : "250px",

     "items": [

      {

        "type": "TextBlock",

        "text": "@{body('Parse_JSON')?['sections']?[0]?['activitySubtitle']}",

        "wrap": false,

        "isSubtle": true

      },

      {

        "type": "FactSet",

        "facts": [

          {

            "title": "Status",

            "value": "@{body('Parse_JSON')?['sections']?[0]?['facts']?[0]?['value']}"

          },

          {

            "title": "Remarks",

            "value": "@{body('Parse_JSON')?['sections']?[0]?['facts']?[1]?['value']}"

          }

        ]

      }

     ]

    },

    {

     "type": "Column",

     "items": [

      {

       "type": "Image",

       "url": "https://www.smile-suisse.com/app/uploads/2019/05/logo_techno_jenkins.png",

       "height" : "90px",

       "horizontalAlignment": "center"

      }

     ]

    }

   ]

  }

 ],

  "actions": [

  {

   "type": "Action.OpenUrl",

   "title": "View build",

   "url": "@{variables('Build URL')}"

  }

 ]

}

Lev P.

Azure DevOps Engineer | Automation & Infrastructure

1 年
回复
Vijay Daniel Y

Senior Security Architect

2 年

thanks for sharing, this was helpful.

要查看或添加评论,请登录

Alexis Kinzelin的更多文章

社区洞察

其他会员也浏览了