Query Details

HUNT 06 Orphaned Privileged Roles

Query

// Hunt     : Hunt - Privileged Role Assignments Matching Disabled or Deleted AAD Identities
// Tactics  : Persistence,PrivilegeEscalation
// MITRE    : T1098.003
// Purpose  : Finds role assignments made AFTER the target identity was disabled or deleted. This indicates either an attacker re-using a known principal ID to maintain access, or a cleanup gap creating a dormant privilege.
//==========================================================================================

let DisabledUsers = AuditLogs
    | where TimeGenerated > ago(90d)
    | where OperationName has_any ("Disable account", "Delete user", "Update user")
    | where Result =~ "success"
    | where OperationName !has "Enable"
    | extend TargetId = tostring(TargetResources[0].id), TargetUPN = tostring(TargetResources[0].userPrincipalName)
    | summarize DisabledAt = max(ActivityDateTime) by TargetId, TargetUPN;
AzureActivity
| where TimeGenerated > ago(90d)
| where OperationNameValue =~ "MICROSOFT.AUTHORIZATION/ROLEASSIGNMENTS/WRITE"
| where ActivityStatusValue =~ "Success"
| extend AssignedPrincipalId = tostring(parse_json(Properties).requestbody.properties.principalId)
| extend RoleDefId = tostring(parse_json(Properties).requestbody.properties.roleDefinitionId)
| where isnotempty(AssignedPrincipalId)
| join kind=inner DisabledUsers on $left.AssignedPrincipalId == $right.TargetId
| where TimeGenerated > DisabledAt
| project TimeGenerated, Caller, AssignedPrincipalId, TargetUPN, RoleDefId, ResourceGroup, SubscriptionId, DisabledAt
| order by TimeGenerated desc

Explanation

This query is designed to identify instances where privileged role assignments in Azure Active Directory (AAD) are made to identities that have been disabled or deleted. This could indicate either malicious activity, where an attacker is exploiting a known identity to maintain unauthorized access, or an administrative oversight that leaves dormant privileges in the system.

Here's a simplified breakdown of the query:

  1. Identify Disabled or Deleted Users:

    • The query first looks at audit logs from the past 90 days to find user accounts that have been disabled, deleted, or updated successfully, excluding any that were re-enabled.
    • It captures the latest time these actions occurred for each user, along with their ID and user principal name (UPN).
  2. Find Role Assignments:

    • It then examines Azure activity logs from the same 90-day period to find successful role assignment operations.
    • The query extracts the principal ID and role definition ID from these operations.
  3. Match Role Assignments with Disabled Users:

    • The query joins the role assignments with the list of disabled or deleted users, matching them by the principal ID.
    • It filters the results to include only those role assignments that occurred after the user was disabled or deleted.
  4. Output Results:

    • The final output includes details such as the time of the role assignment, the caller who made the assignment, the principal ID, the user's UPN, the role definition ID, the resource group, the subscription ID, and the time the user was disabled.
    • The results are sorted by the time of the role assignment in descending order, showing the most recent activities first.

This query helps security teams detect potential security risks by highlighting role assignments to identities that should no longer have access, allowing for timely investigation and remediation.

Details

David Alonso profile picture

David Alonso

Released: March 12, 2026

Tables

AuditLogsAzureActivity

Keywords

AuditLogsAzureActivityOperationNameTargetResourcesActivityDateTimePropertiesRequestbodyPrincipalIdRoleDefinitionIdCallerResourceGroupSubscriptionId

Operators

let|where>ago()has_any()=~!hasextendtostring()summarizemax()byparse_json()isnotempty()join kind=inneron==projectorder bydesc

Actions