AnalysisType: rule
Filename: azure_restore_point_collection_deleted.py
RuleID: "Azure.MonitorActivity.Compute.RestorePointCollectionDeleted"
DisplayName: "Azure Restore Point Collection Deleted"
Enabled: true
LogTypes:
- Azure.MonitorActivity
Severity: Medium
Description: >
Detects when an Azure restore point collection is deleted. Restore point collections contain
crash-consistent and application-consistent recovery points for virtual machines. Adversaries
may delete these collections to prevent system recovery, destroy forensic evidence, or undermine
backup strategies before launching ransomware attacks. This is a strong indicator of inhibiting
system recovery capabilities.
Reports:
MITRE ATT&CK:
- TA0040:T1490 # Impact: Inhibit System Recovery
- TA0040:T1485 # Impact: Data Destruction
Tags:
- Impact
- Data Destruction
- Inhibit System Recovery
- Ransomware
Runbook: |
1. Query Azure Monitor Activity logs for all backup and recovery operations (restore point deletions, snapshot deletions, disk deletions) by the callerIpAddress in the 24 hours before and after the alert
2. Find all restore point collection and snapshot deletions in the past 6 hours to determine if this is part of a pre-ransomware attack pattern
3. Check if the callerIpAddress has deleted recovery resources in the past 90 days to establish if this is normal maintenance activity
Reference: https://github.com/elastic/detection-rules/blob/main/rules/integrations/azure/impact_azure_virtual_machine_restore_point_collection_deleted.toml
SummaryAttributes:
- resourceId
- callerIpAddress
- correlationId
- location
Tests:
- Name: Restore Point Collection Deleted Successfully
ExpectedResult: true
Log:
{
"time": "2025-12-22T10:30:00.0000000Z",
"resourceId": "/subscriptions/12345678-1234-1234-1234-123456789abc/resourceGroups/backup-rg/providers/Microsoft.Compute/restorePointCollections/vm-restore-points",
"operationName": "MICROSOFT.COMPUTE/RESTOREPOINTCOLLECTIONS/DELETE",
"operationVersion": "2021-12-01",
"category": "Administrative",
"resultType": "Success",
"resultSignature": "200",
"callerIpAddress": "1.1.1.1",
"correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"level": "Informational",
"location": "eastus",
"tenantId": "87654321-4321-4321-4321-111111111111"
}
- Name: Restore Point Collection Deleted Case Insensitive
ExpectedResult: true
Log:
{
"time": "2025-12-22T11:15:00.0000000Z",
"resourceId": "/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/prod-backups/providers/Microsoft.Compute/restorePointCollections/prod-vm-collection",
"operationName": "microsoft.compute/restorepointcollections/delete",
"category": "Administrative",
"resultType": "Succeeded",
"callerIpAddress": "2.2.2.2",
"correlationId": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
"level": "Information",
"location": "westeurope",
"tenantId": "22222222-2222-2222-2222-222222222222"
}
- Name: Different Operation
ExpectedResult: false
Log:
{
"time": "2025-12-22T13:00:00.0000000Z",
"resourceId": "/subscriptions/44444444-4444-4444-4444-444444444444/resourceGroups/backup-rg/providers/Microsoft.Compute/restorePointCollections/new-collection",
"operationName": "MICROSOFT.COMPUTE/RESTOREPOINTCOLLECTIONS/WRITE",
"category": "Administrative",
"resultType": "Success",
"callerIpAddress": "4.4.4.4",
"correlationId": "d4e5f6a7-b8c9-0123-def0-456789012345",
"tenantId": "44444444-4444-4444-4444-444444444444"
}
# ------ paired body: azure_restore_point_collection_deleted.py ------
from panther_azureactivity_helpers import (
azure_activity_alert_context,
azure_activity_success,
extract_resource_name_from_id,
)
RESTORE_POINT_DELETE_OPERATION = "MICROSOFT.COMPUTE/RESTOREPOINTCOLLECTIONS/DELETE"
def rule(event):
return event.get(
"operationName", ""
).upper() == RESTORE_POINT_DELETE_OPERATION and azure_activity_success(event)
def title(event):
resource_id = event.get("resourceId", "<UNKNOWN_RESOURCE>")
collection_name = extract_resource_name_from_id(
resource_id, "restorePointCollections", default="<UNKNOWN_COLLECTION>"
)
return f"Azure Restore Point Collection Deleted: [{collection_name}]"
def alert_context(event):
context = azure_activity_alert_context(event)
resource_id = event.get("resourceId", "")
resource_group = extract_resource_name_from_id(resource_id, "resourceGroups", default="")
if resource_group:
context["resource_group"] = resource_group
return context