Query Details

Recent Added Privileges

Query

id: 87770afa-b48b-4aea-b055-4bc48d805260
Function:
  Title: Function to get a list of all classified and privileged Entra ID or App Roles that has been assigned in the last 24 hours.
  Version: "1.0.0"
  LastUpdated: "2023-11-11"
Category: Microsoft Sentinel Parser
FunctionName: RecentAddedPrivileges
FunctionAlias: RecentAddedPrivileges
FunctionQuery: |
  let SensitiveMsGraphPermissions = externaldata(EAMTierLevelName: string, Category: string, AppRoleDisplayName: string)["https://raw.githubusercontent.com/Cloud-Architekt/AzurePrivilegedIAM/main/Classification/Classification_MsGraphAppRoles.json"] with(format='multijson');
  let SensitiveEntraDirectoryRoles = externaldata(Classification: string, RolePermissions: string, Category: string, RoleId: string, RoleName: string)["https://raw.githubusercontent.com/Cloud-Architekt/AzurePrivilegedIAM/main/Classification/Classification_EntraIdDirectoryRoles.json"] with(format='multijson') | mv-expand parse_json(RolePermissions)
  | summarize Category = make_list(RolePermissions.Category) by RoleId, RoleName, Classification = tostring(parse_json(Classification).EAMTierLevelName);
  let AddedSensitiveApiPermissions = AuditLogs
      | where TimeGenerated >ago(1d)
      | where LoggedByService =~ "Core Directory"
      | where Category =~ "ApplicationManagement"
      | where AADOperationType =~ "Assign"  
      | where OperationName == "Add app role assignment to service principal"
      | where Result =~ "success"
      | mv-expand TargetResources
      | mv-expand TargetResources.modifiedProperties
      | extend displayName_ = tostring(TargetResources_modifiedProperties.displayName)
      | where displayName_ =~ "AppRole.Value"
      | extend AppRole = tostring(parse_json(tostring(TargetResources_modifiedProperties.newValue)))
      | extend AddedPermission = replace_string(tostring(TargetResources_modifiedProperties.newValue),'"','')
      | join kind=inner ( SensitiveMsGraphPermissions | project AddedPermissionClassification = EAMTierLevelName, AddedPermissionCategory = Category, AppRoleDisplayName ) on $left.AddedPermission == $right.AppRoleDisplayName
      | mv-expand TargetResources.modifiedProperties | where TargetResources_modifiedProperties.displayName == "ServicePrincipal.ObjectID" | extend PrivilegedIdentityObjectId = replace_string(tostring(TargetResources_modifiedProperties.newValue),'"','')
      | mv-expand TargetResources.modifiedProperties | where TargetResources_modifiedProperties.displayName == "ServicePrincipal.DisplayName" | extend PrivilegedIdentityName = replace_string(tostring(TargetResources_modifiedProperties.newValue),'"','')
      | extend PrivilegedIdentityType = "ServicePrincipal"
      | extend PrivilegedAssignmentType = "AppRole"
      | summarize EnterpriseAccessModelTiering = make_set(AddedPermissionClassification), AssignedPrivileges = make_set(AddedPermission), CorrelationId = make_set(CorrelationId) by PrivilegedIdentityName, PrivilegedIdentityObjectId, PrivilegedIdentityType, PrivilegedAssignmentType;
  let AddedSensitiveDirectoryRoles = AuditLogs
  | where TimeGenerated >ago(1d)
      | where Category =~ "RoleManagement"
  | where ActivityDisplayName has_any ("Add eligible member to role", "Add member to role") and Identity != "MS-PIM"
      | mv-apply TargetResource = TargetResources on
        (
          where TargetResource.type in~ ("User", "ServicePrincipal", "Group")
          | extend PrivilegedIdentityObjectId = tostring(TargetResource.id),
                   PrivilegedIdentityType = tostring(TargetResource.type),
                   PrivilegedIdentityName = iff(TargetResource.type !~ "User", tostring(TargetResource.displayName), tostring(TargetResource.userPrincipalName))
        )
      | mv-expand TargetResources | where TargetResources.type == "Role" | extend RoleId = tostring(TargetResources.id)
      // Name of user is not included in audit log, lookup to UnifiedIdentityInfo table for enrichment
      | join kind=leftouter ( UnifiedIdentityInfo | project ObjectId, ObjectDisplayName ) on $left.PrivilegedIdentityObjectId == $right.ObjectId
      // Name of user is not included in audit log, using ObjectDisplayName from UnifiedIdentityInfo table
      | extend PrivilegedIdentityName = iff(TargetResource.type =~ "User", ObjectDisplayName, PrivilegedIdentityName)
      | where Result == "success"
      | join kind=inner ( SensitiveEntraDirectoryRoles | project AddedRoleClassification = Classification, Category = Category, RoleId, RoleName ) on RoleId
      | extend PrivilegedAssignmentType = "DirectoryRole"
      | summarize EnterpriseAccessModelTiering = make_set(AddedRoleClassification), CorrelationId = make_set(CorrelationId), AssignedPrivileges = make_set(RoleName) by PrivilegedIdentityName, PrivilegedIdentityObjectId, PrivilegedIdentityType, PrivilegedAssignmentType;
  union AddedSensitiveApiPermissions, AddedSensitiveDirectoryRoles
  | summarize PrivilegedAssignmentType = make_set(PrivilegedAssignmentType), AssignedPrivileges = make_set(AssignedPrivileges), EnterpriseAccessModelTiering = make_set(EnterpriseAccessModelTiering), CorrelationId = make_set(CorrelationId) by PrivilegedIdentityName, PrivilegedIdentityObjectId, PrivilegedIdentityType

Explanation

This query is designed to identify and list all classified and privileged roles or permissions that have been assigned to Entra ID (formerly Azure Active Directory) or application roles within the last 24 hours. Here's a simplified breakdown of what the query does:

  1. Load Sensitive Permissions Data:

    • It retrieves data about sensitive Microsoft Graph API permissions and Entra Directory roles from external JSON files. These files classify roles and permissions based on their sensitivity.
  2. Identify Recent App Role Assignments:

    • It examines audit logs to find successful assignments of application roles to service principals within the last day.
    • It filters these assignments to include only those that match the sensitive permissions list.
    • It extracts details like the service principal's name and ID, the type of privilege (AppRole), and the classification of the assigned permission.
  3. Identify Recent Directory Role Assignments:

    • Similarly, it checks audit logs for successful additions of members to directory roles (like users, service principals, or groups) within the last day.
    • It enriches this data with additional user information from another table if necessary.
    • It filters these assignments to include only those that match the sensitive directory roles list.
    • It extracts details like the identity's name and ID, the type of privilege (DirectoryRole), and the classification of the assigned role.
  4. Combine and Summarize Results:

    • It combines the results from both app role and directory role assignments.
    • It summarizes the data by each privileged identity, listing the types of assignments, the privileges assigned, their classifications, and any correlation IDs associated with the assignments.

In essence, this query helps security teams monitor and review recent assignments of sensitive roles and permissions, ensuring that any changes to privileged access are tracked and can be audited.

Details

Thomas Naunheim profile picture

Thomas Naunheim

Released: June 8, 2026

Tables

AuditLogsUnifiedIdentityInfo

Keywords

MicrosoftSentinelParserEntraIDAppRolesAuditLogsServicePrincipalDirectoryUnifiedIdentityInfo

Operators

letexternaldatawithformatmv-expandparse_jsonsummarizemake_listbytostringwhere>ago=~==extendreplace_stringjoinkindonprojectiffhas_anyin~mv-applyleftouterunionmake_set

Actions

GitHub