Azure-Sentinel/Workbooks/AnalyticsEfficiency.json

1257 строки
72 KiB
JSON

{
"version": "Notebook/1.0",
"items": [
{
"type": 1,
"content": {
"json": "# Analytics Efficiency (Preview)"
},
"customWidth": "35",
"name": "Page header"
},
{
"type": 9,
"content": {
"version": "KqlParameterItem/1.0",
"parameters": [
{
"id": "26d2fea7-3646-4993-b79f-6722f9ef8ddb",
"version": "KqlParameterItem/1.0",
"name": "DefaultSubscription_Internal",
"type": 1,
"isRequired": true,
"query": "where type =~ 'microsoft.operationalinsights/workspaces'\n| take 1\n| project subscriptionId",
"isHiddenWhenLocked": true,
"timeContextFromParameter": "TimeRange",
"queryType": 1,
"resourceType": "microsoft.resourcegraph/resources"
},
{
"id": "109f8d4a-05e3-4d1b-adac-087873d109a6",
"version": "KqlParameterItem/1.0",
"name": "InternalWSs",
"type": 1,
"isRequired": true,
"query": "SecurityIncident\n| take 1\n| parse IncidentUrl with * \"/workspaces/\" Workspace \"/\" *\n| project Workspace",
"isHiddenWhenLocked": true,
"timeContext": {
"durationMs": 86400000
},
"queryType": 0,
"resourceType": "microsoft.operationalinsights/workspaces"
},
{
"id": "55d3ab63-6e1f-4d02-8d9e-2225526689c7",
"version": "KqlParameterItem/1.0",
"name": "Subscription",
"type": 6,
"query": "summarize by subscriptionId\n| project value = strcat(\"/subscriptions/\", subscriptionId), label = subscriptionId, selected = iff(subscriptionId =~ '{DefaultSubscription_Internal}', true, false)",
"crossComponentResources": [
"value::all"
],
"typeSettings": {
"additionalResourceOptions": [],
"showDefault": false
},
"timeContext": {
"durationMs": 0
},
"timeContextFromParameter": "TimeRange",
"queryType": 1,
"resourceType": "microsoft.resourcegraph/resources"
},
{
"id": "95a45501-31b5-4ea2-bcb3-eb208e0080e2",
"version": "KqlParameterItem/1.0",
"name": "Workspace",
"type": 5,
"isRequired": true,
"query": "where type =~ \"microsoft.operationalinsights/workspaces\"\r\n| project subscriptionId,id,name\r\n| where '{Subscription}' has subscriptionId\r\n| project value =id, label = name, selected = iff(name =~ '{InternalWSs}', true, false)",
"crossComponentResources": [
"value::all"
],
"typeSettings": {
"additionalResourceOptions": [],
"showDefault": false
},
"timeContextFromParameter": "TimeRange",
"queryType": 1,
"resourceType": "microsoft.resourcegraph/resources"
},
{
"id": "7d597ad7-4a2a-45ed-a4fe-7ee32de0fc22",
"version": "KqlParameterItem/1.0",
"name": "TimeRange",
"label": "Time Range",
"type": 4,
"isRequired": true,
"value": {
"durationMs": 2592000000
},
"typeSettings": {
"selectableValues": [
{
"durationMs": 14400000
},
{
"durationMs": 43200000
},
{
"durationMs": 86400000
},
{
"durationMs": 172800000
},
{
"durationMs": 259200000
},
{
"durationMs": 604800000
},
{
"durationMs": 1209600000
},
{
"durationMs": 2592000000
}
],
"allowCustom": true
}
},
{
"id": "9a199167-2dde-49dd-8f01-23e9d1fa8151",
"version": "KqlParameterItem/1.0",
"name": "InternalRG",
"type": 1,
"isRequired": true,
"query": "where type =~ \"microsoft.operationalinsights/workspaces\"\r\n| where id =~ \"{Workspace}\"\r\n| project resourceGroup",
"crossComponentResources": [
"{Workspace}"
],
"isHiddenWhenLocked": true,
"timeContext": {
"durationMs": 0
},
"timeContextFromParameter": "TimeRange",
"queryType": 1,
"resourceType": "microsoft.resourcegraph/resources"
},
{
"id": "c4470c37-5a8a-4ecd-8ece-5e98db8e8a92",
"version": "KqlParameterItem/1.0",
"name": "Help",
"label": "Show Help",
"type": 10,
"description": "This will show some help information to help you understand the page you are on",
"isRequired": true,
"typeSettings": {
"additionalResourceOptions": []
},
"jsonData": "[{ \"value\": \"Yes\", \"label\": \"Yes\"},\r\n {\"value\": \"No\", \"label\": \"No\", \"selected\":true }]"
}
],
"style": "pills",
"queryType": 1,
"resourceType": "microsoft.resourcegraph/resources"
},
"customWidth": "100",
"name": "WSSelector"
},
{
"type": 1,
"content": {
"json": "### Help\r\nSelect **Subscription**, **Workspace**, and **Time Range** from the drop-downs above.\r\nThe workbook will display analytics rules according to your selections.\r\n\r\nThe **Time range** filter refers to the time the rule was last modified, and applies to all components.",
"style": "info"
},
"conditionalVisibility": {
"parameterName": "Help",
"comparison": "isEqualTo",
"value": "Yes"
},
"name": "Help text"
},
{
"type": 11,
"content": {
"version": "LinkItem/1.0",
"style": "tabs",
"links": [
{
"id": "18c690d7-7cbd-46c1-b677-1f72692d40cd",
"cellValue": "TAB",
"linkTarget": "parameter",
"linkLabel": "Alert Rules",
"subTarget": "Rule",
"preText": "Alert rules",
"style": "link"
},
{
"id": "94d4ab9a-4ec7-4e4b-a69d-b6acb5cccd62",
"cellValue": "TAB",
"linkTarget": "parameter",
"linkLabel": "Alerts",
"subTarget": "Alert",
"style": "link"
},
{
"id": "19b985d3-9819-4d97-aeec-dfc558977b39",
"cellValue": "TAB",
"linkTarget": "parameter",
"linkLabel": "Incidents",
"subTarget": "Incident",
"style": "link"
}
]
},
"name": "Tabs link"
},
{
"type": 1,
"content": {
"json": "### Analytics rules\r\nThis table displays all the analytics rules in the chosen subscription, workspace, and time range. Select rules to analyze based on the selected tab. "
},
"name": "Heading-workbook"
},
{
"type": 1,
"content": {
"json": "### Help\r\nSelect rules from the table for analysis.\r\nThe workbook is updated dynamically as rules are selected or deselected.",
"style": "info"
},
"conditionalVisibility": {
"parameterName": "Help",
"comparison": "isEqualTo",
"value": "Yes"
},
"name": "Rules selection help text"
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "{\"version\":\"ARMEndpoint/1.0\",\"data\":null,\"headers\":[],\"method\":\"GET\",\"path\":\"/subscriptions/{Subscription:id}/resourceGroups/{InternalRG}/providers/Microsoft.OperationalInsights/workspaces/{Workspace:name}/providers/Microsoft.SecurityInsights/alertRules\",\"urlParams\":[{\"key\":\"api-version\",\"value\":\"2020-01-01\"}],\"batchDisabled\":false,\"transformers\":[{\"type\":\"jsonpath\",\"settings\":{\"tablePath\":\"$.value\",\"columns\":[{\"path\":\"$.properties.displayName\",\"columnid\":\"RuleName\"},{\"path\":\"$.properties.description\",\"columnid\":\"Description\"},{\"path\":\"$.name\",\"columnid\":\"AlertRuleID\"},{\"path\":\"$.kind\",\"columnid\":\"Kind\"},{\"path\":\"$.properties.productFilter\",\"columnid\":\"ProductName\"},{\"path\":\"$.properties.tactics\",\"columnid\":\"Tactics\"},{\"path\":\"$.properties.enabled\",\"columnid\":\"Status\"},{\"path\":\"$.properties\",\"columnid\":\"prop\"},{\"path\":\"$.properties.lastModifiedUtc\",\"columnid\":\"lastModifiedUtc\"}]}}]}",
"size": 0,
"noDataMessage": "No analytic rules are defined ",
"exportMultipleValues": true,
"exportedParameters": [
{
"fieldName": "AlertRuleID",
"parameterName": "AlertRuleID",
"parameterType": 1
},
{
"fieldName": "ProductName",
"parameterName": "ProductName",
"parameterType": 1
},
{
"fieldName": "Tactics",
"parameterName": "Tactics",
"parameterType": 1
},
{
"fieldName": "RuleName",
"parameterName": "RuleName",
"parameterType": 1
},
{
"fieldName": "Status",
"parameterName": "Status",
"parameterType": 1
},
{
"fieldName": "prop",
"parameterName": "prop",
"parameterType": 1,
"delimiter": ",",
"quote": ""
}
],
"exportToExcelOptions": "all",
"queryType": 12,
"gridSettings": {
"formatters": [
{
"columnMatch": "Kind",
"formatter": 1
},
{
"columnMatch": "ProductName",
"formatter": 5
},
{
"columnMatch": "Tactics",
"formatter": 5,
"formatOptions": {
"customColumnWidthSetting": "0ch"
}
},
{
"columnMatch": "Status",
"formatter": 18,
"formatOptions": {
"thresholdsOptions": "icons",
"thresholdsGrid": [
{
"operator": "==",
"thresholdValue": "1",
"representation": "success",
"text": "enabled"
},
{
"operator": "==",
"thresholdValue": "0",
"representation": "disabled",
"text": "disabled"
},
{
"operator": "==",
"thresholdValue": "false",
"representation": "disabled",
"text": "disabled"
},
{
"operator": "Default",
"thresholdValue": null,
"representation": "success",
"text": "enabled"
}
]
}
},
{
"columnMatch": "prop",
"formatter": 5
},
{
"columnMatch": "enabled",
"formatter": 3,
"formatOptions": {
"min": 0,
"max": 1,
"palette": "redGreen",
"compositeBarSettings": {
"labelText": "",
"columnSettings": [
{
"columnName": "enabled",
"color": "green"
}
]
}
}
}
],
"rowLimit": 512,
"filter": true,
"sortBy": [
{
"itemKey": "lastModifiedUtc",
"sortOrder": 2
}
],
"labelSettings": [
{
"columnId": "RuleName",
"label": "Rule name"
},
{
"columnId": "Description"
},
{
"columnId": "AlertRuleID",
"label": "Alert rule ID"
},
{
"columnId": "Kind",
"label": "Rule type"
},
{
"columnId": "ProductName",
"label": "Product name"
},
{
"columnId": "Tactics"
},
{
"columnId": "Status"
},
{
"columnId": "prop"
}
]
},
"sortBy": [
{
"itemKey": "lastModifiedUtc",
"sortOrder": 2
}
],
"graphSettings": {
"type": 0
},
"mapSettings": {
"locInfo": "LatLong"
}
},
"name": "Analytic rules"
},
{
"type": 12,
"content": {
"version": "NotebookGroup/1.0",
"groupType": "editable",
"items": [
{
"type": 1,
"content": {
"json": "## Total number of incidents \nSum of all the incidents generated by all the selected rules over the selected time range"
},
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "Incindets sum text"
},
{
"type": 1,
"content": {
"json": "### Help\r\nThe **Total number of incidents** chart shows the total count of the incidents generated by the selected rules over the selected time range.\r\nYou can use this chart to monitor the incidents load on the SOC.\r\n\r\n",
"style": "info"
},
"conditionalVisibilities": [
{
"parameterName": "Help",
"comparison": "isEqualTo",
"value": "Yes"
},
{
"parameterName": "prop",
"comparison": "isNotEqualTo"
}
],
"name": "Incindets sum text help"
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "let alertText = strcat_array(dynamic([{AlertRuleID}]),\",\");\r\nlet isProductMarked = (product:string) {\r\n let productText = strcat_array(dynamic([{ProductName}]),\",\");\r\n array_index_of(split(productText,'\\\"'),product)\r\n};\r\nlet SecurityAlertFiltered= SecurityAlert\r\n| where TimeGenerated >= {TimeRange:start} and TimeGenerated <= {TimeRange:end}\r\n| project ProductName,ExtendedProperties,SystemAlertId\r\n| extend AnalyticRuleIdStr = replace('\\\"','',tostring(todynamic(ExtendedProperties)[\"Analytic Rule Ids\"]))\r\n| extend AnaliticRulesInAlertArray= split(substring(AnalyticRuleIdStr,1,string_size(AnalyticRuleIdStr)-2),\",\")\r\n| mv-expand SingleAnaliticRuleID=AnaliticRulesInAlertArray\r\n| extend SingleAnaliticRuleID=tostring(SingleAnaliticRuleID)\r\n| extend SingleAnaliticRuleID=iff(ProductName==\"Azure Sentinel\",tostring(SingleAnaliticRuleID),ProductName)\r\n| where alertText has SingleAnaliticRuleID or isProductMarked(SingleAnaliticRuleID)!=-1;\r\nlet SecurityIncedentFiltered = SecurityIncident\r\n| where TimeGenerated >= {TimeRange:start} and TimeGenerated <= {TimeRange:end}\r\n| project AlertIds,TimeGenerated\r\n| mv-expand AnalyticAlertId=AlertIds\r\n| extend AnalyticAlertId=tostring(AnalyticAlertId);\r\nSecurityAlertFiltered\r\n| join SecurityIncedentFiltered on $left.SystemAlertId==$right.AnalyticAlertId\r\n| project TimeGenerated,SingleAnaliticRuleID\r\n| summarize incedentAmount=count() by bin(TimeGenerated,1h)\r\n",
"size": 0,
"timeContext": {
"durationMs": 86400000
},
"queryType": 0,
"resourceType": "microsoft.operationalinsights/workspaces",
"visualization": "areachart"
},
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "Total amount of Incidents "
},
{
"type": 1,
"content": {
"json": "## Number of incidents, by rule\nCount of the incidents generated by each of the selected rules over time"
},
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "Total amount of Incidents text"
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "let getRuleNameIdTable = (){\r\n let alertText = strcat_array(dynamic([{AlertRuleID}]),\",\");\r\n let RuleName = strcat_array(dynamic([{RuleName}]),\",\");\r\n let rulesData = range x from 0 to array_length(split(alertText,','))-1 step 1\r\n | extend AlertRuleId= tostring(split(alertText,',')[x]),\r\n RuleName=tostring(split(RuleName,',')[x]);\r\n rulesData\r\n};\r\nlet GetAlertRuleTable = (){\r\n let proerties = dynamic([{prop}]);\r\n let TmpRuleTable = datatable (MockColumn:string)[\"Mock\"];\r\n TmpRuleTable\r\n | mv-expand SingleRuleProperties=proerties\r\n | project-away MockColumn\r\n | extend \r\n Product=iff(SingleRuleProperties.productFilter!='',SingleRuleProperties.productFilter,\"Azure Sentinel\"), \r\n RuleName=tostring(SingleRuleProperties.displayName), \r\n MITRE_Tactics=iff(SingleRuleProperties.tactics!='',SingleRuleProperties.tactics,dynamic([])),\r\n Description=SingleRuleProperties.description\r\n | extend Status= iff(SingleRuleProperties.enabled==true,'Enabled',iff(RuleName startswith 'AUTO DISABLED','Auto disabled', 'Disabled'))\r\n | project-away SingleRuleProperties\r\n | join getRuleNameIdTable() on $left.RuleName==$right.RuleName\r\n | project-away RuleName1,x\r\n};\r\nlet AlertAmount = materialize( SecurityAlert\r\n| project ExtendedProperties,ProductName, ProviderName,TimeGenerated\r\n| where TimeGenerated >= {TimeRange:start} and TimeGenerated <= {TimeRange:end}\r\n| extend AnalyticRuleIdStr = replace('\\\"','',tostring(todynamic(ExtendedProperties)[\"Analytic Rule Ids\"]))\r\n| extend AnaliticRulesInAlertArray= split(substring(AnalyticRuleIdStr,1,string_size(AnalyticRuleIdStr)-2),\",\")\r\n| mv-expand SingleAnaliticRuleID=AnaliticRulesInAlertArray\r\n| extend SingleAnaliticRuleID=iff(ProductName==\"Azure Sentinel\",tostring(SingleAnaliticRuleID),ProductName)\r\n| summarize AlertAmount=count() by SingleAnaliticRuleID\r\n| extend AlertAmount=iff(AlertAmount>0,AlertAmount,0));\r\nlet AlertRulesTable =GetAlertRuleTable();\r\nlet alertText = strcat_array(dynamic([{AlertRuleID}]),\",\");\r\nlet isProductMarked = (product:string) {\r\n let productText = strcat_array(dynamic([{ProductName}]),\",\");\r\n array_index_of(split(productText,'\\\"'),product)\r\n};\r\nlet SecurityAlertFiltered= SecurityAlert\r\n| where TimeGenerated >= {TimeRange:start} and TimeGenerated <= {TimeRange:end}\r\n| project ProductName,ExtendedProperties,SystemAlertId\r\n| extend AnalyticRuleIdStr = replace('\\\"','',tostring(todynamic(ExtendedProperties)[\"Analytic Rule Ids\"]))\r\n| extend AnaliticRulesInAlertArray= split(substring(AnalyticRuleIdStr,1,string_size(AnalyticRuleIdStr)-2),\",\")\r\n| mv-expand SingleAnaliticRuleID=AnaliticRulesInAlertArray\r\n| extend SingleAnaliticRuleID=tostring(SingleAnaliticRuleID)\r\n| extend SingleAnaliticRuleID=iff(ProductName==\"Azure Sentinel\",tostring(SingleAnaliticRuleID),ProductName)\r\n| where alertText has SingleAnaliticRuleID or isProductMarked(SingleAnaliticRuleID)!=-1;\r\nlet SecurityIncedentFiltered = SecurityIncident\r\n| where TimeGenerated >= {TimeRange:start} and TimeGenerated <= {TimeRange:end}\r\n| project AlertIds,TimeGenerated\r\n| mv-expand AnalyticAlertId=AlertIds\r\n| extend AnalyticAlertId=tostring(AnalyticAlertId);\r\nSecurityAlertFiltered\r\n| join SecurityIncedentFiltered on $left.SystemAlertId==$right.AnalyticAlertId\r\n| project TimeGenerated,SingleAnaliticRuleID\r\n| join kind=leftouter AlertRulesTable on $left.SingleAnaliticRuleID==$right.AlertRuleId\r\n| summarize incedentAmount=count() by bin(TimeGenerated,1h),SingleAnaliticRuleID,RuleName\r\n| where RuleName !=\"\"\r\n|project incedentAmount,TimeGenerated,RuleName",
"size": 0,
"queryType": 0,
"resourceType": "microsoft.operationalinsights/workspaces",
"visualization": "linechart",
"chartSettings": {
"xAxis": "TimeGenerated",
"yAxis": [
"incedentAmount"
],
"showMetrics": false,
"showLegend": true
}
},
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "Incidents created by rules"
},
{
"type": 1,
"content": {
"json": "## Amount of incidents by rule \nFor each of the selected rules the total incidents number genereted on the selected time period by closing status "
},
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "Total amount of Incidents help text"
},
{
"type": 1,
"content": {
"json": "### Help\r\nHere you can see the amount of incidents created from each rule you selected.\r\nIn addition you can see the distribution of closing reasons for each of the selected rules. \r\nMonitoring the amount of incidents each detection is creating is essential for the SOC.\r\n",
"style": "info"
},
"conditionalVisibilities": [
{
"parameterName": "Help",
"comparison": "isEqualTo",
"value": "Yes"
},
{
"parameterName": "prop",
"comparison": "isNotEqualTo"
}
],
"name": "Incident by rules text "
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "let alertText = strcat_array(dynamic([{AlertRuleID}]),\",\");\r\nlet isProductMarked = (product:string) {\r\n let productText = strcat_array(dynamic([{ProductName}]),\",\");\r\n array_index_of(split(productText,','),product)\r\n};\r\nlet getAmountOfIncedentForRuleId = (classification:string,status:string){\r\nSecurityIncident\r\n | project TimeGenerated, Status, Classification, RelatedAnalyticRuleIds, AdditionalData\r\n | where TimeGenerated >= {TimeRange:start} and TimeGenerated <= {TimeRange:end}\r\n | where Status == status and Classification == classification\r\n | mv-expand RuleId=RelatedAnalyticRuleIds\r\n | extend RuleId=tostring(RuleId)\r\n | extend AlertProductNames = todynamic(AdditionalData)[\"alertProductNames\"]\r\n | mv-expand AlertProductName =AlertProductNames\r\n | extend AlertProductName=tostring(AlertProductName)\r\n | project AlertProductName,RuleId\r\n | where isProductMarked(AlertProductName)!=-1 or alertText has RuleId\r\n | extend RuleId=iff(AlertProductName!= 'Azure Sentinel', AlertProductName, RuleId)\r\n | summarize counter=count() by RuleId\r\n};\r\n\r\nlet falsePositiveClassificationTable = getAmountOfIncedentForRuleId(\"FalsePositive\",\"Closed\") | extend FalsePositiveCounter=counter | project-away counter;\r\nlet undeterminedClassificationTable = getAmountOfIncedentForRuleId(\"Undetermined\",\"Closed\") | extend UndeterminedCounter=counter | project-away counter;\r\nlet benignPositiveClassificationTable = getAmountOfIncedentForRuleId(\"BenignPositive\",\"Closed\") | extend BenignPositiveCounter=counter | project-away counter;\r\nlet truePositiveClassificationTable = getAmountOfIncedentForRuleId(\"TruePositive\",\"Closed\") | extend TruePositiveCounter=counter | project-away counter;\r\nlet activeIncedentTable = getAmountOfIncedentForRuleId(\"\",\"Active\") | extend ActiveIncedentsCounter=counter | project-away counter; \r\nlet newIncedentTable = getAmountOfIncedentForRuleId(\"\",\"New\") | extend NewIncedentsCounter=counter | project-away counter;\r\nlet joinByRuleId = (T:(RuleId:string), S:(RuleId:string)){\r\n T \r\n | join kind=fullouter S on $left.RuleId == $right.RuleId\r\n | extend RuleId= iff(RuleId == '', RuleId1,RuleId)\r\n | project-away RuleId1\r\n};\r\njoinByRuleId(joinByRuleId(joinByRuleId(joinByRuleId(joinByRuleId(falsePositiveClassificationTable, undeterminedClassificationTable) , benignPositiveClassificationTable), truePositiveClassificationTable),activeIncedentTable), newIncedentTable)\r\n// Adding the Rule name to the table(we want to display name and not ID) \r\n| join kind=leftouter \r\n(SecurityAlert | project TimeGenerated, ProductName, ExtendedProperties\r\n| where TimeGenerated >= {TimeRange:start} and TimeGenerated <= {TimeRange:end}\r\n| where ProductName == 'Azure Sentinel'\r\n| extend RuleId = parsejson( tostring(todynamic(ExtendedProperties)['Analytic Rule Ids']))\r\n| mv-expand RuleId=RuleId\r\n| extend RuleId=tostring(RuleId)\r\n| extend RuleName= tostring(todynamic(ExtendedProperties)['Analytic Rule Name'])\r\n| project RuleId,RuleName\r\n| distinct RuleId,RuleName)\r\n on $left.RuleId==$right.RuleId\r\n| extend RuleName=iff(isempty(RuleName),RuleId,RuleName)\r\n| project-away RuleId1\r\n| where alertText has RuleId or isProductMarked(RuleName)!=-1 \r\n| project-away RuleId\r\n| extend TotalAlerts= iff(isempty(FalsePositiveCounter),0,FalsePositiveCounter) + \r\niff(isempty(UndeterminedCounter),0,UndeterminedCounter)+ \r\niff(isempty(BenignPositiveCounter),0,BenignPositiveCounter)+\r\niff(isempty(TruePositiveCounter),0,TruePositiveCounter) +\r\niff(isempty(ActiveIncedentsCounter),0,ActiveIncedentsCounter) +\r\niff(isempty(NewIncedentsCounter),0,NewIncedentsCounter)\r\n| where TotalAlerts>0\r\n| sort by TotalAlerts desc \r\n| project-away TotalAlerts \r\n",
"size": 0,
"noDataMessage": "No incedents created",
"queryType": 0,
"resourceType": "microsoft.operationalinsights/workspaces",
"visualization": "barchart",
"chartSettings": {
"xAxis": "RuleName"
}
},
"conditionalVisibility": {
"parameterName": "AlertRuleID",
"comparison": "isNotEqualTo"
},
"name": "Incident closing reasons"
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "let alertText = strcat_array(dynamic([{AlertRuleID}]),\",\");\r\nlet isProductMarked = (product:string) {\r\n let productText = strcat_array(dynamic([{ProductName}]),\",\");\r\n array_index_of(split(productText,','),product)\r\n};\r\nSecurityIncident\r\n| project TimeGenerated, RelatedAnalyticRuleIds, AdditionalData\r\n| where TimeGenerated >= {TimeRange:start} and TimeGenerated <= {TimeRange:end}\r\n| mv-expand AlertRuleId=RelatedAnalyticRuleIds\r\n| extend AlertRuleId=tostring(AlertRuleId)\r\n| extend AlertProductNames = todynamic(AdditionalData)[\"alertProductNames\"]\r\n| mv-expand AlertProductName =AlertProductNames\r\n| extend AlertProductName=tostring(AlertProductName)\r\n| project AlertProductName,AlertRuleId\r\n| where isProductMarked(AlertProductName)!=-1 or alertText has AlertRuleId\r\n| summarize count() by AlertProductName",
"size": 2,
"title": "Incident created by rules",
"noDataMessage": "No incedents created",
"queryType": 0,
"resourceType": "microsoft.operationalinsights/workspaces",
"visualization": "piechart"
},
"customWidth": "38",
"conditionalVisibility": {
"parameterName": "AlertRuleID",
"comparison": "isNotEqualTo"
},
"name": "Incident closing reson query"
}
]
},
"conditionalVisibility": {
"parameterName": "TAB",
"comparison": "isEqualTo",
"value": "Incident"
},
"name": "IncidentRelatedGroup"
},
{
"type": 12,
"content": {
"version": "NotebookGroup/1.0",
"groupType": "editable",
"items": [
{
"type": 1,
"content": {
"json": "## Rules analysis \nThe following metrics apply to the selected rules in the table."
},
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "analysys for rules text "
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "let getSecurityAlertRulesTable = (){\r\n let getRuleNameIdTable = (){\r\n let alertText = strcat_array(dynamic([{AlertRuleID}]),\",\");\r\n let RuleName = strcat_array(dynamic([{RuleName}]),\",\");\r\n let rulesData = range x from 0 to array_length(split(alertText,','))-1 step 1\r\n | extend AlertRuleId= tostring(split(alertText,',')[x]),\r\n RuleName=tostring(split(RuleName,',')[x]);\r\n rulesData\r\n };\r\n let GetAlertRuleTable = (){\r\n let proerties = dynamic([{prop}]);\r\n let TmpRuleTable = datatable (MockColumn:string)[\"Mock\"];\r\n TmpRuleTable\r\n | mv-expand SingleRuleProperties=proerties\r\n | project-away MockColumn\r\n | extend \r\n Product=iff(SingleRuleProperties.productFilter!='',SingleRuleProperties.productFilter,\"Azure Sentinel\"), \r\n RuleName=tostring(SingleRuleProperties.displayName), \r\n MITRE_Tactics=iff(SingleRuleProperties.tactics!='',SingleRuleProperties.tactics,dynamic([])),\r\n Description=SingleRuleProperties.description\r\n | extend Status= iff(SingleRuleProperties.enabled==true,'Enabled',iff(RuleName startswith 'AUTO DISABLED','Auto disabled', 'Disabled'))\r\n | project-away SingleRuleProperties\r\n | join getRuleNameIdTable() on $left.RuleName==$right.RuleName\r\n | project-away RuleName1,x\r\n };\r\n let AlertAmount = materialize( SecurityAlert\r\n | project ExtendedProperties,ProductName, ProviderName,TimeGenerated\r\n | where TimeGenerated >= {TimeRange:start} and TimeGenerated <= {TimeRange:end}\r\n | extend AnalyticRuleIdStr = replace('\\\"','',tostring(todynamic(ExtendedProperties)[\"Analytic Rule Ids\"]))\r\n | extend AnaliticRulesInAlertArray= split(substring(AnalyticRuleIdStr,1,string_size(AnalyticRuleIdStr)-2),\",\")\r\n | mv-expand SingleAnaliticRuleID=AnaliticRulesInAlertArray\r\n | extend SingleAnaliticRuleID=iff(ProductName==\"Azure Sentinel\",tostring(SingleAnaliticRuleID),ProductName)\r\n | summarize AlertAmount=count() by SingleAnaliticRuleID\r\n | extend AlertAmount=iff(AlertAmount>0,AlertAmount,0));\r\n GetAlertRuleTable()\r\n | join kind=leftouter AlertAmount on $left.AlertRuleId==$right.SingleAnaliticRuleID \r\n | project-away SingleAnaliticRuleID\r\n | extend AlertAmount=iff(AlertAmount>0 or Product!= 'Azure Sentinel',AlertAmount,0)\r\n | join kind=leftouter AlertAmount on $left.Product==$right.SingleAnaliticRuleID\r\n | extend AlertAmount=iff(Product!= 'Azure Sentinel',AlertAmount1,AlertAmount)\r\n | project-away AlertAmount1\r\n | sort by AlertAmount desc\r\n \r\n };\r\ngetSecurityAlertRulesTable()\r\n| summarize count() by Product\r\n",
"size": 2,
"title": "Selected rules by provider",
"timeContext": {
"durationMs": 86400000
},
"queryType": 0,
"resourceType": "microsoft.operationalinsights/workspaces",
"visualization": "piechart"
},
"customWidth": "40",
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "analysys for rules query"
},
{
"type": 12,
"content": {
"version": "NotebookGroup/1.0",
"groupType": "editable",
"items": [
{
"type": 1,
"content": {
"json": "#### Selected rules by MITRE ATT&CK tactics coverage\nThis chart shows each rule's MITRE ATT&CK tactics, as configured at the rule's creation.\n\nWe aspire to have full coverage. For more complete MITRE ATT&CK coverage, use Azure Sentinel templates."
},
"name": "text - 5"
},
{
"type": 1,
"content": {
"json": "### Help\r\nThe **Selected rules by MITRE ATT&CK tactics coverage** chart displays the MITRE ATT&CK tactics coverage of the selected rules.\r\n\r\nWhen creating **scheduled alert rules**, you are able to specify the MITRE ATT&CK tactics associated with each rule. These specified tactics are used to populate this chart.\r\n\r\nThis tactic specification is available only with **scheduled alert** rules, not with other types of analytics rules.\r\n",
"style": "info"
},
"conditionalVisibilities": [
{
"parameterName": "Help",
"comparison": "isEqualTo",
"value": "Yes"
},
{
"parameterName": "prop",
"comparison": "isNotEqualTo"
}
],
"name": "MITRE attack coverage text "
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "let MITRE_DEF_TABLE = datatable(Tactic:string)[\"InitialAccess\",\"Execution\",\"Persistence\",\"PrivilegeEscalation\", \"DefenseEvasion\",\"CredentialAccess\",\"Discovery\", \"LateralMovement\",\"Collection\",\"Exfiltration\",\"CommandAndControl\",\"Impact\"];\r\nlet getRuleNameIdTable = (){\r\n let alertText = strcat_array(dynamic([{AlertRuleID}]),\",\");\r\n let RuleName = strcat_array(dynamic([{RuleName}]),\",\");\r\n let rulesData = range x from 0 to array_length(split(alertText,','))-1 step 1\r\n | extend AlertRuleId= tostring(split(alertText,',')[x]),\r\n RuleName=tostring(split(RuleName,',')[x]);\r\n rulesData\r\n};\r\nlet GetAlertRuleTable = (){\r\n let proerties = dynamic([{prop}]);\r\n let TmpRuleTable = datatable (MockColumn:string)[\"Mock\"];\r\n TmpRuleTable\r\n | mv-expand SingleRuleProperties=proerties\r\n | project-away MockColumn\r\n | extend \r\n Product=iff(SingleRuleProperties.productFilter!='',SingleRuleProperties.productFilter,\"Azure Sentinel\"), \r\n RuleName=tostring(SingleRuleProperties.displayName), \r\n MITRE_Tactics=iff(SingleRuleProperties.tactics!='',SingleRuleProperties.tactics,dynamic([])),\r\n Description=SingleRuleProperties.description\r\n | extend Status= iff(SingleRuleProperties.enabled==true,'Enabled',iff(RuleName startswith 'AUTO DISABLED','Auto disabled', 'disabled'))\r\n | project-away SingleRuleProperties\r\n | join getRuleNameIdTable() on $left.RuleName==$right.RuleName\r\n | project-away RuleName1\r\n};\r\nMITRE_DEF_TABLE\r\n| join kind=leftouter (GetAlertRuleTable()\r\n| project MITRE_Tactics\r\n| mv-expand MITRE_Tactics\r\n| extend MITRE_Tactics = tostring(MITRE_Tactics)\r\n| summarize RuleAmount=count() by MITRE_Tactics) on $left.Tactic==$right.MITRE_Tactics\r\n| extend RuleAmount=iff(RuleAmount>0,RuleAmount,0)\r\n| project Tactic,RuleAmount\r\n",
"size": 0,
"timeContext": {
"durationMs": 86400000
},
"queryType": 0,
"resourceType": "microsoft.operationalinsights/workspaces",
"visualization": "barchart",
"chartSettings": {
"seriesLabelSettings": [
{
"seriesName": "DefenseEvasion",
"label": "Defense evasion"
},
{
"seriesName": "CommandAndControl",
"label": "Command and control"
},
{
"seriesName": "InitialAccess",
"label": "Initial access"
},
{
"seriesName": "LateralMovement",
"label": "Lateral movement"
},
{
"seriesName": "PrivilegeEscalation",
"label": "Privilege escalation"
},
{
"seriesName": "CredentialAccess",
"label": "Credential access"
}
]
}
},
"customWidth": "100",
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "MITRE attack coverage query"
}
]
},
"customWidth": "60",
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "MITRE group"
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "let getRuleNameIdTable = (){\r\n let alertText = strcat_array(dynamic([{AlertRuleID}]),\",\");\r\n let RuleName = strcat_array(dynamic([{RuleName}]),\",\");\r\n let rulesData = range x from 0 to array_length(split(alertText,','))-1 step 1\r\n | extend AlertRuleId= tostring(split(alertText,',')[x]),\r\n RuleName=tostring(split(RuleName,',')[x]);\r\n rulesData\r\n};\r\nlet GetAlertRuleTable = (){\r\n let proerties = dynamic([{prop}]);\r\n let TmpRuleTable = datatable (MockColumn:string)[\"Mock\"];\r\n TmpRuleTable\r\n | mv-expand SingleRuleProperties=proerties\r\n | project-away MockColumn\r\n | extend \r\n Product=iff(SingleRuleProperties.productFilter!='',SingleRuleProperties.productFilter,\"Azure Sentinel\"), \r\n RuleName=tostring(SingleRuleProperties.displayName), \r\n MITRE_Tactics=iff(SingleRuleProperties.tactics!='',SingleRuleProperties.tactics,dynamic([])),\r\n Description=SingleRuleProperties.description\r\n | extend Status= iff(SingleRuleProperties.enabled==true,'Enabled',iff(RuleName startswith 'AUTO DISABLED','Auto disabled', 'Disabled'))\r\n | project-away SingleRuleProperties\r\n | join getRuleNameIdTable() on $left.RuleName==$right.RuleName\r\n | project-away RuleName1\r\n};\r\n let StatusTable = datatable (Status:string)[\"Enabled\",\"Disabled\",\"Auto Disabled\"];\r\nGetAlertRuleTable()\r\n| project Status\r\n| summarize AmountOfRules=count() by Status\r\n| join kind=fullouter StatusTable on $left.Status==$right.Status\r\n| extend Status=Status1\r\n| project Status,AmountOfRules\r\n| extend AmountOfRules=iff(AmountOfRules>0,AmountOfRules,0)\r\n",
"size": 2,
"title": "Selected rules by status",
"timeContext": {
"durationMs": 86400000
},
"queryType": 0,
"resourceType": "microsoft.operationalinsights/workspaces",
"visualization": "piechart",
"tileSettings": {
"showBorder": false,
"titleContent": {
"columnMatch": "Status",
"formatter": 1
},
"leftContent": {
"columnMatch": "AmountOfRules",
"formatter": 12,
"formatOptions": {
"palette": "auto"
},
"numberFormat": {
"unit": 17,
"options": {
"maximumSignificantDigits": 3,
"maximumFractionDigits": 2
}
}
}
},
"chartSettings": {
"seriesLabelSettings": [
{
"seriesName": "Enabled",
"color": "greenDark"
},
{
"seriesName": "Disabled",
"color": "red"
},
{
"seriesName": "Auto Disabled",
"color": "yellow"
}
]
}
},
"customWidth": "40",
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "rules status query"
},
{
"type": 12,
"content": {
"version": "NotebookGroup/1.0",
"groupType": "editable",
"items": [
{
"type": 1,
"content": {
"json": "#### Rules that require attention\nThis chart displays the selected rules that require attention, either because they didn't create alerts during the selected period, or because they were auto-disabled.\n\nAzure Sentinel will automatically disable rules that violate the scheduled alert rules guidelines."
},
"name": "text - 1"
},
{
"type": 1,
"content": {
"json": "### Help\r\nThis chart displays rules that require attention.\r\nThese include rules that didn't create alerts during the selected time range, and auto-disabled rules. \r\nRules that are not triggered might have problems, which may result in overestimation of the detection coverage.\r\nAzure Sentinel will automatically disable rules that violate the scheduled alert rules guidelines.\r\n[Learn more](https://docs.microsoft.com/azure/sentinel/tutorial-detect-threats-custom) about scheduled alerts in Azure Sentinel.",
"style": "info"
},
"conditionalVisibilities": [
{
"parameterName": "Help",
"comparison": "isEqualTo",
"value": "Yes"
},
{
"parameterName": "prop",
"comparison": "isNotEqualTo"
}
],
"name": "text - 2"
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "let getRuleNameIdTable = (){\r\n let alertText = strcat_array(dynamic([{AlertRuleID}]),\",\");\r\n let RuleName = strcat_array(dynamic([{RuleName}]),\",\");\r\n let rulesData = range x from 0 to array_length(split(alertText,','))-1 step 1\r\n | extend AlertRuleId= tostring(split(alertText,',')[x]),\r\n RuleName=tostring(split(RuleName,',')[x]);\r\n rulesData\r\n};\r\nlet GetAlertRuleTable = (){\r\n let proerties = dynamic([{prop}]);\r\n let TmpRuleTable = datatable (MockColumn:string)[\"Mock\"];\r\n TmpRuleTable\r\n | mv-expand SingleRuleProperties=proerties\r\n | project-away MockColumn\r\n | extend \r\n Product=iff(SingleRuleProperties.productFilter!='',SingleRuleProperties.productFilter,\"Azure Sentinel\"), \r\n RuleName=tostring(SingleRuleProperties.displayName), \r\n MITRE_Tactics=iff(SingleRuleProperties.tactics!='',SingleRuleProperties.tactics,dynamic([])),\r\n Description=SingleRuleProperties.description\r\n | extend Status= iff(SingleRuleProperties.enabled==true,'Enabled',iff(RuleName startswith 'AUTO DISABLED','Auto disabled', 'Disabled'))\r\n | project-away SingleRuleProperties\r\n | join getRuleNameIdTable() on $left.RuleName==$right.RuleName\r\n | project-away RuleName1,x\r\n};\r\nlet AlertAmount = materialize( SecurityAlert\r\n| project ExtendedProperties,ProductName, ProviderName,TimeGenerated\r\n| where TimeGenerated >= {TimeRange:start} and TimeGenerated <= {TimeRange:end}\r\n| extend AnalyticRuleIdStr = replace('\\\"','',tostring(todynamic(ExtendedProperties)[\"Analytic Rule Ids\"]))\r\n| extend AnaliticRulesInAlertArray= split(substring(AnalyticRuleIdStr,1,string_size(AnalyticRuleIdStr)-2),\",\")\r\n| mv-expand SingleAnaliticRuleID=AnaliticRulesInAlertArray\r\n| extend SingleAnaliticRuleID=iff(ProductName==\"Azure Sentinel\",tostring(SingleAnaliticRuleID),ProductName)\r\n| summarize AlertAmount=count() by SingleAnaliticRuleID\r\n| extend AlertAmount=iff(AlertAmount>0,AlertAmount,0));\r\nGetAlertRuleTable()\r\n| join kind=leftouter AlertAmount on $left.AlertRuleId==$right.SingleAnaliticRuleID \r\n| project-away SingleAnaliticRuleID\r\n| extend AlertAmount=iff(AlertAmount>0 or Product!= 'Azure Sentinel',AlertAmount,0)\r\n| join kind=leftouter AlertAmount on $left.Product==$right.SingleAnaliticRuleID\r\n| extend AlertAmount=iff(Product!= 'Azure Sentinel',AlertAmount1,AlertAmount)\r\n| extend AlertAmount=iff(AlertAmount>0,AlertAmount,0)\r\n| project-away AlertAmount1\r\n| sort by AlertAmount desc\r\n| project Status,Product,RuleName,AlertAmount\r\n| where AlertAmount==0 and Status == 'Enabled' or Status==\"Auto disabled\"\r\n| extend Status=iff(Status == 'Enabled' and AlertAmount==0, \"No Alerts\", Status)\r\n| project Status,RuleName, AlertAmount\r\n",
"size": 0,
"queryType": 0,
"resourceType": "microsoft.operationalinsights/workspaces",
"gridSettings": {
"formatters": [
{
"columnMatch": "Status",
"formatter": 18,
"formatOptions": {
"thresholdsOptions": "icons",
"thresholdsGrid": [
{
"operator": "contains",
"thresholdValue": "No Alerts",
"representation": "1",
"text": "No Alerts"
},
{
"operator": "contains",
"thresholdValue": "Auto disabled",
"representation": "2",
"text": "Auto disabled"
},
{
"operator": "Default",
"thresholdValue": null,
"representation": "question",
"text": ""
}
],
"customColumnWidthSetting": "11ch"
}
}
],
"labelSettings": [
{
"columnId": "Status"
},
{
"columnId": "RuleName",
"label": "Rule name"
}
]
}
},
"customWidth": "100",
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "query - 17"
}
]
},
"customWidth": "60",
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "rule required attention group"
}
]
},
"conditionalVisibility": {
"parameterName": "TAB",
"comparison": "isEqualTo",
"value": "Rule"
},
"name": "AlertRulesGroup"
},
{
"type": 12,
"content": {
"version": "NotebookGroup/1.0",
"groupType": "editable",
"items": [
{
"type": 1,
"content": {
"json": "## Total number of alerts\nSum of the alerts generated by all the selected rules over the selected time range"
},
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "text Sum of the numbers of alerts generated by all the selected rules"
},
{
"type": 1,
"content": {
"json": "### Help\r\nThe **Total number of alerts** chart shows the total count of the alerts generated by the selected rules over the selected time range.\r\nYou can use this chart to monitor the alert load on the SOC.\r\n",
"style": "info"
},
"conditionalVisibilities": [
{
"parameterName": "Help",
"comparison": "isEqualTo",
"value": "Yes"
},
{
"parameterName": "prop",
"comparison": "isNotEqualTo"
}
],
"name": "text alerts generated by the selected rules over the selected time"
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "let alertText = strcat_array(dynamic([{AlertRuleID}]),\",\");\r\nlet isProductMarked = (product:string) {\r\n let productText = strcat_array(dynamic([{ProductName}]),\",\");\r\n array_index_of(split(productText,'\\\"'),product)\r\n};\r\nSecurityAlert\r\n| project TimeGenerated,ProductName,ExtendedProperties\r\n| where TimeGenerated >= {TimeRange:start} and TimeGenerated <= {TimeRange:end}\r\n| extend AnalyticRuleIdStr = replace('\\\"','',tostring(todynamic(ExtendedProperties)[\"Analytic Rule Ids\"]))\r\n| extend AnaliticRulesInAlertArray= split(substring(AnalyticRuleIdStr,1,string_size(AnalyticRuleIdStr)-2),\",\")\r\n| mv-expand SingleAnaliticRuleID=AnaliticRulesInAlertArray\r\n| extend SingleAnaliticRuleID=iff(ProductName==\"Azure Sentinel\",tostring(SingleAnaliticRuleID),ProductName)\r\n| extend RuleName= iff(ProductName==\"Azure Sentinel\",tostring(todynamic(ExtendedProperties)['Analytic Rule Name']),ProductName)\r\n| where isProductMarked(ProductName)!=-1 or ProductName == \"Azure Sentinel\" and alertText has SingleAnaliticRuleID\r\n| summarize AlertAmount=count() by bin(TimeGenerated,1h)",
"size": 0,
"timeContext": {
"durationMs": 86400000
},
"queryType": 0,
"resourceType": "microsoft.operationalinsights/workspaces",
"visualization": "areachart",
"chartSettings": {
"seriesLabelSettings": [
{
"seriesName": "AlertAmount",
"label": "Number of alerts"
}
]
}
},
"customWidth": "100",
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "Total amount of alerts"
},
{
"type": 1,
"content": {
"json": "## Number of alerts, by rule\nCount of the alerts generated by each of the selected rules over time"
},
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "number of alerts by selected rule text "
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "let alertText = strcat_array(dynamic([{AlertRuleID}]),\",\");\r\nlet isProductMarked = (product:string) {\r\n let productText = strcat_array(dynamic([{ProductName}]),\",\");\r\n array_index_of(split(productText,'\\\"'),product)\r\n};\r\nSecurityAlert\r\n| project TimeGenerated,ProductName,ExtendedProperties\r\n| where TimeGenerated >= {TimeRange:start} and TimeGenerated <= {TimeRange:end}\r\n| extend AnalyticRuleIdStr = replace('\\\"','',tostring(todynamic(ExtendedProperties)[\"Analytic Rule Ids\"]))\r\n| extend AnaliticRulesInAlertArray= split(substring(AnalyticRuleIdStr,1,string_size(AnalyticRuleIdStr)-2),\",\")\r\n| mv-expand SingleAnaliticRuleID=AnaliticRulesInAlertArray\r\n| extend SingleAnaliticRuleID=iff(ProductName==\"Azure Sentinel\",tostring(SingleAnaliticRuleID),ProductName)\r\n| extend RuleName= iff(ProductName==\"Azure Sentinel\",tostring(todynamic(ExtendedProperties)['Analytic Rule Name']),ProductName)\r\n| where isProductMarked(ProductName)!=-1 or ProductName == \"Azure Sentinel\" and alertText has SingleAnaliticRuleID\r\n| summarize AlertAmount=count() by bin(TimeGenerated,1h),SingleAnaliticRuleID,RuleName",
"size": 0,
"noDataMessage": "No alerts generated by selected rules",
"queryType": 0,
"resourceType": "microsoft.operationalinsights/workspaces",
"visualization": "linechart",
"chartSettings": {
"xAxis": "TimeGenerated",
"yAxis": [
"AlertAmount"
],
"group": "RuleName",
"createOtherGroup": null
}
},
"customWidth": "100",
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "Amount of alerts by rule over time"
},
{
"type": 12,
"content": {
"version": "NotebookGroup/1.0",
"groupType": "editable",
"items": [
{
"type": 1,
"content": {
"json": "#### Alert rules trigger status\r\nThe number of rules that triggered alerts and those that didn't."
},
"name": "Alert rules triggered text"
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "let getRuleNameIdTable = (){\r\n let alertText = strcat_array(dynamic([{AlertRuleID}]),\",\");\r\n let RuleName = strcat_array(dynamic([{RuleName}]),\",\");\r\n let rulesData = range x from 0 to array_length(split(alertText,','))-1 step 1\r\n | extend AlertRuleId= tostring(split(alertText,',')[x]),\r\n RuleName=tostring(split(RuleName,',')[x]);\r\n rulesData\r\n};\r\nlet GetAlertRuleTable = (){\r\n let proerties = dynamic([{prop}]);\r\n let TmpRuleTable = datatable (MockColumn:string)[\"Mock\"];\r\n TmpRuleTable\r\n | mv-expand SingleRuleProperties=proerties\r\n | project-away MockColumn\r\n | extend \r\n Product=iff(SingleRuleProperties.productFilter!='',SingleRuleProperties.productFilter,\"Azure Sentinel\"), \r\n RuleName=tostring(SingleRuleProperties.displayName), \r\n MITRE_Tactics=iff(SingleRuleProperties.tactics!='',SingleRuleProperties.tactics,dynamic([])),\r\n Description=SingleRuleProperties.description\r\n | extend Status= iff(SingleRuleProperties.enabled==true,'Enabled',iff(RuleName startswith 'AUTO DISABLED','Auto disabled', 'Disabled'))\r\n | project-away SingleRuleProperties\r\n | join getRuleNameIdTable() on $left.RuleName==$right.RuleName\r\n | project-away RuleName1,x\r\n};\r\nlet AlertAmount = materialize( SecurityAlert\r\n| project ExtendedProperties,ProductName, ProviderName,TimeGenerated\r\n| where TimeGenerated >= {TimeRange:start} and TimeGenerated <= {TimeRange:end}\r\n| extend AnalyticRuleIdStr = replace('\\\"','',tostring(todynamic(ExtendedProperties)[\"Analytic Rule Ids\"]))\r\n| extend AnaliticRulesInAlertArray= split(substring(AnalyticRuleIdStr,1,string_size(AnalyticRuleIdStr)-2),\",\")\r\n| mv-expand SingleAnaliticRuleID=AnaliticRulesInAlertArray\r\n| extend SingleAnaliticRuleID=iff(ProductName==\"Azure Sentinel\",tostring(SingleAnaliticRuleID),ProductName)\r\n| summarize AlertAmount=count() by SingleAnaliticRuleID\r\n| extend AlertAmount=iff(AlertAmount>0,AlertAmount,0));\r\nlet contentTable= materialize(GetAlertRuleTable()\r\n| join kind=leftouter AlertAmount on $left.AlertRuleId==$right.SingleAnaliticRuleID \r\n| project-away SingleAnaliticRuleID\r\n| extend AlertAmount=iff(AlertAmount>0 or Product!= 'Azure Sentinel',AlertAmount,0)\r\n| join kind=leftouter AlertAmount on $left.Product==$right.SingleAnaliticRuleID\r\n| extend AlertAmount=iff(Product!= 'Azure Sentinel',AlertAmount1,AlertAmount)\r\n| extend AlertAmount=iff(AlertAmount>0,AlertAmount,0)\r\n| project-away AlertAmount1\r\n| sort by AlertAmount desc\r\n| project Status,Product,RuleName,AlertAmount);\r\ncontentTable\r\n| summarize Amount=countif(AlertAmount > 0)\r\n| extend Type='Alerted'\r\n| join kind=fullouter \r\n(contentTable | summarize Amount=countif(AlertAmount == 0)\r\n| extend Type='No alerts') on $left.Type==$right.Type\r\n| extend Type = iff(Type=='',Type1,Type), Amount=iff(Amount>0,Amount,Amount1)\r\n| project Type,Amount\r\n\r\n",
"size": 2,
"timeContext": {
"durationMs": 86400000
},
"queryType": 0,
"resourceType": "microsoft.operationalinsights/workspaces",
"visualization": "piechart",
"chartSettings": {
"seriesLabelSettings": [
{
"seriesName": "No alerts",
"label": "Rules that did not generate alerts",
"color": "red"
},
{
"seriesName": "Alerted",
"label": "Rules that generated alerts",
"color": "greenDark"
}
]
}
},
"customWidth": "100",
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "Rules alert generation status"
}
]
},
"customWidth": "40",
"name": "AlertGenerationGroup"
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "let alertText = strcat_array(dynamic([{AlertRuleID}]),\",\");\r\nlet isProductMarked = (product:string) {\r\n let productText = strcat_array(dynamic([{ProductName}]),\",\");\r\n array_index_of(split(productText,'\\\"'),product)\r\n};\r\nSecurityAlert\r\n| where TimeGenerated >= {TimeRange:start} and TimeGenerated <= {TimeRange:end}\r\n| where isProductMarked(ProductName)!=-1 or ProductName == 'Azure Sentinel'\r\n| extend AlertRuleIDArray= parsejson(tostring(todynamic(ExtendedProperties)[\"Analytic Rule Ids\"]))\r\n| mv-expand SingleAnaliticRuleID=AlertRuleIDArray\r\n| extend SingleAnaliticRuleID= tostring(SingleAnaliticRuleID)\r\n| where alertText has SingleAnaliticRuleID\r\n| summarize AmountOfAlerts=count() by AlertSeverity",
"size": 0,
"title": "Alerts generated, by severity",
"noDataMessage": "No alerts",
"queryType": 0,
"resourceType": "microsoft.operationalinsights/workspaces",
"visualization": "barchart"
},
"customWidth": "60",
"conditionalVisibility": {
"parameterName": "AlertRuleID",
"comparison": "isNotEqualTo"
},
"name": "query alert generation status"
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "let getRuleNameIdTable = (){\r\n let alertText = strcat_array(dynamic([{AlertRuleID}]),\",\");\r\n let RuleName = strcat_array(dynamic([{RuleName}]),\",\");\r\n let rulesData = range x from 0 to array_length(split(alertText,','))-1 step 1\r\n | extend AlertRuleId= tostring(split(alertText,',')[x]),\r\n RuleName=tostring(split(RuleName,',')[x]);\r\n rulesData\r\n};\r\nlet GetAlertRuleTable = (){\r\n let proerties = dynamic([{prop}]);\r\n let TmpRuleTable = datatable (MockColumn:string)[\"Mock\"];\r\n TmpRuleTable\r\n | mv-expand SingleRuleProperties=proerties\r\n | project-away MockColumn\r\n | extend \r\n Product=iff(SingleRuleProperties.productFilter!='',SingleRuleProperties.productFilter,\"Azure Sentinel\"), \r\n RuleName=tostring(SingleRuleProperties.displayName), \r\n MITRE_Tactics=iff(SingleRuleProperties.tactics!='',SingleRuleProperties.tactics,dynamic([])),\r\n Description=SingleRuleProperties.description\r\n | extend Status= iff(SingleRuleProperties.enabled==true,'Enabled',iff(RuleName startswith 'AUTO DISABLED','Auto disabled', 'Disabled'))\r\n | project-away SingleRuleProperties\r\n | join getRuleNameIdTable() on $left.RuleName==$right.RuleName\r\n | project-away RuleName1,x\r\n};\r\nlet AlertAmount = materialize( SecurityAlert\r\n| project ExtendedProperties,ProductName, ProviderName,TimeGenerated\r\n| where TimeGenerated >= {TimeRange:start} and TimeGenerated <= {TimeRange:end}\r\n| extend AnalyticRuleIdStr = replace('\\\"','',tostring(todynamic(ExtendedProperties)[\"Analytic Rule Ids\"]))\r\n| extend AnaliticRulesInAlertArray= split(substring(AnalyticRuleIdStr,1,string_size(AnalyticRuleIdStr)-2),\",\")\r\n| mv-expand SingleAnaliticRuleID=AnaliticRulesInAlertArray\r\n| extend SingleAnaliticRuleID=iff(ProductName==\"Azure Sentinel\",tostring(SingleAnaliticRuleID),ProductName)\r\n| summarize AlertAmount=count() by SingleAnaliticRuleID\r\n| extend AlertAmount=iff(AlertAmount>0,AlertAmount,0));\r\nGetAlertRuleTable()\r\n| join kind=leftouter AlertAmount on $left.AlertRuleId==$right.SingleAnaliticRuleID \r\n| project-away SingleAnaliticRuleID\r\n| extend AlertAmount=iff(AlertAmount>0 or Product!= 'Azure Sentinel',AlertAmount,0)\r\n| join kind=leftouter AlertAmount on $left.Product==$right.SingleAnaliticRuleID\r\n| extend AlertAmount=iff(Product!= 'Azure Sentinel',AlertAmount1,AlertAmount)\r\n| extend AlertAmount=iff(AlertAmount>0,AlertAmount,0)\r\n| project-away AlertAmount1\r\n| sort by AlertAmount desc\r\n| project Status,Product,RuleName,AlertAmount",
"size": 0,
"title": "Rules alert amount table ",
"timeContext": {
"durationMs": 86400000
},
"queryType": 0,
"resourceType": "microsoft.operationalinsights/workspaces",
"gridSettings": {
"formatters": [
{
"columnMatch": "Status",
"formatter": 18,
"formatOptions": {
"thresholdsOptions": "icons",
"thresholdsGrid": [
{
"operator": "==",
"thresholdValue": "Enabled",
"representation": "success",
"text": ""
},
{
"operator": "==",
"thresholdValue": "Disabled",
"representation": "disabled",
"text": ""
},
{
"operator": "Default",
"thresholdValue": null,
"representation": "failed",
"text": ""
}
],
"compositeBarSettings": {
"labelText": "",
"columnSettings": []
},
"customColumnWidthSetting": "11ch"
}
},
{
"columnMatch": "Product",
"formatter": 0,
"formatOptions": {
"customColumnWidthSetting": "30.7143ch"
}
},
{
"columnMatch": "AlertAmount",
"formatter": 8,
"formatOptions": {
"palette": "magenta"
}
},
{
"columnMatch": "MITRE_Tactics",
"formatter": 5
},
{
"columnMatch": "Description",
"formatter": 5
},
{
"columnMatch": "AlertRuleId",
"formatter": 5
},
{
"columnMatch": "SingleAnaliticRuleID",
"formatter": 5
}
],
"labelSettings": [
{
"columnId": "Status",
"comment": ""
},
{
"columnId": "Product"
},
{
"columnId": "RuleName",
"label": "Rule name"
},
{
"columnId": "AlertAmount",
"label": "Number of alerts"
}
]
},
"sortBy": []
},
"customWidth": "56",
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "Alert amount table query"
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "let alertText = strcat_array(dynamic([{AlertRuleID}]),\",\");\r\nlet isProductMarked = (product:string) {\r\n let productText = strcat_array(dynamic([{ProductName}]),\",\");\r\n array_index_of(split(productText,','),product)\r\n};\r\nSecurityAlert\r\n| where TimeGenerated >= {TimeRange:start} and TimeGenerated <= {TimeRange:end}\r\n| where isProductMarked(ProductName)!=-1 or ProductName == 'Azure Sentinel'\r\n| extend AlertRuleIDArray= parsejson(tostring(todynamic(ExtendedProperties)[\"Analytic Rule Ids\"]))\r\n| mv-expand SingleAnaliticRuleID=AlertRuleIDArray\r\n| extend SingleAnaliticRuleID= tostring(SingleAnaliticRuleID)\r\n| where alertText has SingleAnaliticRuleID\r\n| summarize count() by ProductName\r\n",
"size": 2,
"title": "Number of alerts by provider",
"noDataMessage": "No alerts created",
"queryType": 0,
"resourceType": "microsoft.operationalinsights/workspaces",
"visualization": "piechart",
"graphSettings": {
"type": 0,
"topContent": {
"columnMatch": "ProviderName",
"formatter": 1
},
"centerContent": {
"columnMatch": "count_",
"formatter": 1,
"numberFormat": {
"unit": 17,
"options": {
"maximumSignificantDigits": 3,
"maximumFractionDigits": 2
}
}
}
},
"mapSettings": {
"locInfo": "LatLong",
"sizeSettings": "count_",
"sizeAggregation": "Sum",
"legendMetric": "count_",
"legendAggregation": "Sum",
"itemColorSettings": {
"type": "heatmap",
"colorAggregation": "Sum",
"nodeColorField": "count_",
"heatmapPalette": "greenRed"
}
}
},
"customWidth": "38",
"conditionalVisibility": {
"parameterName": "AlertRuleID",
"comparison": "isNotEqualTo"
},
"name": "query numver of alert by provider"
},
{
"type": 1,
"content": {
"json": "### Count of alerts, by rule\nThe total number of alerts generated by each rule over the selected time period "
},
"conditionalVisibility": {
"parameterName": "prop",
"comparison": "isNotEqualTo"
},
"name": "heading text alert amount created by each rules "
},
{
"type": 1,
"content": {
"json": "### Help\r\nHere you can see the amount of alerts created from each rule you selected. \r\nMonitoring the amount of alert each detection is creating is essential for the SOC.\r\n\r\n",
"style": "info"
},
"customWidth": "80",
"conditionalVisibilities": [
{
"parameterName": "Help",
"comparison": "isEqualTo",
"value": "Yes"
},
{
"parameterName": "prop",
"comparison": "isNotEqualTo"
}
],
"name": "Help text alert amount created by each rules "
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "let alertText = strcat_array(dynamic([{AlertRuleID}]),\",\");\r\nlet isProductMarked = (product:string) {\r\n let productText = strcat_array(dynamic([{ProductName}]),\",\");\r\n array_index_of(split(productText,'\\\"'),product)\r\n};\r\nSecurityAlert\r\n| project TimeGenerated,ProductName,ExtendedProperties\r\n| where TimeGenerated >= {TimeRange:start} and TimeGenerated <= {TimeRange:end}\r\n| extend AlertRuleName = parsejson(tostring(todynamic(ExtendedProperties)[\"Analytic Rule Name\"]))\r\n| extend AlertRuleIDArray= parsejson(tostring(todynamic(ExtendedProperties)[\"Analytic Rule Ids\"]))\r\n| mv-expand SingleAnaliticRuleID=AlertRuleIDArray\r\n| extend SingleAnaliticRuleID=tostring(SingleAnaliticRuleID)\r\n| project SingleAnaliticRuleID,AlertRuleName, ProductName\r\n| where isProductMarked(ProductName)!=-1 or ProductName == \"Azure Sentinel\" and alertText has SingleAnaliticRuleID\r\n| extend AlertRuleIdentifier = iff(ProductName==\"Azure Sentinel\", tostring(AlertRuleName), tostring(ProductName))\r\n| summarize AlertAmount=count() by AlertRuleIdentifier",
"size": 0,
"noDataMessage": "No alerts created",
"queryType": 0,
"resourceType": "microsoft.operationalinsights/workspaces",
"visualization": "barchart",
"chartSettings": {
"createOtherGroup": 6
}
},
"customWidth": "80",
"conditionalVisibility": {
"parameterName": "AlertRuleID",
"comparison": "isNotEqualTo"
},
"name": "Alerts generated by selected rules"
}
]
},
"conditionalVisibility": {
"parameterName": "TAB",
"comparison": "isEqualTo",
"value": "Alert"
},
"name": "AlertGroup"
},
{
"type": 1,
"content": {
"json": ""
},
"name": "text - 19"
}
],
"fromTemplateId": "sentinel-AnalyticsEfficiency",
"$schema": "https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json"
}