Query Details

Multiple Multiple Registered Devices By Account

Query

let query_frequency = 4h;
let query_period = 8h;
let threshold = 4;
union
    (
    AuditLogs
    | where TimeGenerated > ago(query_period)
    | where OperationName == "Add device" and Result == "success"
    | mv-apply modifiedProperty = TargetResources[0]["modifiedProperties"] on (
        summarize modifiedProperties = make_bag(bag_pack(tostring(modifiedProperty["displayName"]), translate(@'["\]', "", tostring(modifiedProperty["newValue"]))))
        )
    | extend
        DeviceDisplayName = tostring(TargetResources[0]["displayName"]),
        DeviceOSType = tostring(modifiedProperties["DeviceOSType"]),
        DeviceObjectId = tostring(TargetResources[0]["id"]),
        DeviceId = tostring(modifiedProperties["DeviceId"]),
        UserId = extract(@"USER\-HWID\:([^\:\,]+)", 1, tostring(modifiedProperties["DevicePhysicalIds"]))
    ),
    (
    AuditLogs
    | where TimeGenerated > ago(query_period)
    | where OperationName == "Register device" and Result == "success"
    | mv-apply AdditionalDetail = AdditionalDetails on (
        summarize ParsedAdditionalDetails = make_bag(bag_pack(tostring(AdditionalDetail["key"]), tostring(AdditionalDetail["value"])))
        )
    | extend
        DeviceDisplayName = tostring(TargetResources[0]["displayName"]),
        DeviceOSType = tostring(ParsedAdditionalDetails["Device OS"]),
        IPAddress = tostring(InitiatedBy[tostring(bag_keys(InitiatedBy)[0])]["ipAddress"]),
        DeviceId = tostring(ParsedAdditionalDetails["Device Id"]),
        UserId = iif(isnotempty(InitiatedBy["app"]), tostring(InitiatedBy["app"]["servicePrincipalId"]), tostring(InitiatedBy["user"]["id"]))
    )
| summarize
    StartTime = min(TimeGenerated),
    OperationNames = array_sort_asc(make_set(OperationName)),
    UserId = take_any(UserId),
    IPAddress = take_any(IPAddress),
    DeviceDisplayName = take_any(DeviceDisplayName),
    DeviceOSType = take_any(DeviceOSType),
    DeviceObjectId = take_any(DeviceObjectId),
    TargetResources = make_bag(bag_pack(OperationName, TargetResources)),
    CorrelationIds = make_set(CorrelationId)
    by DeviceId//, UserId
| join kind=leftouter (
    IntuneOperationalLogs
    | where TimeGenerated > ago(query_period)
    | where OperationName == "Enrollment"
    | extend Properties = todynamic(Properties)
    | project
        EnrollmentTimeGenerated = TimeGenerated,
        EnrollmentResult = Result,
        FailureReason = tostring(Properties["FailureReason"]),
        DeviceId = tostring(Properties["AADDeviceId"]),
        UserId = tostring(Properties["IntuneUserId"]),
        IntuneDeviceId = tostring(Properties["IntuneDeviceId"]),
        //EnrollmentDeviceOSType = tostring(Properties["Os"]),
        EnrollmentProperties = Properties
    ) on DeviceId//, UserId
| project-away DeviceId1, UserId1
| extend OperationNames = iff(isnotempty(EnrollmentTimeGenerated), set_union(OperationNames, dynamic(["Enrollment"])), OperationNames)
| where isnotempty(UserId)
// | where isnotempty(EnrollmentTimeGenerated)
| as _Events
| join kind=leftsemi (
    _Events
    | extend
        Type = "AuditLogs",
        TimeGenerated = StartTime
    | evaluate activity_counts_metrics(Type, TimeGenerated, ago(query_period), now(), query_frequency, UserId)
    | summarize
        arg_min(PreviousTimeGenerated = TimeGenerated, PreviousCount = ["count"]),
        arg_max(CurrentTimeGenerated = TimeGenerated, CurrentCount = ["count"])
        by UserId
    | where CurrentTimeGenerated > ago(query_period)
    | extend PreviousCount = iff(PreviousTimeGenerated == CurrentTimeGenerated, 0, PreviousCount)
    | where (not(PreviousCount > threshold) and CurrentCount > threshold)
        or ((CurrentCount - PreviousCount) > threshold)
    ) on UserId
| join kind=leftouter (
    IdentityInfo
    | where TimeGenerated > ago(14d)
    | summarize arg_max(TimeGenerated, AccountUPN, JobTitle) by AccountObjectId, AccountSID
    | project
        AccountObjectId,
        UserPrincipalName = AccountUPN,
        JobTitle
    ) on $left.UserId == $right.AccountObjectId
| project-away AccountObjectId
| sort by UserId, StartTime asc
| project
    StartTime,
    UserId,
    UserPrincipalName,
    JobTitle,
    IPAddress,
    DeviceDisplayName,
    DeviceOSType,
    DeviceId,
    OperationNames,
    EnrollmentTimeGenerated,
    EnrollmentResult,
    FailureReason,
    EnrollmentProperties,
    TargetResources,
    IntuneDeviceId,
    DeviceObjectId,
    CorrelationIds

Explanation

This KQL query is designed to analyze and monitor device-related activities and user behaviors within a specified time frame. Here's a simplified breakdown of what the query does:

  1. Define Parameters:

    • query_frequency: Set to 4 hours, determines how often the query checks for activity.
    • query_period: Set to 8 hours, defines the time window for looking back at logs.
    • threshold: Set to 4, used to identify significant changes in activity.
  2. Extract Device Activities:

    • The query pulls data from AuditLogs for "Add device" and "Register device" operations that were successful within the last 8 hours.
    • It extracts details like device display name, OS type, device ID, and user ID from these logs.
  3. Summarize Device Information:

    • It summarizes the extracted data by DeviceId, capturing the earliest activity time, operation names, user ID, IP address, and other device-related details.
  4. Join with Enrollment Logs:

    • The query joins the summarized data with IntuneOperationalLogs to include device enrollment information, such as enrollment time, result, and failure reasons.
  5. Filter by User Activity:

    • It filters users based on their activity count, identifying those whose activity count has increased significantly (by more than the threshold) within the last 8 hours.
  6. Join with Identity Information:

    • The query further enriches the data by joining it with IdentityInfo to include user principal name and job title.
  7. Output the Results:

    • Finally, it sorts and projects the relevant information, including start time, user details, device details, operation names, enrollment details, and correlation IDs.

In essence, this query is used to monitor device-related operations and user activities, identify significant changes in user activity, and provide detailed insights into device enrollments and user identities within a specified period.

Details

Jose Sebastián Canós profile picture

Jose Sebastián Canós

Released: April 23, 2026

Tables

AuditLogsIntuneOperationalLogsIdentityInfo

Keywords

AuditLogsDevicesIntuneOperationalLogsIdentityInfoUser

Operators

letunionwheremv-applysummarizemake_bagbag_packextendtostringextractiifisnotemptyarray_sort_ascmake_settake_anyjoinkindleftoutertodynamicprojectset_uniondynamiciffasleftsemievaluateactivity_counts_metricsarg_minarg_maxnotoronproject-awaysortbyasc

Actions