Query Details

Analytics Azure RBAC Role Assignments

Query

// This query can help you to detect Azure RBAC role assignments.
//
// Click "Save as function", in Parameters write in the fields:
// "timespan" "query_frequency" "1d"
//
// If you name the function "AzureRBACRoleAssignments", you can check the function with queries like the following:
//
// AzureRBACRoleAssignments()
//
// AzureRBACRoleAssignments(1h)
//
//let query_frequency = 1d;
//let Function = (query_frequency:timespan = 1d){
let query_period = 14d;
let _PIM_ids = toscalar(
    _GetWatchlist("Activity-ExpectedSignificantActivity")
    | where Activity == "PrivilegedIdentityManagement" and Notes has "[App]"
    | summarize make_list(ActorId)
);
let _PrivAzureRBACRoles = toscalar(
    _GetWatchlist("RegEx-PrivAzureRBACRoles")
    | summarize RegEx = make_list(RegEx)
    | extend RegEx = strcat(@'^(', strcat_array(RegEx, '|'), @')$')
);
let _Users =
    IdentityInfo
    | where TimeGenerated > ago(query_period)
    | summarize arg_max(TimeGenerated, *) by AccountObjectId
    | project-away TimeGenerated
;
let _AADApps = _GetWatchlist("UUID-AADApps");
let _Groups = _GetWatchlist("UUID-AADGroups");
let _AzureSubscriptions = _GetWatchlist("UUID-AzureSubscriptions");
let _AzureRBACRoles = _GetWatchlist("UUID-AzureRBACRoles");
let _ResourceManagementPIM = materialize(
    AuditLogs
    | where TimeGenerated > ago(query_frequency)
    | where Category == "ResourceManagement" and LoggedByService == "PIM" and AADOperationType matches regex "Role" and not(AADOperationType == "CreateRoleUpdateRequest")
    | mv-apply AdditionalDetail = AdditionalDetails on (
        summarize ParsedAdditionalDetails = make_bag(pack(tostring(AdditionalDetail["key"]), tostring(AdditionalDetail["value"])))
    )
    // | mv-apply TargetResource = TargetResources on (
    //     summarize TargetResourcesTypes = make_list(tostring(TargetResource["type"]))
    // )
    | mv-apply TargetResource = TargetResources on (
        summarize TargetResource = make_list(TargetResource) by TargetResourceType = tostring(TargetResource["type"])
        | extend TargetResource = iff(array_length(TargetResource) == 1, TargetResource[0], TargetResource)
        | summarize ParsedTargetResources = make_bag(pack(TargetResourceType, TargetResource))
    )
    | project
        TimeGenerated,
        LoggedByService,
        Category,
        AADOperationType,
        OperationName,
        Result,
        ResultReason,
        ActivateOperation = OperationName has_any ("activation", "deactivate"),
        EligibleOperation = OperationName has "eligible",
        RemoveOperation = OperationName has "remove",
        PermanentOperation = OperationName has "permanent",
        // Information about the actor
        ActorIdentity = Identity,
        // Information about the actor, if it was a user
        ActorUserId = tostring(InitiatedBy["user"]["id"]),
        ActorUserPrincipalName = tolower(tostring(InitiatedBy["user"]["userPrincipalName"])),
        ActorUserIPAddress = tostring(InitiatedBy["user"]["ipAddress"]),
        ActorUserRoles = tostring(InitiatedBy["user"]["roles"]),
        // Information about the actor, if it was an application
        ActorAppName = tostring(InitiatedBy["app"]["displayName"]),
        ActorAppId = tostring(InitiatedBy["app"]["appId"]),
        ActorAppServicePrincipalName = tostring(InitiatedBy["app"]["servicePrincipalName"]),
        ActorAppServicePrincipalId = tostring(InitiatedBy["app"]["servicePrincipalId"]),
        // Information about the role
        RoleProvider = tostring(ParsedTargetResources["Provider"]["displayName"]),
        //RoleDefinitionOriginType = tostring(ParsedAdditionalDetails["RoleDefinitionOriginType"]),
        TargetRoleName = tostring(TargetResources[0]["displayName"]),
        TargetRoleTemplateId = tostring(ParsedAdditionalDetails["TemplateId"]),
        // Information about the targets
        TargetManagementGroupId = tostring(ParsedTargetResources["managementgroup"]["id"]),
        TargetManagementGroupName = tostring(ParsedTargetResources["managementgroup"]["displayName"]),
        TargetSubscriptionId = extract(@"\/subscriptions\/([^\/]+)\/", 1, tostring(ParsedAdditionalDetails["RoleDefinitionOriginId"])),
        TargetSubscriptionName = tostring(ParsedTargetResources["subscription"]["displayName"]),
        TargetGroupName = tostring(ParsedTargetResources["Group"]["displayName"]),
        TargetGroupId = tostring(ParsedTargetResources["Group"]["id"]),
        TargetUserPrincipalName = tostring(ParsedTargetResources["User"]["userPrincipalName"]),
        TargetUserId = tostring(ParsedTargetResources["User"]["id"]),
        // JSONs containers
        InitiatedBy,
        AdditionalDetails,
        TargetResources,
        RequestId = iff(isempty(array_length(ParsedTargetResources["Request"])), tostring(ParsedTargetResources["Request"]["id"]), tostring(ParsedTargetResources["Request"][0]["id"])),
        RoleScheduleId = iff(isempty(array_length(ParsedTargetResources["Request"])), tostring(ParsedTargetResources["RoleSchedule"]["id"]), tostring(ParsedTargetResources["Request"][1]["id"])),
        CorrelationId,
        Type
    | extend
        Caller = coalesce(ActorUserPrincipalName, ActorUserId, ActorAppServicePrincipalId),
        CallerDisplayName = ActorIdentity,
        CallerIpAddress = coalesce(ActorUserIPAddress, ""),
        TargetId = coalesce(TargetUserId, TargetGroupId),
        Target = coalesce(TargetUserPrincipalName, TargetGroupName),
        SourceSystem = LoggedByService
    | summarize
        StartTime = min(TimeGenerated),
        EndTime = max(TimeGenerated),
        RequestedTimeGenerated = make_list_if(TimeGenerated, OperationName has "requested"),
        CompletedTimeGenerated = make_list_if(TimeGenerated, not(OperationName has "requested") and Result == "success"),
        RequestedCount = countif(OperationName has "requested"),
        CompletedCount = countif(not(OperationName has "requested") and Result == "success"),
        arg_max(TimeGenerated, *)
        by CorrelationId, EligibleOperation, RemoveOperation, PermanentOperation, TargetManagementGroupId, TargetSubscriptionId, TargetRoleTemplateId, TargetGroupName, TargetUserPrincipalName, TargetUserId
    | extend
        RequestedTimeGenerated = iff(array_length(RequestedTimeGenerated) == 0, dynamic(null), RequestedTimeGenerated),
        RequestedCount = iff(RequestedCount == 0, int(null), RequestedCount)
);
let _RoleAssignmentsAzure = materialize(
    AzureActivity
    | where TimeGenerated > ago(query_frequency)
    | where ResourceProviderValue =~ "Microsoft.Authorization" and OperationNameValue has_any ("roleAssignments")
    | extend PreferenceInteger = case(
        ResourceProviderValue == "Microsoft.Authorization", 1,
        ResourceProviderValue == "MICROSOFT.AUTHORIZATION", 0,
        -1
        )
    // Group together Start, Accept, Success... operations
    | summarize hint.shufflekey=CorrelationId
        StartTime = min(TimeGenerated),
        EndTime = max(TimeGenerated),
        PropertiesDynamic = make_bag(pack(ActivityStatusValue, iff(PreferenceInteger == 1, todynamic(Properties), Properties_d))),
        EventDataId = array_sort_asc(make_list(EventDataId)),
        arg_max(TimeGenerated, *)
        by CorrelationId, ResourceProviderValue, OperationNameValue, _ResourceId
    | project-away EventDataId1
    // Group together two kinds of logs (where ResourceProviderValue is all caps or title - MICROSOFT.AUTHORIZATION or Microsoft.Authorization)
    | summarize hint.shufflekey=CorrelationId
        PropertiesDynamic = make_bag(pack(ResourceProviderValue, PropertiesDynamic)),
        EventDataId = make_bag(pack(ResourceProviderValue, EventDataId)),
        take_any(TenantId, SourceSystem, CategoryValue, SubscriptionId, Type),
        arg_min(PreferenceInteger, CallerIpAddress, Authorization, Authorization_d, Claims_d, Properties_d, EventSubmissionTimestamp, Hierarchy),
        arg_max(PreferenceInteger, Level, OperationNameValue, Caller, HTTPRequest, OperationId, ResourceGroup, ResourceProviderValue, ActivityStatusValue, ActivitySubstatusValue, OperationName, ActivityStatus, ActivitySubstatus, Category, ResourceId, ResourceProvider, Resource)
        by CorrelationId, TimeGenerated, StartTime, EndTime, _ResourceId
    | project-away PreferenceInteger*
    // Add origin events of operations (if a resource group is deleted, it might trigger a role assignment delete operation)
    | join kind=leftouter (
        AzureActivity
        | where TimeGenerated > ago(query_frequency)
        | where OperationNameValue has "/delete" and not(OperationNameValue has_any ("roleAssignments")) and ActivityStatusValue in ("Start", "Started")
        | summarize take_any(*) by EventDataId
        | extend StartOperationNameValue = coalesce(tostring(Properties_d["message"]), OperationNameValue), Start_HTTPRequest = HTTPRequest
        | summarize arg_min(TimeGenerated, StartOperationNameValue, Start_HTTPRequest) by CorrelationId, Auxiliar = tolower(StartOperationNameValue)
        | project-away TimeGenerated, Auxiliar
    ) on CorrelationId
    | project-away CorrelationId1
    | join kind=leftouter (
        AzureActivity
        | where TimeGenerated > ago(query_frequency)
        | where OperationNameValue has "/delete" and not(OperationNameValue has_any ("roleAssignments")) and not(ActivityStatusValue in ("Start", "Started"))
        | summarize take_any(*) by EventDataId
        | extend GroupOperationNameValue = coalesce(tostring(Properties_d["message"]), OperationNameValue), Group_ResourceId = _ResourceId, Group_HTTPRequest = HTTPRequest
        | summarize arg_min(TimeGenerated, GroupOperationNameValue, Group_HTTPRequest) by CorrelationId, Auxiliar = tolower(GroupOperationNameValue), Group_ResourceId
        | project-away TimeGenerated, Auxiliar
    ) on CorrelationId
    | project-away CorrelationId1
    | where not(isnotempty(GroupOperationNameValue) and (not(_ResourceId contains Group_ResourceId) or _ResourceId == Group_ResourceId))
    | extend OriginOperationNameValue = coalesce(StartOperationNameValue, GroupOperationNameValue, tostring(PropertiesDynamic["MICROSOFT.AUTHORIZATION"]["Start"]["message"]), tostring(PropertiesDynamic["Microsoft.Authorization"]["Start"]["message"]))
    | extend OriginOperationNameValue = iff(OperationNameValue =~ OriginOperationNameValue, "", OriginOperationNameValue)
    | project-away StartOperationNameValue, GroupOperationNameValue, Group_ResourceId
    // Start coalescing fields
    | extend
        Caller = tolower(Caller),
        AllCapsPropertiesDynamic = coalesce(PropertiesDynamic["MICROSOFT.AUTHORIZATION"]["Start"], PropertiesDynamic["MICROSOFT.AUTHORIZATION"]["Success"]),
        TitlePropertiesDynamic = coalesce(PropertiesDynamic["Microsoft.Authorization"]["Started"], PropertiesDynamic["Microsoft.Authorization"]["Succeeded"])
    | extend
        ResourceId = coalesce(ResourceId, tostring(AllCapsPropertiesDynamic["entity"])),
        Category = coalesce(Category, tostring(AllCapsPropertiesDynamic["eventCategory"])),
        OperationNameValue = coalesce(tostring(AllCapsPropertiesDynamic["message"]), OperationNameValue),
        ResourceProvider = coalesce(ResourceProvider, tostring(split(tostring(AllCapsPropertiesDynamic["message"]), "/")[0])),
        Resource = coalesce(
            Resource,
            replace_regex(tostring(AllCapsPropertiesDynamic["resource"]), @"([a-f0-9]{8})([a-f0-9]{4})([a-f0-9]{4})([a-f0-9]{4})([a-f0-9]{12})", @"\1-\2-\3-\4-\5")
            ),
        TargetResourceGroup = coalesce(tostring(split(split(tostring(AllCapsPropertiesDynamic["entity"]), "/resourceGroups/")[1], "/")[0]), ResourceGroup),
        HTTPRequest = coalesce(HTTPRequest, tostring(AllCapsPropertiesDynamic["httpRequest"]), Start_HTTPRequest, Group_HTTPRequest),
        Hierarchy = coalesce(Hierarchy, tostring(TitlePropertiesDynamic["hierarchy"])),
        AppId = tostring(Claims_d["appid"]),
        CallerType = case(
            tostring(Claims_d["http://schemas.microsoft.com/identity/claims/scope"]) == "user_impersonation", "User",
            tostring(Claims_d["idtyp"]) == "app", "App",
            ""
            ),
        CallerRole = tostring(todynamic(Authorization)["evidence"]["role"]),
        CallerRoleType = tostring(todynamic(Authorization)["evidence"]["principalType"]),
        Body = todynamic(dynamic_to_json(coalesce(
            PropertiesDynamic["MICROSOFT.AUTHORIZATION"]["Start"]["requestbody"],
            PropertiesDynamic["Microsoft.Authorization"]["Started"]["requestbody"],
            PropertiesDynamic["MICROSOFT.AUTHORIZATION"]["Success"]["responseBody"],
            PropertiesDynamic["Microsoft.Authorization"]["Succeeded"]["responseBody"]
            ))),
        CoalescedProperties = coalesce(
            PropertiesDynamic["MICROSOFT.AUTHORIZATION"]["Success"],
            PropertiesDynamic["Microsoft.Authorization"]["Succeeded"]
        )
    | extend
    //     BodyKeys = bag_keys(Body),
    //     PropertiesKeys = bag_keys(CoalescedProperties),
        CallerIpAddress = coalesce(CallerIpAddress, tostring(todynamic(HTTPRequest)["clientIpAddress"])),
        BodyPropertiesKeys = bag_keys(coalesce(Body["properties"], Body["Properties"]))
    | extend
        TargetId = coalesce(
            tostring(Body["properties"]["principalId"]), 
            tostring(Body["Properties"]["PrincipalId"]),
            replace_regex(tostring(CoalescedProperties["principalId"]), @"([a-f0-9]{8})([a-f0-9]{4})([a-f0-9]{4})([a-f0-9]{4})([a-f0-9]{12})", @"\1-\2-\3-\4-\5")
            ),
        TargetRoleDefinitionId = coalesce(tostring(Body["properties"]["roleDefinitionId"]), tostring(Body["Properties"]["RoleDefinitionId"])),
        TargetType = coalesce(tostring(Body["properties"]["principalType"]), tostring(Body["Properties"]["PrincipalType"]), iff(BodyPropertiesKeys has "EmailAddress", "User", ""))//,
        // TargetCondition = coalesce(tostring(Body["Properties"]["Condition"]), ""),
        // TargetConditionVersion = coalesce(tostring(Body["Properties"]["ConditionVersion"]), "")
    | extend
        TargetRoleTemplateId = coalesce(
            tostring(split(TargetRoleDefinitionId, "/roleDefinitions/")[1]),
            replace_regex(tostring(CoalescedProperties["roleDefinitionId"]), @"([a-f0-9]{8})([a-f0-9]{4})([a-f0-9]{4})([a-f0-9]{4})([a-f0-9]{12})", @"\1-\2-\3-\4-\5")
            ),
        RemoveOperation = case(
            OperationNameValue =~ "Microsoft.Authorization/roleAssignments/delete", true,
            OperationNameValue =~ "Microsoft.Authorization/roleAssignments/write", false,
            false
        )
    | project-away Body, CoalescedProperties, BodyPropertiesKeys, Start_HTTPRequest, Group_HTTPRequest, AllCapsPropertiesDynamic, TitlePropertiesDynamic//, TargetRoleDefinitionId
    | lookup kind=leftouter (
        union
            (
            _AADApps
            | project CallerType = "App", Caller = tostring(ObjectId), CallerDisplayName = AppDisplayName
            ),
            (
            AuditLogs
            | where TimeGenerated > ago(query_period)
            | where Category == "ApplicationManagement" and OperationName has "service principal" and not(AADOperationType in ("Assign", "Unassign"))
            | distinct Caller = tostring(TargetResources[0]["id"]), CallerDisplayName = tostring(TargetResources[0]["displayName"])//, TargetType = "ServicePrincipal"
            | extend CallerType = "App"
            )
            | where isnotempty(Caller)
        ) on Caller//, CallerType
    | extend CallerDisplayName = coalesce(CallerDisplayName, tostring(Claims_d["name"]))
    | extend PermanentOperation = iff(CallerDisplayName == "MS-PIM" and Caller in (_PIM_ids), bool(null), true)
    | lookup kind=leftouter (
        _AADApps
        | project AppId = tostring(AppId), AppDisplayName
        | where isnotempty(AppId)
        ) on AppId
    | project-rename TargetSubscriptionId = SubscriptionId
    | lookup kind=leftouter (
        _AzureSubscriptions
        | project TargetSubscriptionId = tostring(SubscriptionId), TargetSubscriptionName = tostring(SubscriptionName)
        ) on TargetSubscriptionId
    | lookup kind=leftouter (
        union
            (
            _AADApps
            | project TargetId = tostring(ObjectId), Target = tostring(AppDisplayName)//, TargetType = "ServicePrincipal"
            ),
            (
            AuditLogs
            | where TimeGenerated > ago(query_period)
            | where Category == "ApplicationManagement" and OperationName has "service principal" and not(AADOperationType in ("Assign", "Unassign"))
            | distinct TargetId = tostring(TargetResources[0]["id"]), Target = tostring(TargetResources[0]["displayName"])//, TargetType = "ServicePrincipal"
            ),
            (
            _Users
            | where isnotempty(AccountObjectId) and isnotempty(AccountUPN)
            | project TargetId = AccountObjectId, Target = tostring(AccountUPN)//, TargetType = "User"
            ),
            (
            _Groups
            | where isnotempty(ObjectId) and isnotempty(DisplayName)
            | project TargetId = tostring(ObjectId), Target = tostring(DisplayName)//, TargetType = "Group"
            )
        | where isnotempty(TargetId)
        | distinct *
        ) on TargetId//, TargetType
    | lookup kind=leftouter (
        _AzureRBACRoles
        | project TargetRoleTemplateId = tostring(RoleId), TargetRoleName = tostring(RoleName)
        | where isnotempty(TargetRoleTemplateId)
        ) on TargetRoleTemplateId
    | project
        TimeGenerated,
        StartTime,
        EndTime,
        ResourceProvider,
        Category,
        CategoryValue,
        ResourceProviderValue,
        Level,
        CallerIpAddress,
        CallerType,
        Caller,
        CallerDisplayName,
        CallerRole,
        CallerRoleType,
        AppId,
        AppDisplayName,
        OperationName,
        OperationNameValue,
        OriginOperationNameValue,
        ActivityStatusValue,
        //ActivityStatus,
        ActivitySubstatusValue,
        ActivitySubstatus,
        TargetSubscriptionId,
        TargetSubscriptionName,
        TargetResourceGroup,
        TargetType,
        TargetId,
        Target,
        TargetRoleTemplateId,
        TargetRoleName,
        //TargetRoleDefinitionId,
        // TargetCondition,
        // TargetConditionVersion,
        Authorization,
        PropertiesDynamic,
        HTTPRequest,
        Authorization_d,
        Properties_d,
        Claims_d,
        Resource,
        ResourceId,
        _ResourceId,
        Hierarchy,
        OperationId,
        CorrelationId,
        RemoveOperation,
        PermanentOperation,
        EventSubmissionTimestamp,
        EventDataId,
        //TenantId,
        SourceSystem,
        Type
);
union
    (
    union
        (
        _ResourceManagementPIM
        | where not(EligibleOperation)
        | lookup kind=leftouter (
            _RoleAssignmentsAzure
            | where CallerDisplayName == "MS-PIM" and Caller in (_PIM_ids) and isnotempty(TargetSubscriptionId) and isnotempty(TargetRoleTemplateId) and isnotempty(TargetId) and isnotempty(RemoveOperation)
            | project-rename Azure_EndTime = EndTime, AzureActivity_CorrelationId = CorrelationId, Azure_CallerIpAddress = CallerIpAddress
            | project-away TimeGenerated, StartTime, Category, Caller, CallerDisplayName, OperationName, TargetSubscriptionName, TargetRoleName, Target, SourceSystem, Type, PermanentOperation
            ) on TargetSubscriptionId, TargetRoleTemplateId, TargetId, RemoveOperation
        | where not(isnotempty(Azure_EndTime) and Azure_EndTime > EndTime)
        | summarize arg_max(Azure_EndTime, *) by TimeGenerated, CorrelationId, TargetSubscriptionId, TargetRoleTemplateId, TargetId, RemoveOperation
        | extend CallerIpAddress = coalesce(CallerIpAddress, Azure_CallerIpAddress)
        | project-away Azure_EndTime, Azure_CallerIpAddress
        ),
        (
        _ResourceManagementPIM
        | where EligibleOperation
        )
    | project-rename AuditLogs_CorrelationId = CorrelationId
    | project-away LoggedByService
    ),
    (
    _RoleAssignmentsAzure
    | where not(CallerDisplayName == "MS-PIM" and Caller in (_PIM_ids) and isnotempty(TargetSubscriptionId) and isnotempty(TargetRoleTemplateId) and isnotempty(TargetId)) and isnotempty(RemoveOperation)
    | project-rename AzureActivity_CorrelationId = CorrelationId
    )
| where not(SourceSystem == "PIM" and isnotempty(CompletedCount) and CompletedCount == 0)
| extend
    PrivilegedRole = isempty(TargetRoleName) or TargetRoleName matches regex _PrivAzureRBACRoles,
    WorkingTime = IsWorkingTime(TimeGenerated),
    AlertName = strcat(
        iff(RemoveOperation, "Remove", "Add"),
        " ",
        iff(PermanentOperation, "permanent", iff(not(ActivateOperation), "temporary", "activated")),
        " ",
        iff(EligibleOperation, "eligible ", ""),
        "member ",
        iff(RemoveOperation, "from", "to"),
        " Azure RBAC role"),
    AlertDescription = strcat(
        'This rule detects operations with Azure RBAC roles.\n\nThe ',
        case(TargetType == "User", 'user', TargetType == "ServicePrincipal", 'service principal', TargetType == "Group", 'group', 'member'),
        strcat(' "', coalesce(Target, TargetId), '"'),
        ' was ',
        iff(RemoveOperation, 'removed from', 'added to'),
        ' role "',
        coalesce(TargetRoleName, TargetRoleTemplateId),
        '" in ',
        iff(isnotempty(TargetResourceGroup), strcat('resource group "', TargetResourceGroup,'" from '), ''),
        case(
            isnotempty(TargetSubscriptionId), 'subscription "',
            isnotempty(TargetManagementGroupId), 'management group "',
            ''),
        case(
            isnotempty(TargetSubscriptionId), coalesce(TargetSubscriptionName, TargetSubscriptionId),
            isnotempty(TargetManagementGroupId), coalesce(TargetManagementGroupName, TargetManagementGroupId),
            ''),
        '" by "',
        iff(Caller matches regex @"[a-f0-9]+\-[a-f0-9]+\-[a-f0-9]+\-[a-f0-9]+\-[a-f0-9]+", coalesce(CallerDisplayName, Caller), Caller),
        '".\n'
    )
| extend AlertSeverity = case(
    // Remove operations by Azure AD PIM (assumedly automatic)
    RemoveOperation and CallerDisplayName == "Azure AD PIM" and AppDisplayName == "MS-PIM" and Caller in (_PIM_ids), "Informational",
    // Remove operations originated by resource group deletion
    RemoveOperation and SourceSystem == "Azure" and isnotempty(OriginOperationNameValue), "Informational",
    // Direct Azure operations
    not(SourceSystem == "PIM") and PrivilegedRole, "High",
    not(SourceSystem == "PIM") and not(PrivilegedRole), "Medium",
    // Voluntary deactivation operations
    AADOperationType == "DeactivateRole", "Informational",
    // Activation
    AADOperationType == "ActivateRole" and not(WorkingTime), "Medium",
    AADOperationType == "ActivateRole" and WorkingTime, "Informational",
    // Non-user target
    not(TargetGroupName == Target or TargetType in ("User", "Group")), "High",
    // Non-working hours
    not(WorkingTime), "High",
    // Working hours
    WorkingTime and PrivilegedRole and PermanentOperation, "Medium",
    WorkingTime and PrivilegedRole and not(PermanentOperation), "Medium",
    WorkingTime and not(PrivilegedRole) and PermanentOperation, "Low",
    WorkingTime and not(PrivilegedRole) and not(PermanentOperation), "Informational",
    "High"
    )
| project
    Type,
    SourceSystem,
    StartTime,
    EndTime,
    ResourceProviderValue,
    Category,
    CallerIpAddress,
    Caller,
    CallerDisplayName,
    AppDisplayName,
    OriginOperationNameValue,
    OperationNameValue,
    ActivityStatusValue,
    ActivitySubstatusValue,
    OperationName,
    Result,
    ResultReason,
    TargetManagementGroupName,
    TargetSubscriptionName,
    TargetResourceGroup,
    TargetRoleName,
    TargetGroupName,
	//PrivilegedRole,
    Target,
    TargetType,
    TargetManagementGroupId,
    TargetSubscriptionId,
    TargetRoleTemplateId,
    TargetId,
    AzureActivity_CorrelationId,
    ResourceProvider,
    CategoryValue,
    Level,
    CallerType,
    CallerRoleType,
    CallerRole,
    AppId,
    ActivitySubstatus,
    Authorization,
    PropertiesDynamic,
    HTTPRequest,
    Authorization_d,
    Properties_d,
    Claims_d,
    Resource,
    ResourceId,
    _ResourceId,
    Hierarchy,
    OperationId,
    AuditLogs_CorrelationId,
    AADOperationType,
    // WorkingTime,
    // ActivateOperation,
    // EligibleOperation,
    // RemoveOperation,
    // PermanentOperation,
    ActorIdentity,
    ActorUserId,
    ActorUserPrincipalName,
    ActorUserIPAddress,
    ActorUserRoles,
    ActorAppId,
    ActorAppName,
    ActorAppServicePrincipalId,
    ActorAppServicePrincipalName,
    //RoleProvider,
	TargetGroupId,
    TargetUserId,
    TargetUserPrincipalName,
    RequestId,
    RoleScheduleId,
    InitiatedBy,
    AdditionalDetails,
    TargetResources,
    AlertName,
    AlertSeverity,
    AlertDescription
//};
//Function(query_frequency)

Explanation

This query helps detect Azure RBAC role assignments by analyzing various logs and activities. It looks for operations where users or applications are added or removed from Azure RBAC roles. The query also considers factors like working hours, operation types, and target entities to determine the severity of the detected role assignments. The results provide information about the operation, target entity, caller, and more to help monitor and manage Azure RBAC role assignments effectively.

Details

Jose Sebastián Canós profile picture

Jose Sebastián Canós

Released: October 6, 2023

Tables

_ResourceManagementPIM_RoleAssignmentsAzure_AADApps_Groups_AzureSubscriptions_AzureRBACRoles_UsersIdentityInfoAuditLogsAzureActivity

Keywords

AzureRBACRoleAssignments,Devices,Intune,User

Operators

toscalarwheresummarizemake_listtolowertostringextendstrcatstrcat_arrayarray_lengthiffmake_bagpackmv-applyprojectisnotemptyextractreplacedynamicnullunioncasetodynamicdynamic_to_jsonbag_keyslookupproject-renameisworkingtimecoalesceregexcontainsmatchesjoinleftouterarray_sort_asctake_anyarg_minarg_maxarray_lengthisemptyminmaxcountifmake_list_ifmake_bagmake_listmake_bag_ifmake_bag_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake_list_ifmake

Actions