Query Details
let _MonitoredResources =
_GetWatchlist("ResourceId-AuditAzureResources")
| where Notes has "[Snapshot]"
| project ResourceId, SubscriptionId, ResourceGroup, Resource
;
AzureActivity
| where OperationNameValue has_any ("/snapshots/beginGetAccess", "/snapshots/write") // "/snapshots/delete", "/snapshots/endGetAccess"
| where _ResourceId has_any (toscalar(_MonitoredResources | where isempty(Resource) | summarize make_list(ResourceId)))
or _ResourceId has_any (toscalar(_MonitoredResources | where isnotempty(Resource) | extend ResourceId = replace_regex(ResourceId, @"^(.+?\/)[^\/]+(\/[^\/]+)$", @"\1snapshots\2") | summarize make_list(ResourceId)))
| extend PreferenceInteger = iff(ResourceProviderValue == toupper(ResourceProviderValue), 0, 1)
// Group together Start, Accept, Success... operations
| summarize hint.shufflekey=CorrelationId
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated),
PropertiesDynamic = make_bag(pack(ActivityStatusValue, iff(PreferenceInteger == 1, todynamic(Properties), Properties_d))),
EventDataId = array_sort_asc(make_list(EventDataId)),
arg_max(TimeGenerated, *)
by CorrelationId, ResourceProviderValue, OperationNameValue, _ResourceId
// Group together two kinds of logs (where ResourceProviderValue is all caps or title - e.g. MICROSOFT.AUTHORIZATION or Microsoft.Authorization)
| summarize hint.shufflekey=CorrelationId
PropertiesDynamic = make_bag(pack(ResourceProviderValue, PropertiesDynamic)),
EventDataId = make_bag(pack(ResourceProviderValue, EventDataId)),
take_any(TenantId, SourceSystem, CategoryValue, SubscriptionId, Type),
arg_min(PreferenceInteger, CallerIpAddress, Authorization, Authorization_d, Claims_d, Properties_d, EventSubmissionTimestamp, Hierarchy),
arg_max(PreferenceInteger, Level, OperationNameValue, Caller, HTTPRequest, OperationId, ResourceGroup, ResourceProviderValue, ActivityStatusValue, ActivitySubstatusValue, OperationName, ActivityStatus, ActivitySubstatus, Category, ResourceId, ResourceProvider, Resource)
by CorrelationId, TimeGenerated, StartTime, EndTime, _ResourceId
| project
TimeGenerated,
StartTime,
EndTime,
ResourceProvider,
Category,
CategoryValue,
ResourceProviderValue,
Level,
CallerIpAddress,
Caller,
OperationName,
OperationNameValue,
ActivityStatusValue,
//ActivityStatus,
ActivitySubstatusValue,
ActivitySubstatus,
SubscriptionId,
ResourceGroup,
Resource,
ResourceId,
_ResourceId,
Authorization,
PropertiesDynamic,
HTTPRequest,
Authorization_d,
Properties_d,
Claims_d,
Hierarchy,
OperationId,
CorrelationId,
EventSubmissionTimestamp,
EventDataId,
Type
This KQL (Kusto Query Language) query is designed to analyze Azure activity logs, specifically focusing on operations related to "snapshots" within Azure resources. Here's a simplified breakdown of what the query does:
Define Monitored Resources:
ResourceId, SubscriptionId, ResourceGroup, and Resource.Filter Azure Activity Logs:
AzureActivity logs, filtering for operations that involve accessing or writing snapshots (e.g., "/snapshots/beginGetAccess", "/snapshots/write").Resource field is empty or not, adjusting the ResourceId format accordingly.Process and Group Logs:
PreferenceInteger to differentiate between logs where the ResourceProviderValue is in uppercase versus title case.CorrelationId, and various operations are summarized, such as the start and end times, properties, and event data IDs.Further Grouping and Aggregation:
CorrelationId and other fields, aggregating properties and event data IDs into bags (collections).arg_min and arg_max functions to select specific log entries based on the PreferenceInteger and other criteria.Project Final Output:
In summary, this query is designed to monitor and analyze specific snapshot-related operations on Azure resources, providing detailed insights into these activities by filtering, grouping, and summarizing relevant log data.

Jose Sebastián Canós
Released: April 15, 2025
Tables
Keywords
Operators