RULE 17 Anomalous Management Operations
Query
// Rule : Azure - Anomalous Spike in Management Operations (Statistical Baseline Deviation)
// Severity: Medium
// Tactics : Discovery, Impact, DefenseEvasion
// MITRE : T1078, T1580
// Freq : PT1H Period: P14D
//==========================================================================================
let DeviationThreshold = 3.0; // sigma — 3 standard deviations above personal mean
// Tuned: raised minimum-ops floor 20 -> 30 to suppress low-volume identities where a modest
// spike from a small baseline mean flips into a large sigma score.
let MinOpsThreshold = 30;
let MinBaselineDays = 3; // require at least 3 days of history to build a baseline
let MinAvgOpsPerHour = 0.5; // require some historical activity so tiny baselines aren't amplified
let ExcludedPatterns = dynamic(["terraform", "bicep", "pipeline", "github", "pulumi",
"devops", "arm-deployment"]);
let AzurePlatformIPs = dynamic(["168.63.", "169.254."]);
// ── Baseline: per-caller hourly operation counts for the last 13 days ──────────────────
let Baseline = AzureActivity
| where TimeGenerated between (ago(14d) .. ago(1h))
| where ActivityStatusValue =~ "Success"
| where OperationNameValue has_any ("WRITE", "DELETE", "ACTION")
| where not(OperationNameValue has_any ("READ", "LIST", "GET", "LISTKEYS"))
| where isnotempty(Caller) and isnotempty(CallerIpAddress)
| where not(CallerIpAddress has_any (AzurePlatformIPs))
| where not(tolower(Caller) has_any (ExcludedPatterns))
| summarize HourlyOps = count() by Caller, HourBin = bin(TimeGenerated, 1h)
| summarize
AvgOpsPerHour = avg(HourlyOps),
StdDevOps = stdev(HourlyOps),
SampleDays = dcount(bin(HourBin, 1d))
by Caller
| where SampleDays >= MinBaselineDays;
// ── Current 1-hour window ──────────────────────────────────────────────────────────────
let CurrentOps = AzureActivity
| where TimeGenerated > ago(1h)
| where ActivityStatusValue =~ "Success"
| where OperationNameValue has_any ("WRITE", "DELETE", "ACTION")
| where not(OperationNameValue has_any ("READ", "LIST", "GET", "LISTKEYS"))
| where isnotempty(Caller) and isnotempty(CallerIpAddress)
| where not(CallerIpAddress has_any (AzurePlatformIPs))
| where not(tolower(Caller) has_any (ExcludedPatterns))
| summarize
CurrentHourOps = count(),
Operations = make_set(OperationNameValue, 10),
AffectedResources = make_set(ResourceId, 10),
SourceIPs = make_set(CallerIpAddress, 5),
CallerIP = any(CallerIpAddress),
SubscriptionIds = make_set(SubscriptionId, 5),
ResourceGroups = make_set(ResourceGroup, 5),
FirstOp = min(TimeGenerated),
LastOp = max(TimeGenerated)
by Caller;
// ── Join & score ──────────────────────────────────────────────────────────────────────
CurrentOps
| join kind=inner Baseline on Caller
| where CurrentHourOps >= MinOpsThreshold
| where AvgOpsPerHour >= MinAvgOpsPerHour
| extend DeviationScore = iff(
StdDevOps > 0,
(CurrentHourOps - AvgOpsPerHour) / StdDevOps,
toreal(CurrentHourOps))
| where DeviationScore >= DeviationThreshold
| project
Caller,
CallerIP,
SourceIPs,
CurrentHourOps,
AvgOpsPerHour = round(AvgOpsPerHour, 1),
StdDevOps = round(StdDevOps, 1),
DeviationScore = round(DeviationScore, 1),
Operations,
AffectedResources,
SubscriptionIds,
ResourceGroups,
FirstOp,
LastOp
| extend
AccountName = tostring(split(Caller, "@")[0]),
AccountUPNSuffix = tostring(split(Caller, "@")[1])Explanation
This query is designed to detect unusual spikes in management operations within Azure, which could indicate potential security threats. Here's a simplified breakdown:
-
Purpose: The query identifies anomalies in the number of management operations performed by users, which deviate significantly from their usual activity patterns. This can help detect potential security incidents such as unauthorized access or misuse.
-
Severity and Tactics: The rule is of medium severity and aligns with tactics like Discovery, Impact, and Defense Evasion, referencing specific MITRE techniques (T1078, T1580).
-
Frequency and Period: The query runs every hour and analyzes data from the past 14 days.
-
Thresholds and Filters:
- DeviationThreshold: An anomaly is flagged if the current activity is 3 standard deviations above the user's average.
- MinOpsThreshold: Only users with at least 30 operations in the current hour are considered, to avoid noise from low-volume users.
- MinBaselineDays: A baseline is established only if there are at least 3 days of historical data.
- MinAvgOpsPerHour: Users must have an average of at least 0.5 operations per hour historically to be considered.
- ExcludedPatterns: Operations related to specific tools (e.g., terraform, github) are excluded to reduce false positives.
- AzurePlatformIPs: Operations from known Azure platform IPs are excluded.
-
Baseline Calculation:
- The query calculates the average and standard deviation of hourly operations for each user over the past 13 days, excluding certain operations and IPs.
-
Current Activity:
- It examines the operations performed in the last hour, summarizing details like the number of operations, types of operations, resources affected, and source IPs.
-
Anomaly Detection:
- The current activity is compared against the baseline. If the number of operations in the last hour significantly exceeds the user's historical average (by more than 3 standard deviations), it's flagged as anomalous.
-
Output:
- The query outputs details about the user (Caller), their IP, the number of operations, deviation score, and other relevant information like affected resources and subscription IDs.
In summary, this query helps identify users whose recent activity significantly deviates from their normal behavior, potentially indicating suspicious activity.
Details

David Alonso
Released: July 27, 2026
Tables
Keywords
Operators