Creating lightweight Teams bots with outgoing webhooks and Azure Functions

Microsoft Teams is becoming always more a central point for the everyday’s work on many of us now and often having Bots on a Teams channel that can solve our queries could be helpful.

Teams bots are “automations” that allows you to interact with conversations and they can perform tasks or call external services for you.

As a preface to this post (for all the Dynamics 365 Business Central readers), remember that Microsoft has plans to integrate Dynamics 365 Business Central data into Microsoft Teams conversations (and they’re doing a good job), so stay tuned for a first public preview on October ’20. This is just another possible way of doing so, maybe with more freedom 🙂 For reference, I’ve written in the past also a post on how to send a message from AL code to Microsoft Teams here.

Now… Imagine just to write on your Teams channel something like “Hey BOT, how is the status of my Sales Order SO123456?” and then receiving an answer like “The Sales Order SO123456 is completely shipped” after a query on your Dynamics 365 Business Central ERP? Or something like “Hey BOT, can I ship to Customer XXX?“. Answer: “No, the Financial Department has blocked that customer for shipping.“. How cool is that?

You can create Bots for Microsoft Teams by using the Microsoft Bot Framework and this is the recommended way if you want the full power. But there’s also a quite poor documented way for creating “lightweight bots” for Microsoft Teams by using outgoing webhooks and Azure Functions.

In Teams, outgoing webhooks provide a simple way to allow users to send messages to your web service without having to go through the full process of creating bots via the Microsoft Bot Framework. Outgoing webhooks post data from Teams to any chosen service capable of accepting a JSON payload via a POST request. Once an outgoing webhook is added to a team, it acts like a bot, listening in channels for messages using @mention, sending notifications to external web services and responding with rich messages that can include text, cards and images.

So, simply speaking, an outgoing webhook is essentially a POST request sent to a callback URL. This callback URL (your service) will receive messages in the standard Azure bot service messaging schema.

An example of the JSON of a message sent from a Teams outgoing webhook is the following:

{
“type”: “message”,
“id”: “bf3cc9a2f5de…”,
“timestamp”: “2016-10-19T20:17:52.2891902Z”,
“serviceUrl”: “https://smba.trafficmanager.net/apis”,
“channelId”: “channel’s name/id”,
“from”: {
“id”: “1234abcd”,
“name”: “user’s name”
},
“conversation”: {
“id”: “abcd1234”,
“name”: “conversation’s name”
},
“recipient”: {
“id”: “12345678”,
“name”: “bot’s name”
},
“text”: “Haircut on Saturday”
}

Saying that, how can we create a Teams bot by using outgoing webhooks?

We need essentially to perform two main tasks:

  1. Create an Azure Function (HTTP Trigger) that receives a POST request from the outgoing webhooks in Teams and do what you want for your business (by handling the received message from the Teams channel)
  2. Create an outgoing webhook on your Teams channel.

The Azure Function that I’m creating here is a simple HTTPTrigger function:

public static class TeamsOutgoingWebhookFunction
    {
        [FunctionName("TeamsBot")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");
            log.LogInformation(req.Headers["Authorization"]);

            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic data = JsonConvert.DeserializeObject(requestBody);

            return (ActionResult)new OkObjectResult(
                new {
                    type = "message",
                    text = String.Format("Hi {0}. I'm your Azure Functions Teams Bot. You have written: {1}", (String)data?.from?.name, (String)data?.text)
                    }
                );
        }
    }

Here I’m simply reading the request body (JSON sent from the outgoing webhook) and then I return then name of the sender and the text of the conversation. Obviously, you can do more than this here: you can parse the received message and do what you want (for example, calling Dynamics 365 Business Central APIs for returning data as in my initial image on this post).

When the Azure Function is published, you need to retrieve the function url from the Azure Portal, because it will be used for creating the outgoing webhook in Teams.

Now, open Teams, go to your channel, select Manage Teams, click on Apps and then select the Create an outgoing webhook link at the bottom of the screen:

Now give it a name (here called TestOutgoingWebhook) and in the Callback URL field insert the url of your Azure Functions:

You can also upload a picture that will be used as the image for your bot. Please remember that the name inserted here will be the name that you will use for calling your bot.

Click Create to create the outgoing webhook.

When the webhook is created, you will have a security token that you need to copy:

To ensure that your service is receiving calls only from actual Teams clients, Teams provides an HMAC Code in the HTTP hmac header that should always be included in your authentication protocol.

Your code should always validate the HMAC signature included in the request:

  • Generate the HMAC token from the request body of the message. There are standard libraries to do this on most platforms (see Crypto for Node.js or see Teams Webhook Sample for C#). Microsoft Teams uses standard SHA256 HMAC cryptography . You will need to convert the body to a byte array in UTF8.
  • Compute the hash from the byte array of the security token provided by Teams when you registered the outgoing webhook in the Teams client]. See Create an outgoing webhook, below.
  • Convert the hash to a string using UTF-8 encoding.
  • Compare the string value of the generated hash with the value provided in the HTTP request.

I will avoid this extra security layer here.

That’s all!

What happens now on our Teams channel? We have a new bot that we can call simply by using a mention like @TestOutgoingWebhook:

The Azure Function is triggered, your code is executed in the cloud and a response is sent back to Microsoft Teams:

Please remember that webhooks are scoped at the team level. You’ll need to go through the setup process for each team you want to add your outgoing webhook to.

As said before, you can do more than this. A tipical example is to parse the text message for keywords and then for example calling the Dynamics 365 Business Central APIs for retrieving data. You can then send back responses for your users in text format but also in more complex formats like adaptive cards. But why I love this approach too? Because you can have a set of Azure Functions that handles your integrations (with Dynamics 365 Business Central or with other third-party apps) and these functions can be completely aware of the caller (Teams or other apps). Just create a single function that handles the in and out messages with Teams and your logic for integrating the external apps will be untouched (no integration logic inside the bot).

Quite cool and useful I think, isn’t it?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.