Detection rules › Kusto
M2131_LogRetentionLessThan1Year
'This alert is designed to monitor log retention within the Maturity Model for Event Log Management (M-21-31) standard. The alert triggers when a log analytics workspace in active storage is configured for less than 1 year.'
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Discovery |
Rule body
id: 8178a514-1270-4e31-a1d9-aaafeb40122f
name: M2131_LogRetentionLessThan1Year
description: |
'This alert is designed to monitor log retention within the Maturity Model for Event Log Management (M-21-31) standard. The alert triggers when a log analytics workspace in active storage is configured for less than 1 year.'
severity: Medium
status: Available
requiredDataConnectors: []
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Discovery
relevantTechniques:
- T1082
query: |
SecurityRecommendation
| summarize arg_max(TimeGenerated, *) by RecommendationDisplayName, AssessedResourceId
| where RecommendationDisplayName == "Activity log should be retained for at least one year"
| summarize Failed = countif(RecommendationState == "Unhealthy"), Passed = countif(RecommendationState == "Healthy"), Total = countif(RecommendationState == "Healthy" or RecommendationState == "Unhealthy") by RecommendationDisplayName, AssessedResourceId
| extend PassedControls = (Passed/todouble(Total))*100
| extend RemediationLink = strcat('https://portal.azure.com/#blade/Microsoft_Azure_Security/SecurityMenuBlade/22')
| project RecommendationDisplayName, AssessedResourceId, Total, PassedControls, Passed, Failed, RemediationLink, LastObserved=now()
| where Failed > 0
| sort by Failed desc
| extend URLCustomEntity = RemediationLink, ResourceId = AssessedResourceId
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: URLCustomEntity
- entityType: AzureResource
fieldMappings:
- identifier: ResourceId
columnName: AssessedResourceId
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
SecurityRecommendation
Stage 2: summarize
| summarize arg_max(TimeGenerated, *) by RecommendationDisplayName, AssessedResourceId
Stage 3: where
| where RecommendationDisplayName == "Activity log should be retained for at least one year"
Stage 4: summarize
| summarize Failed = countif(RecommendationState == "Unhealthy"), Passed = countif(RecommendationState == "Healthy"), Total = countif(RecommendationState == "Healthy" or RecommendationState == "Unhealthy") by RecommendationDisplayName, AssessedResourceId
Stage 5: extend
| extend PassedControls = (Passed/todouble(Total))*100
Stage 6: extend
| extend RemediationLink = strcat('https://portal.azure.com/#blade/Microsoft_Azure_Security/SecurityMenuBlade/22')
Stage 7: project
| project RecommendationDisplayName, AssessedResourceId, Total, PassedControls, Passed, Failed, RemediationLink, LastObserved=now()
Stage 8: where
| where Failed > 0
Stage 9: sort
| sort by Failed desc
Stage 10: extend
| extend URLCustomEntity = RemediationLink, ResourceId = AssessedResourceId
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Failed | gt |
| field:"Failed" kind:gt value:"0" |
RecommendationDisplayName | eq |
| field:"RecommendationDisplayName" kind:eq value:"Activity log should be retained for at least one year" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AssessedResourceId | project |
Failed | project |
LastObserved | project |
Passed | project |
PassedControls | project |
RecommendationDisplayName | project |
RemediationLink | project |
Total | project |
ResourceId | extend |
URLCustomEntity | extend |