Operations Task Management Feedback Loop
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
- Create a webhook that will close/dismiss the Azure alert/incident as you resolve the work item (State = Resolved/Closed), and
- Reactivate the Azure alert/incident when the work item reopens (State != Resolved/Closed)
This article is also posted in the Azure Architecture Blog.
Solution Idea
Here’s the high level diagram for this solution, see here for the logic app code.
flowchart TD
A[Work Item Updated] -- webhook --> B[Logic App]
B --> C{Did the state change?}
C -- Yes --> D{Is State=Resolved?}
C -- No --> E[Do nothing]
D -- Yes --> F{Check Source}
D -- No --> G{Check if reopened}
F -- Sentinel Incident --> H[Close Incident]
F -- Sentinel Alert --> I[Do nothing*]
F -- Defender Alert --> J[Dismiss Alert]
F -- Defender Recommendation --> I[Do nothing*]
F -- Defender Security Compliance --> I[Do nothing*]
G -- Yes --> K{Check Source}
G -- No --> L[Do nothing]
K -- Sentinel Incident --> M[Reactivate]
K -- Defender Alert --> M
K -- Others --> L
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.
- Create a blank Logic App with an HTTP request trigger. (Take note of this URL for use in Azure DevOps)
- Change the state of a work item to trigger the webhook.
- Capture the payload sent by this webhook
- Remove properties that are not needed
This eventually led to this schema:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
{
"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.
The classification reason is an expression where I assume that if the State=Closed
, then it is a true positive. Otherwise, it’s undetermined.
1
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.
After closing a workitem, the result will look like this:
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:
- Dismissing alerts: https://docs.microsoft.com/en-us/rest/api/securitycenter/alerts/update-subscription-level-state-to-dismiss
- Reactivating alerts: https://docs.microsoft.com/en-us/rest/api/securitycenter/alerts/update-subscription-level-state-to-activate
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.
After closing a workitem, the result will look like this:
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. For reference, here is a screenshot of the list of possible triggers.
Here are some screenshots of how it is configured:
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.