Query Details
// HQ-011 — Privileged Role Changes Monitoring
// Purpose : Monitor all Add / Remove operations targeting high-privilege Entra ID roles.
// Covers both PIM-managed and direct (outside-PIM) assignments.
// Highlights direct assignments as elevated risk.
// Tables : AuditLogs
// MITRE : TA0004 Privilege Escalation — T1078.004 Valid Accounts: Cloud Accounts
// TA0003 Persistence — T1098.003 Account Manipulation: Additional Cloud Roles
// Author : Custom
// ---------------------------------------------------------------------------
let LookbackDays = 14d;
let PrivilegedRoles = dynamic([
"Global Administrator",
"Security Administrator",
"Privileged Role Administrator",
"User Administrator",
"Authentication Administrator",
"Privileged Authentication Administrator",
"Exchange Administrator",
"SharePoint Administrator",
"Compliance Administrator",
"Conditional Access Administrator",
"Application Administrator",
"Cloud Application Administrator",
"Helpdesk Administrator",
"Password Administrator",
"Security Operator",
"Security Reader"
]);
let RoleChangeActivities = dynamic([
"Add member to role",
"Remove member from role",
"Add eligible member to role",
"Remove eligible member from role",
"Add scoped member to role",
"Remove scoped member from role",
"Add permanent member to role",
"Remove permanent member from role",
"Add member to role outside of PIM",
"Add eligible member to role (permanent)"
]);
// Extract events then expand TargetResources to get both Role and User entries
AuditLogs
| where TimeGenerated > ago(LookbackDays)
| where Category == "RoleManagement"
| where ActivityDisplayName in~ (RoleChangeActivities)
| extend InitiatorUPN = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatorApp = tostring(InitiatedBy.app.displayName)
| extend InitiatorSPId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatorType = iff(isnotempty(tostring(InitiatedBy.user.userPrincipalName)), "User", "Application")
// Expand each TargetResource to find role name and target user
| mv-expand TargetResource = TargetResources
| extend ResType = tostring(TargetResource.type)
| extend ResName = tostring(TargetResource.displayName)
| extend ResUserUPN = tostring(TargetResource.userPrincipalName)
// Reconstruct one row per audit event — pick role name and target user across all typed entries
| summarize
RoleName = max(iff(ResType == "Role", ResName, "")),
TargetUserUPN = tolower(max(iff(isnotempty(ResUserUPN), ResUserUPN, "")))
by TimeGenerated, ActivityDisplayName, LoggedByService, Result,
InitiatorUPN, InitiatorApp, InitiatorSPId, InitiatorType, Id, CorrelationId
// Keep only rows where role is in privileged list
| where RoleName in (PrivilegedRoles)
// Annotate
| extend OperationType = case(
ActivityDisplayName has "Add", "Role Assigned",
ActivityDisplayName has "Remove", "Role Removed",
"Other"
)
| extend AssignmentSource = case(
LoggedByService in ("PIM", "Privileged Identity Management"), "PIM-Managed",
ActivityDisplayName has "outside of PIM", "DirectAssignment-HighRisk",
"DirectAssignment-HighRisk"
)
// Flag direct assignments (not via PIM) as high-risk entries
| extend RiskSignal = iff(AssignmentSource == "DirectAssignment-HighRisk",
strcat("ALERT: Privileged role assigned outside PIM by ", coalesce(InitiatorUPN, InitiatorApp)),
"INFO: PIM-managed role change"
)
| project
TimeGenerated,
RoleName,
TargetUserUPN,
OperationType,
AssignmentSource,
InitiatorType,
InitiatorUPN,
InitiatorApp,
LoggedByService,
Result,
RiskSignal,
ActivityDisplayName,
CorrelationId
| sort by TimeGenerated desc
This query is designed to monitor changes in high-privilege roles within Entra ID (formerly Azure Active Directory) over the past 14 days. It focuses on tracking the addition and removal of users to/from these roles, whether managed through Privileged Identity Management (PIM) or directly assigned outside of PIM. Direct assignments are flagged as high-risk due to their potential security implications.
Here's a simplified breakdown of what the query does:
Define Lookback Period and Roles: It sets a 14-day period to look back and specifies a list of high-privilege roles to monitor.
Identify Role Change Activities: It lists the types of role change activities to track, such as adding or removing members from roles.
Filter Audit Logs: It filters the AuditLogs table to find entries related to role management activities within the specified timeframe.
Extract and Expand Data: It extracts relevant information about who initiated the change (user or application) and expands the target resources to identify the specific role and user involved.
Summarize Events: It reconstructs the data to ensure each event has a clear role name and target user, and filters to keep only those involving the specified privileged roles.
Annotate and Flag Risks: It categorizes the operation type (role assigned or removed) and identifies whether the assignment was managed by PIM or directly assigned. Direct assignments are flagged as high-risk.
Output and Sort: It projects the relevant fields and sorts the results by the time the event was generated, showing the most recent events first.
The query helps identify potentially risky role changes, especially those not managed through PIM, and provides alerts for security teams to investigate further.

David Alonso
Released: April 6, 2026
Tables
Keywords
Operators