let mass_delete_threshold = 10000;
let query_frequency = 1d;
let delete_activities = DataverseActivity
| where TimeGenerated >= ago(query_frequency)
| where Message == "Delete";
union
(
delete_activities
| summarize FirstEvent = min(TimeGenerated), TotalEvents = count() by UserId, InstanceUrl
| where TotalEvents > mass_delete_threshold
| join kind=inner (
delete_activities
| summarize DeleteCount = count() by UserId, InstanceUrl, ClientIp, EntityName)
on UserId, InstanceUrl
| extend Entities = bag_pack("Entity", EntityName, "Count", DeleteCount)
| summarize Details = make_set(Entities, 100), FirstEvent = min(FirstEvent) by UserId, InstanceUrl, ClientIp, TotalEvents
),
(
DataverseActivity
| where TimeGenerated >= ago(query_frequency)
| where Message == "BulkDelete"
| summarize FirstEvent = min(TimeGenerated), TotalEvents = count() by UserId, InstanceUrl, ClientIp
| extend Details = todynamic("Bulk delete scheduled")
)
| extend
CloudAppId = int(32780),
AccountName = tostring(split(UserId, '@')[0]),
UPNSuffix = tostring(split(UserId, '@')[1])
| project
FirstEvent,
UserId,
ClientIp,
TotalEvents,
Details,
InstanceUrl,
CloudAppId,
AccountName,
UPNSuffix
queryPeriod: 14d
query: |
let mass_delete_threshold = 10000;
let query_frequency = 1d;
let delete_activities = DataverseActivity
| where TimeGenerated >= ago(query_frequency)
| where Message == "Delete";
union
(
delete_activities
| summarize FirstEvent = min(TimeGenerated), TotalEvents = count() by UserId, InstanceUrl
| where TotalEvents > mass_delete_threshold
| join kind=inner (
delete_activities
| summarize DeleteCount = count() by UserId, InstanceUrl, ClientIp, EntityName)
on UserId, InstanceUrl
| extend Entities = bag_pack("Entity", EntityName, "Count", DeleteCount)
| summarize Details = make_set(Entities, 100), FirstEvent = min(FirstEvent) by UserId, InstanceUrl, ClientIp, TotalEvents
),
(
DataverseActivity
| where TimeGenerated >= ago(query_frequency)
| where Message == "BulkDelete"
| summarize FirstEvent = min(TimeGenerated), TotalEvents = count() by UserId, InstanceUrl, ClientIp
| extend Details = todynamic("Bulk delete scheduled")
)
| extend
CloudAppId = int(32780),
AccountName = tostring(split(UserId, '@')[0]),
UPNSuffix = tostring(split(UserId, '@')[1])
| project
FirstEvent,
UserId,
ClientIp,
TotalEvents,
Details,
InstanceUrl,
CloudAppId,
AccountName,
UPNSuffix
version: 3.2.0
name: Dataverse - Mass deletion of records
entityMappings:
- fieldMappings:
- columnName: AccountName
identifier: Name
- columnName: UPNSuffix
identifier: UPNSuffix
entityType: Account
- fieldMappings:
- columnName: CloudAppId
identifier: AppId
- columnName: InstanceUrl
identifier: InstanceName
entityType: CloudApplication
- fieldMappings:
- columnName: ClientIp
identifier: Address
entityType: IP
eventGroupingSettings:
aggregationKind: AlertPerResult
queryFrequency: 1h
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Business Applications/Analytic Rules/Dataverse - Mass deletion of records.yaml
alertDetailsOverride:
alertDisplayNameFormat: 'Dataverse - mass deletion or bulk deletion job detected in {{InstanceUrl}} '
alertDescriptionFormat: '{{UserId}} triggered the mass deletion detection with the following information: {{Details}}'
description: Identifies large scale record delete operations based on a predefined threshold and also detects scheduled bulk deletion jobs.
kind: Scheduled
status: Available
severity: Medium
requiredDataConnectors:
- connectorId: Dataverse
dataTypes:
- DataverseActivity
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
id: 716cf6d4-97ad-407b-923e-6790083acb58
relevantTechniques:
- T1485