01 Agent Entitlement Materialization
Query
MicrosoftGraphActivityLogs
| where TimeGenerated > ago(LookbackWindow)
| where isnotempty(ServicePrincipalId)
| extend RoleClaims = iif(isempty(Roles), dynamic([]), split(Roles, " "))
| extend ScopeClaims = iif(isempty(Scopes), dynamic([]), split(Scopes, " "))
| mv-expand ClaimValue = array_concat(RoleClaims, ScopeClaims) to typeof(string)
| where isnotempty(ClaimValue)
| extend PermissionType = iff(set_has_element(RoleClaims, ClaimValue), "application", "delegated") // NOTE: in() requires scalar constants and fails against a dynamic array (Kusto error "in(): failed to cast argument 2 to scalar constant"); set_has_element() is the correct dynamic-array membership check.
| summarize
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
CallCount = count(),
DistinctTokens = dcount(UniqueTokenId)
by ServicePrincipalId, ResourceApp = "Microsoft Graph", Permission = ClaimValue, PermissionType
| order by ServicePrincipalId asc, Permission asc
// Next step (manual, not yet encoded as KQL): for each (ServicePrincipalId, Permission)
// row with LastSeen older than your revocation-propagation window, cross-check
// against a live Microsoft Graph read of appRoleAssignments/oauth2PermissionGrants
// before concluding the grant still exists -- this table alone cannot prove a
// *current* negative (i.e. that a permission was removed).About this query
Explanation
This KQL query is designed to analyze Microsoft Graph activity logs to determine which permissions are currently active (or "materialized") for a service principal's identity in Microsoft Graph. Here's a simplified breakdown of what the query does:
-
Purpose: The query aims to approximate which permissions (resourceApp, permission pairs) are active on a service principal by examining the token claims it has presented to Microsoft Graph. This serves as a cross-check against the authoritative data from Microsoft Graph's direct reads of app role assignments and permission grants.
-
Data Source: The query uses the
MicrosoftGraphActivityLogstable, which should be enabled in the Microsoft Entra ID diagnostic settings. -
Lookback Window: It considers logs from the past 30 days (or another specified period) to account for the frequency of agent calls. This window can be adjusted based on how often the service principal interacts with Microsoft Graph.
-
Processing Steps:
- Filters logs to only include those with a
ServicePrincipalId. - Extracts role and scope claims from the logs, splitting them into separate lists.
- Expands these lists into individual claim values.
- Determines the type of permission (application or delegated) based on the presence of the claim in the role claims list.
- Summarizes the data to show when each permission was first and last seen, how many times it was called, and the number of distinct tokens.
- Filters logs to only include those with a
-
Output: The query outputs a list of service principals with their associated permissions, sorted by service principal ID and permission. It includes the first and last time each permission was seen and how often it was used.
-
Limitations: The query cannot definitively prove that a permission has been removed, as token claims may not immediately reflect revocations. It suggests a manual cross-check with live Microsoft Graph data for permissions that haven't been seen recently.
Overall, this query helps identify which permissions are likely active for a service principal, but it should be used in conjunction with direct Microsoft Graph data for complete accuracy.