The standard Microsoft Teams Incoming Webhook connector only accepts simple text payloads and doesn't provide much flexibility for JSON. For more complex scenarios, you can build a custom solution using a middleware (e.g., Azure Functions).
This Azure Function handles incoming HTTP POST requests containing JSON security alerts, extracts key fields, and sends formatted alert messages to a Microsoft Teams channel via an incoming webhook.
By deploying Azure Function as a middleware, you will be able to integrate Calico Webhook with Microsoft Teams Incoming Webhook!
- Extracts key information from the JSON payload (timestamp, IP addresses, rules violated).
- Formats the data in Markdown for easy reading in Microsoft Teams.
- Posts the message using a Microsoft Teams webhook URL.
- Visual Studio Code
- .NET 6.0 (or higher)
- Azure Function Tools
- Microsoft Teams Incoming Webhook
- Calico Enterprise or Calico Cloud
- Set up your Microsoft Teams webhook URL.
- Create the Azure Function.
- Test the Azure Function locally.
- Deploy the Azure Function.
- Test the Azure Function in Azure.
- Test the Azure Function integration with Calico Cloud Webhook.
- Go to Microsoft Teams.
- Navigate to the channel where you want to post the messages.
- Click on the three dots (more options) next to the channel name and select Connectors.
- Search for Incoming Webhook, add it, and configure the webhook name and icon.
- Once done, copy the Webhook URL — you'll need this to send messages to Teams.
For more information please visit and review the official documentation here.
-
Create a Local Azure Function Project :
- Open Visual Studio Code .
- Open the Command Palette (
Cmd+Shift+P
) and typeAzure Functions: Create New Project
. - Select the folder where you want to create the project.
- Select the following settings:
- Language : C#
- .NET Runtime : .NET 6.0 LTS
- Template : HTTP trigger
- Function name : json-webhook-middleware
- Namespace : Company.Function
- Authorization Level : Function (so it's secured with a function key)
- Open project " Open in new window
-
Replace the Template Code with the script below :
- Once the project is created, you'll see a generated file for the HTTP trigger. Replace the default code inside the generated function file (e.g.,
Function1.cs
) with the script below. IMPORTANT: ReplaceREPLACE_ME_WITH_MS_TEAMS_WEBHOOK_URL
with the actual Microsoft Teams URL you saved in a previous step - Ensure that the file is renamed to something appropriate, like
JsonWebhookFunction.cs
. - Ensure the file is saved.
- Once the project is created, you'll see a generated file for the HTTP trigger. Replace the default code inside the generated function file (e.g.,
-
Install Dependencies :
- The function uses Newtonsoft.Json for JSON serialization/deserialization, so you'll need to install this package:
- Open a terminal in VS Code (
Ctrl+
or View > Terminal
). - Run the following command to install Newtonsoft.Json :
dotnet add package Newtonsoft.Json
- This will update your
.csproj
file with the necessary dependency.
- This will update your
- Open a terminal in VS Code (
- The function uses Newtonsoft.Json for JSON serialization/deserialization, so you'll need to install this package:
- Before deploying, test your function locally.
- In the terminal, run:
func start
* This will start your Azure Function locally. It should output a URL like `http://localhost:7071/api/JsonWebhookFunction`.
- Use
curl
on another shell or a tool like Postman to test your function by sending a POST request with a JSON body. Example:curl -X POST http://localhost:7071/api/JsonWebhookFunction --data {}
- You should have received a message in the Microsoft Teams channel where the Incoming Webhook is connected, similar to this:
- In the Command Palette (
Cmd+Shift+P
), typeAzure Functions: Deploy to Function App
. - If you haven’t created a Function App in Azure yet, the deployment process will ask you to create one:
- Subscription : Choose your Azure subscription.
- Function app : Create a new function app.
- Name : Provide a unique name for your function app. Example
json-adaptor
. - Runtime Stack : Select .NET 8.0 LTS In-process
- Location : Choose a nearby region.
- Once the app is created, VS Code will deploy your function code to Azure.
- After deployment, you can test the live function by sending a POST request to the Azure URL. To find your function's URL:
- Go to the Azure tab in VS Code.
- Find your Function App, right-click, and select Copy Function URL .
- Use this URL to send a POST request, similar to your local test. Example:
curl -X POST https://REPLACE_THIS_WITH_YOURS --data {}
- Go to Calico Cloud/Enterprise UI > Activity > Webhooks and create a new webhook with the Function URL that you got before.
- WAF TEST : You can follow these steps to test WAF with a sample application.
- After a few seconds from sending the SQL Injection command, you should receive a post in Microsoft Teams, similar to this:
Congratulations! You have tested and implemented Calico Webhook integration with Microsoft Teams Incoming Webhook.
NOTE: Replace REPLACE_ME_WITH_MS_TEAMS_WEBHOOK_URL
with the actual Microsoft Teams URL you saved in a previous step.
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
public static class JsonWebhookFunction
{
[FunctionName("JsonWebhookFunction")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
ILogger log)
{
log.LogInformation("Incoming JSON webhook payload.");
// Read the incoming JSON payload
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
// Extract and format key fields from the JSON
var timestamp = data?.record?["@timestamp"] != null ? DateTime.Parse(data.record["@timestamp"].ToString()).ToString("yyyy-MM-dd HH:mm:ss") : "N/A";
var requestId = data?.record?.request_id ?? "N/A";
var message = data?.record?.msg ?? "N/A";
var sourceIp = data?.record?.source?.ip ?? "N/A";
var sourcePort = data?.record?.source?.port_num ?? "N/A";
var destinationIp = data?.record?.destination?.ip ?? "N/A";
var destinationPort = data?.record?.destination?.port_num ?? "N/A";
var path = data?.record?.path ?? "N/A";
// Safely cast dynamic to list for rules
List<dynamic> rulesList = data?.record?.rules != null ? data.record.rules.ToObject<List<dynamic>>() : new List<dynamic>();
// Handle the list of rules
var rules = rulesList.Any()
? string.Join("\n", rulesList.Select(rule =>
$" - Rule {rule.id}: {rule.message} (Severity: {rule.severity})"))
: "No rules violated.";
// Format the message using Markdown for Microsoft Teams
var formattedMessage = new
{
text = $"### **🚨 Security Alert: {message}**\n\n" +
$"**Timestamp**: {timestamp}\n" +
$"**Request ID**: {requestId}\n" +
$"**Source IP/Port**: {sourceIp}:{sourcePort}\n" +
$"**Destination IP/Port**: {destinationIp}:{destinationPort}\n" +
$"**Rules Violated**: \n{rules}\n\n" +
$"**Path**: {path}\n" +
$"---\n\n" +
$"**Full JSON Payload**: \n```json\n{JsonConvert.SerializeObject(data, Formatting.Indented)}\n```"
};
// Send the message to Microsoft Teams Incoming Webhook URL
string teamsWebhookUrl = "REPLACE_ME_WITH_MS_TEAMS_WEBHOOK_URL";
var client = new HttpClient();
var jsonContent = new StringContent(JsonConvert.SerializeObject(formattedMessage), Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync(teamsWebhookUrl, jsonContent);
if (response.IsSuccessStatusCode)
{
return new OkObjectResult("Message posted to Teams");
}
else
{
return new BadRequestObjectResult($"Failed to post message to Teams. Status code: {response.StatusCode}");
}
}
}
-
Import Statements:
-
The script imports several namespaces necessary for handling HTTP requests, JSON manipulation, and logging:
System.IO
,System.Net.Http
,System.Text
, etc., provide access to file reading, HTTP communication, and string encoding.Microsoft.AspNetCore.Mvc
,Microsoft.Azure.WebJobs
,Microsoft.Extensions.Logging
support Azure Function definitions and logging.Newtonsoft.Json
is used for JSON serialization and deserialization.
-
-
Function Declaration:
- The static class
JsonWebhookFunction
is defined, containing the Azure function logic. - The method
Run
is decorated with the[FunctionName("JsonWebhookFunction")]
attribute, which names the function.
- The static class
-
HTTP Trigger Setup:
- The function uses an HTTP trigger (
HttpTrigger
) with aPOST
method andFunction
authorization level. This means the function listens for incoming HTTP POST requests.
- The function uses an HTTP trigger (
-
Logging Incoming Payload:
- The
ILogger log
object is used to log a message indicating that an incoming JSON payload has been received.
- The
-
Read and Deserialize JSON Payload:
- The incoming JSON body is read from the HTTP request using a
StreamReader
and stored in therequestBody
. - The
requestBody
is deserialized into a dynamic object (data
) usingJsonConvert.DeserializeObject
, allowing flexible access to the JSON properties.
- The incoming JSON body is read from the HTTP request using a
-
Extract Key Fields from JSON:
- Several key fields are extracted from the
data
object:- Timestamp: Extracts the timestamp and formats it as a
DateTime
string. - Request ID, Message, Source/Destination IPs and Ports, Path: These fields are extracted and default to
"N/A"
if missing from the JSON. - Rules List: Extracts the list of rules violated (if present), and converts it into a list of dynamic objects.
- Timestamp: Extracts the timestamp and formats it as a
- Several key fields are extracted from the
-
Handle Rules List:
- If any rules are found in the JSON payload, each rule's
id
,message
, andseverity
are formatted into a string. If no rules are present, the output defaults to"No rules violated."
.
- If any rules are found in the JSON payload, each rule's
-
Format Message for Microsoft Teams:
- A message is constructed in Markdown format to be sent to Microsoft Teams. The message contains:
- A security alert with the extracted details (e.g., timestamp, request ID, source/destination IP and ports).
- The list of violated rules (if any).
- The full JSON payload is embedded as a code block.
- A message is constructed in Markdown format to be sent to Microsoft Teams. The message contains:
-
Send Message to Microsoft Teams Webhook:
- The script sends the formatted message to a pre-configured Microsoft Teams incoming webhook URL (
teamsWebhookUrl
). - A
HttpClient
instance is used to make aPOST
request with the formatted message as a JSON payload.
- The script sends the formatted message to a pre-configured Microsoft Teams incoming webhook URL (
-
Handle Response:
- If the
POST
request to Teams is successful (i.e., the webhook URL responds with a success status code), the function returns anOkObjectResult
with a success message. - If the request fails, a
BadRequestObjectResult
is returned with the error status code.
- If the