Query Details

Audit Logs End Userconsentstoppedduetorisk Basedconsent

Query

AuditLogs
| where OperationName has "Consent to application" and Result == "failure" and ResultReason has "UserConsentBlockedForRiskyAppsException"
| extend
    Initiator = iif(isnotempty(InitiatedBy["app"]), tostring(InitiatedBy["app"]["displayName"]), tostring(InitiatedBy["user"]["userPrincipalName"])),
    InitiatorId = iif(isnotempty(InitiatedBy["app"]), tostring(InitiatedBy["app"]["servicePrincipalId"]), tostring(InitiatedBy["user"]["id"])),
    IPAddress = tostring(InitiatedBy[tostring(bag_keys(InitiatedBy)[0])]["ipAddress"])
| extend
    AppDisplayName = tostring(TargetResources[0]["displayName"])
| mv-apply modifiedProperty = TargetResources[0]["modifiedProperties"] on (
    summarize BagToUnpack = make_bag(pack(tostring(modifiedProperty["displayName"]), pack("oldValue", trim(@'[\"\s]+', tostring(modifiedProperty["oldValue"])), "newValue", trim(@'[\"\s]+', tostring(modifiedProperty["newValue"])))))
    )
| evaluate bag_unpack(BagToUnpack, columnsConflict='replace_source')
| extend
    AdminConsent = tostring(column_ifexists("ConsentContext.IsAdminConsent", dynamic(null))["newValue"]),
    OnBehalfOfAllUsers = tostring(column_ifexists("ConsentContext.OnBehalfOfAll", dynamic(null))["newValue"]),
    ActionReason = tostring(column_ifexists("ConsentAction.Reason", dynamic(null))["newValue"]),
    Permissions = extract_all(@"PrincipalId: ([a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12})?, ResourceId: ([a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12}), ConsentType:\s+(\w+), Scope:\s+([^,]+)", extract(@"\=\>\s+(.*)", 1, tostring(column_ifexists("ConsentAction.Permissions", dynamic(null))["newValue"])))
| mv-apply Permissions on (
    extend PermissionsDict = bag_pack("ConsentType", tostring(Permissions[2]), "TargetId", tostring(Permissions[0]), "PermissionsResourceId", tostring(Permissions[1]), "Scope", array_sort_asc(split(Permissions[3], ' ')))
    | summarize Permissions = make_list(PermissionsDict)
    )
| extend
    Target = iff(InitiatorId == Permissions[0]["TargetId"], Initiator, ""),
    TargetId = tostring(Permissions[0]["TargetId"])
| project
    TimeGenerated,
    LoggedByService,
    Category,
    Initiator,
    IPAddress,
    OperationName,
    Result,
    ResultReason,
    ActionReason,
    Target,
    AppDisplayName,
    Permissions,
    AdminConsent,
    OnBehalfOfAllUsers,
    InitiatorId,
    TargetId,
    InitiatedBy,
    AdditionalDetails,
    TargetResources,
    CorrelationId

Explanation

This query is designed to analyze audit logs for failed attempts to consent to applications, specifically when the failure is due to a "UserConsentBlockedForRiskyAppsException." Here's a breakdown of what the query does in simple terms:

  1. Filter Logs: It starts by filtering the AuditLogs table to find entries where the operation name includes "Consent to application" and the result is a failure due to a specific reason related to risky apps.

  2. Extract Information: The query extracts and extends several pieces of information:

    • Initiator: Identifies who initiated the operation, either an app or a user.
    • InitiatorId: The ID of the initiator, whether it's an app's service principal ID or a user's ID.
    • IPAddress: The IP address from which the operation was initiated.
    • AppDisplayName: The display name of the application involved in the consent operation.
  3. Unpack Modified Properties: It processes the modified properties of the target resources to create a structured bag of old and new values.

  4. Extract Consent Details: It extracts specific consent-related details:

    • AdminConsent: Whether the consent was given by an admin.
    • OnBehalfOfAllUsers: Whether the consent was intended for all users.
    • ActionReason: The reason for the consent action.
    • Permissions: Details about the permissions involved, including consent type, target ID, resource ID, and scope.
  5. Process Permissions: It further processes the permissions to create a list of permission dictionaries, detailing consent type, target ID, resource ID, and scope.

  6. Identify Target: It checks if the initiator ID matches the target ID from the permissions and assigns the initiator as the target if they match.

  7. Project Results: Finally, it selects and organizes the relevant columns to be displayed in the output, including details about the operation, initiator, application, permissions, and additional context.

Overall, this query is used to analyze and understand failed application consent attempts, focusing on identifying the initiator, the application involved, and the specific permissions and reasons related to the failure.

Details

Jose Sebastián Canós profile picture

Jose Sebastián Canós

Released: August 8, 2025

Tables

AuditLogs

Keywords

AuditLogsUserAppTargetResourcesPermissionsInitiatorIPAddressOperationNameResultResultReasonActionReasonAdminConsentOnBehalfOfAllUsersInitiatorIdTargetIdInitiatedByAdditionalDetailsCorrelationId

Operators

AuditLogswherehasand==extendiifisnotemptytostringbag_keysmv-applysummarizemake_bagpacktrim@evaluatebag_unpackcolumnsConflictreplace_sourcecolumn_ifexistsdynamicextract_allextractregexiffarray_sort_ascsplitbag_packmake_listproject

Actions

GitHub