RULE 09 Privileged Ops New IP
Query
// Rule : Azure - Privileged Management Operations from a New Source IP for Established Identity
// Severity: Medium
// Tactics : InitialAccess, Persistence
// MITRE : T1078
// Freq : PT6H Period: P14D
//==========================================================================================
let PrivilegedOps = dynamic([
"MICROSOFT.AUTHORIZATION/ROLEASSIGNMENTS/WRITE",
"MICROSOFT.AUTHORIZATION/ROLEASSIGNMENTS/DELETE",
"MICROSOFT.KEYVAULT/VAULTS/WRITE",
"MICROSOFT.KEYVAULT/VAULTS/DELETE",
"MICROSOFT.COMPUTE/VIRTUALMACHINES/WRITE",
"MICROSOFT.INSIGHTS/DIAGNOSTICSETTINGS/DELETE",
"MICROSOFT.AUTOMATION/AUTOMATIONACCOUNTS/RUNBOOKS/WRITE",
"MICROSOFT.AUTHORIZATION/LOCKS/DELETE"
]);
let HistoricalCallerIPs = AzureActivity
| where TimeGenerated between (ago(14d) .. ago(6h))
| where OperationNameValue has_any (PrivilegedOps)
| where ActivityStatusValue =~ "Success"
| where isnotempty(Caller) and isnotempty(CallerIpAddress)
| summarize HistoricalIPs = make_set(CallerIpAddress, 50) by Caller;
AzureActivity
| where TimeGenerated > ago(6h)
| where OperationNameValue has_any (PrivilegedOps)
| where ActivityStatusValue =~ "Success"
| where isnotempty(CallerIpAddress)
| where CallerIpAddress !startswith "168.63." and CallerIpAddress !startswith "169.254."
| join kind=inner HistoricalCallerIPs on Caller
| where not(set_has_element(HistoricalIPs, CallerIpAddress))
| summarize
OperationCount = count(),
DistinctOps = dcount(OperationNameValue),
Operations = make_set(OperationNameValue, 10),
AffectedResources = make_set(ResourceId, 10),
CallerIP = any(CallerIpAddress),
SourceIPs = make_set(CallerIpAddress, 5),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by Caller, SubscriptionId
// Tuned: require >=3 privileged operations from the new IP (single-op fires drove most FPs;
// legitimate admins reconnecting from a new IP typically perform many actions in short order).
| where OperationCount >= 3 or DistinctOps >= 2
| extend
AccountName = tostring(split(Caller, "@")[0]),
AccountUPNSuffix = tostring(split(Caller, "@")[1])Explanation
This query is designed to detect potentially suspicious activity in Azure by identifying privileged management operations performed from new IP addresses for established user identities. Here's a simplified breakdown of what the query does:
-
Define Privileged Operations: It starts by listing specific privileged operations, such as creating or deleting role assignments, modifying key vaults, and altering virtual machines, among others.
-
Historical IP Collection: It looks back over a 14-day period (excluding the last 6 hours) to gather a list of IP addresses that have successfully performed these privileged operations for each user.
-
Recent Activity Check: It then examines the last 6 hours of activity to find successful privileged operations.
-
Filter New IPs: The query filters out operations from known internal IP ranges and focuses on operations from IP addresses that haven't been seen in the historical data for the same user.
-
Summarize Suspicious Activity: It summarizes the findings by counting the number of operations, identifying distinct operations, and listing affected resources and source IPs. It also records the first and last time the operations were seen.
-
Threshold for Alerts: To reduce false positives, it only flags cases where there are at least three operations from a new IP or two different types of operations.
-
User Information: Finally, it extracts and displays the account name and domain from the user's email address.
In essence, this query helps identify unusual behavior by detecting when a user performs multiple privileged actions from an IP address they haven't used recently, which could indicate unauthorized access or a compromised account.
Details

David Alonso
Released: July 27, 2026
Tables
Keywords
Operators
MITRE Techniques