Loading...

Operations Task Management Feedback Loop

Operations Task Management Feedback Loop

Background

Last month, I wrote about how work items in Azure DevOps can be automatically created as you receive alerts from Azure (Microsoft Sentinel Alerts and Incidents & Microsoft Defender for Cloud Alerts, Recommendations and Compliance Assessments).

In this post, I'm sharing how you can

  1. Create a webhook that will close/dismiss the Azure alert/incident as you resolve the work item (State = Resolved/Closed), and
  2. Reactivate the Azure alert/incident when the work item reopens (State != Resolved/Closed)

Solution Idea

Here's the high level diagram for this solution, see here for the logic app code.

raffertyuy_0-1652431045117.png

 

Note: The following alert types do not need to be dismissed

  • Sentinel Alerts
    • Alerts that are associated to incidents will be dismissed together with the incident
    • Alerts that are unassociated to incidents will age out according to the workspace retention policy
  • Defender Recommendation
    • The recommendation list will update as actions are made.
  • Defender Security Compliance
    • The security compliance assessment will update as actions are made.

Logic App Playbook

The above diagram can be implemented in a number of ways. In this post, I'm sharing how this was implemented using Azure Logic Apps. Azure Logic Apps is an Azure service that enables engineers to develop automated workflows and integrations using low-code/no-code rapidly.

If you are simply interested in deploying this logic app, you may head to this GitHub Repo, deploy from there, and skip to this article's next section. Otherwise, here are the details of how this was implemented.

Webhook Payload

After receiving the HTTP request, the first step is to parse the HTTP body using the Parse JSON action. Here are the steps I took to arrive at the below schema.

  1. Create a blank Logic App with an HTTP request trigger. (Take note of this URL for use in Azure DevOps)
  2. Change the state of a work item to trigger the webhook.
  3. Capture the payload sent by this webhook
  4. Remove properties that are not needed

This eventually led to this schema:

{
    "properties": {
        "detailedMessage": {
            "properties": {
                "html": {
                    "type": "string"
                },
                "markdown": {
                    "type": "string"
                },
                "text": {
                    "type": "string"
                }
            },
            "type": "object"
        },
        "eventType": {
            "type": "string"
        },
        "id": {
            "type": "string"
        },
        "message": {
            "properties": {
                "html": {
                    "type": "string"
                },
                "markdown": {
                    "type": "string"
                },
                "text": {
                    "type": "string"
                }
            },
            "type": "object"
        },
        "notificationId": {
            "type": "integer"
        },
        "publisherId": {
            "type": "string"
        },
        "resource": {
            "properties": {
                "fields": {
                    "properties": {
                        "System.State": {
                            "properties": {
                                "newValue": {
                                    "type": "string"
                                },
                                "oldValue": {
                                    "type": "string"
                                }
                            },
                            "type": "object"
                        }
                    },
                    "type": "object"
                },
                "id": {
                    "type": "integer"
                },
                "revision": {
                    "properties": {
                        "fields": {
                            "properties": {
                                "Custom.ReferenceLink": {
                                    "type": "string"
                                },
                                "Custom.Source": {
                                    "type": "string"
                                },
                                "Custom.SourceID": {
                                    "type": "string"
                                },
                                "Custom.SourceType": {
                                    "type": "string"
                                },
                                "Custom.SubscriptionId": {
                                    "type": "string"
                                },
                                "System.ChangedBy": {
                                    "type": "string"
                                },
                                "System.Description": {
                                    "type": "string"
                                },
                                "System.Reason": {
                                    "type": "string"
                                },
                                "System.State": {
                                    "type": "string"
                                },
                                "System.WorkItemType": {
                                    "type": "string"
                                }
                            },
                            "type": "object"
                        },
                        "id": {
                            "type": "integer"
                        },
                        "rev": {
                            "type": "integer"
                        }
                    },
                    "type": "object"
                },
                "workItemId": {
                    "type": "integer"
                }
            },
            "type": "object"
        },
        "resourceContainers": {
            "properties": {
                "project": {
                    "properties": {
                        "baseUrl": {
                            "type": "string"
                        },
                        "id": {
                            "type": "string"
                        }
                    },
                    "type": "object"
                }
            },
            "type": "object"
        },
        "subscriptionId": {
            "type": "string"
        }
    },
    "type": "object"
}

The main properties that are used are:

  • properties-->resource-->workItemId
  • properties-->resource->fields-->properties-->System.State
  • properties-->resource-->revision-->properties-->fields-->properties-->
    • Custom.Source
    • Custom.SourceType
    • Custom.SourceID
    • Custom.SubscriptionId
    • System.State
    • System.ChangedBy
  • properties-->resourceContainers-->project

Feedback loop for Sentinel Incidents

To close/reopen Sentinel Incidents, we are using the Update Incident action of the Microsoft Sentinel Logic App connector.

raffertyuy_1-1651826881762.png

 

The classification reason is an expression where I assume that if the State=Closed, then it is a true positive. Otherwise, it's undetermined.

if(equals(body('Parse_HTTP_request_JSON_body')?['resource']?['revision']?['fields']?['System.State'],'Closed'),'TruePositive - SuspiciousActivity','Undetermined')

Additionally, we can add a comment to the incident using the Add Comment to Incident) action.

raffertyuy_2-1651826911682.png

After closing a workitem, the result will look like this:

raffertyuy_3-1651826931646.png

Feedback loop for Defender Alerts

To dismiss/reactivate Defender Alerts, we are using the HTTP action of the HTTP connector. This is mainly because there is no Microsoft Defender logic app action that we can use directly at the time of this article.

The REST APIs that are relevant to our use case are as follows:

Before calling these APIs, please make sure that:

  • The Logic App identity is configured to have a Managed Identity.
  • The managed identity is given Security Admin permissions at the subscription level. See here.

raffertyuy_4-1651826953281.png

After closing a workitem, the result will look like this:

raffertyuy_5-1651826969177.png

Note: For reactivating alerts, call the /activate API instead.

Azure DevOps Webhooks

Webhooks for work item changes are triggered by configuring Service Hooks in the Azure DevOps --> Project --> Settings.

Since we are only interested in state changes, we are using the trigger Work item updated on State change. This will call the logic app REST API when a work item is updated, using the URL that was generated from the logic app above.

Here are some screenshots of how it is configured:

raffertyuy_6-1651827018383.png

raffertyuy_7-1651827029910.png

Tip: When you are making updates to the custom process that will affect the payload schema, you will have to Edit/Save the this configuration for Azure DevOps to recognize the changes and send an updated payload.

Conclusion

In summary, using Azure Logic Apps and Azure DevOps, we have achieved a way for the operations team to more effectively track and task manage alerts coming from Microsoft Sentinel and Microsoft Defender for Cloud using Azure Boards.

  • In my last post, I shared how we can automate the creation of Azure DevOps work items from Azure
  • In this post, I shared how to resolve/dismiss alerts as work item states are resolved.

Published on:

Learn more
Azure Architecture Blog articles
Azure Architecture Blog articles

Azure Architecture Blog articles

Share post:

Related posts

Stay up to date with latest Microsoft Dynamics 365 and Power Platform news!
* Yes, I agree to the privacy policy