Send Azure Management Events to a Teams Channel

This post has been republished via RSS; it originally appeared at: Azure Developer Community Blog articles.

For my example I am going to use Event Grid and Logic Apps. We will post a message using flow bot with adaptive cards as well as a Teams webhook sending a message card.  Yes message cards are a thing of the past, however the Teams Webhook api doesn't yet support posting with adaptive cards.

 

Create the Resource Group

az group create --location westus --name logic_app_posting


Create the Logic App

CLI is not yet a option for logic app creation, which means we must go to the portal or use PS.


Create the Trigger in the logic app

  1. Open the newly create logic app via the portal in Designer mode.
  2. Search for Azure Event Grid and select When a resource event occurs (preview)
  3. Subscription - Choose your target subscription
  4. Resource Type - Microsoft.Resources.Subscriptions (this will target any/all resources in the entire subscription
  5. Resource Name - Choose target subscription that you chose in Step 1.
  6. Save

Adding this trigger will create an Event Grid Subscription which is viewable via this link

https://portal.azure.com/#resource/subscriptions/<subscription_guid>/events

Azure subscriptions emit management events from Azure Resource Manager, such as when a VM is created or a storage account is deleted.

 

Filtering…. with Events you can use Event Filters to filter out events which you don't care about. In the below example I am going to use Logic Apps to filter the events but in general it makes sense to filter them in Event Grid so that you're not charged downstream.

Step 1. Add a Parse JSON action under Data Operations after the Event Grid Resource event occurs trigger. For the schema, I used a previous payload to generate the schema.

  • Content - Select Body from Dynamic Content.
  • Ensure that the this action is named "Parse JSON"
  • Schema:
{
    "properties": {
        "data": {
            "properties": {
                "authorization": {
                    "properties": {
                        "action": {
                            "type": "string"
                        },
                        "evidence": {
                            "properties": {
                                "role": {
                                    "type": "string"
                                }
                            },
                            "type": "object"
                        },
                        "scope": {
                            "type": "string"
                        }
                    },
                    "type": "object"
                },
                "claims": {
                    "properties": {
                        "aio": {
                            "type": "string"
                        },
                        "appid": {
                            "type": "string"
                        },
                        "appidacr": {
                            "type": "string"
                        },
                        "aud": {
                            "type": "string"
                        },
                        "exp": {
                            "type": "string"
                        },
                        "groups": {
                            "type": "string"
                        },
                        "iat": {
                            "type": "string"
                        },
                        "ipaddr": {
                            "type": "string"
                        },
                        "iss": {
                            "type": "string"
                        },
                        "name": {
                            "type": "string"
                        },
                        "nbf": {
                            "type": "string"
                        },
                        "onprem_sid": {
                            "type": "string"
                        },
                        "puid": {
                            "type": "string"
                        },
                        "uti": {
                            "type": "string"
                        },
                        "ver": {
                            "type": "string"
                        }
                    },
                    "type": "object"
                },
                "correlationId": {
                    "type": "string"
                },
                "operationName": {
                    "type": "string"
                },
                "resourceProvider": {
                    "type": "string"
                },
                "resourceUri": {
                    "type": "string"
                },
                "status": {
                    "type": "string"
                },
                "subscriptionId": {
                    "type": "string"
                },
                "tenantId": {
                    "type": "string"
                }
            },
            "type": "object"
        },
        "dataVersion": {
            "type": "string"
        },
        "eventTime": {
            "type": "string"
        },
        "eventType": {
            "type": "string"
        },
        "id": {
            "type": "string"
        },
        "metadataVersion": {
            "type": "string"
        },
        "subject": {
            "type": "string"
        },
        "topic": {
            "type": "string"
        }
    },
    "type": "object"
}


0.png

 

Step 2. Add a Initialize Variable action under Variables after the Parse JSON action. We will be taking the Subject field and splitting it by "/"

  • Name - ResourceIDArray
  • Type - Array
  • Value - Click in the field and then click on expression and paste the following... If Step 1 action is not named correctly than Parse_JSON will not work in this expression.
split(body('Parse_JSON')?['subject'],'/')

0.png

 

 

Step 3. Add a Condition action under Control. We will add 4 conditions which will filter out events which are policy only as well as events which aren't triggered by a human.  You should evaluate what types of events you are targeting and adjust this action.

 

0.png

 

0.png

 

 

Two types of Actions can be added at Step 4.

  1. Teams - Posting using Adaptive Card
  2. Teams - Posting using Message Card


Teams - Adaptive Card

In the True part of the condition we need to Add the Teams Integration. Search for Microsoft Teams and select Post your own adaptive card as Flow bot to a channel (preview).

Click Sign in and log in with the identity which also has access to the Teams channel you posting to.

  • Team - Pick the Team you want to post a message.
  • Channel - Choose the channel
  • Message - The message I built is below, but if you want to build your own you can use https://acdesignerbeta.azurewebsites.net/ to design your own.  Make sure you update the image_url to be valid.
{
  "type": "AdaptiveCard",
  "body": [
    {
      "type": "Container",
      "items": [
        {
          "type": "TextBlock",
          "size": "Medium",
          "weight": "Bolder",
          "text": "@{body('Parse_JSON')?['data']?['claims']?['name']}"
        },
        {
          "type": "ColumnSet",
          "columns": [
            {
              "type": "Column",
              "items": [
                {
                  "type": "Image",
                  "style": "Person",
                  "url": "<logo_url>",
                  "size": "Small"
                }
              ],
              "width": "auto"
            },
            {
              "type": "Column",
              "items": [
                {
                  "type": "TextBlock",
                  "spacing": "None",
                  "text": "@{formatDateTime(body('Parse_JSON')?['eventTime'], 'G' )}",
                  "isSubtle": true,
                  "wrap": true
                }
              ],
              "width": "stretch"
            }
          ]
        }
      ]
    },
    {
      "type": "Container",
      "items": [
        {
          "type": "FactSet",
          "spacing": "Large",
          "facts": [
            {
              "title": "Resource Group",
              "value": "@{variables('ResourceIDArray')[4]}"
            },
            {
              "title": "Resource Name",
              "value": "@{variables('ResourceIDArray')[sub(length(variables('ResourceIDArray')), 1)]}"
            },
            {
              "title": "Resource Type:",
              "value": "@{variables('ResourceIDArray')[sub(length(variables('ResourceIDArray')), 2)]}"
            },
            {
              "title": "IP:",
              "value": "@{body('Parse_JSON')?['data']?['claims']?['ipaddr']}"
            },
            {
              "title": "Event Type:",
              "value": "@{body('Parse_JSON')?['eventType']}"
            }
          ]
        }
      ]
    },
    {
      "type": "TextBlock",
      "size": "Small",
      "text": "@{body('Parse_JSON')?['data']?['operationName']}",
      "isSubtle": true
    }
  ],
  "actions": [
    {
      "type": "Action.OpenUrl",
      "title": "View",
      "url": "https://ms.portal.azure.com/#resource@{body('Parse_JSON')?['data']?['resourceUri']}"
    }
  ],
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "version": "1.0"
}

 

Click Save. The act of saving the template will generate a management event which should post to your teams channel. Hooray!

 

Example Message:

 

0.png

 

Teams - Message Card

In the regular action Post a Teams message doesn't currently support Message Cards which means we need to use a Teams WebHook. 

On the teams channel click on Connectors and search for Incoming Webhook, click configure. Give the webhook a name, change the image and click create to view the url. Save the url for later use.

Back to Logic App designer to add a Http Action.

  • Method - Post
  • URI - Webhook URI from above
  • Headers - Content | application/json
  • Body -
    {
      "sections": [
        {
          "facts": [
            {
              "name": "Resource Group",
              "value": "@{variables('ResourceIDArray')[4]}"
            },
            {
              "name": "Resource Name",
              "value": "@{variables('ResourceIDArray')[sub(length(variables('ResourceIDArray')), 1)]}"
            },
            {
              "name": "Resource Type:",
              "value": "@{variables('ResourceIDArray')[sub(length(variables('ResourceIDArray')), 2)]}"
            },
            {
              "name": "IP:",
              "value": "@{body('Parse_JSON')?['data']?['claims']?['ipaddr']}"
            },
            {
              "name": "Event Type:",
              "value": "@{body('Parse_JSON')?['eventType']}"
            },
            {
              "name": "Operation:",
              "value": "@{body('Parse_JSON')?['data']?['operationName']}"
            },
            {
              "name": "Resource URL:",
              "value": "https://ms.portal.azure.com/#resource@{body('Parse_JSON')?['data']?['resourceUri']}"
            }
          ],
          "title": "@{formatDateTime(body('Parse_JSON')?['eventTime'], 'G' )}:"
        }
      ],
      "summary": "@{body('Parse_JSON')?['data']?['claims']?['name']}"
    }

 

0.png

 

Example Message:

 

0.png

 

Leave a Reply

Your email address will not be published. Required fields are marked *

*

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