Forum Discussion

vinaygowlla's avatar
vinaygowlla
Copper Contributor
Aug 07, 2025

How to fetch dynamic tags in Defender for Endpoint (Machines API or KQL)?

Hi,

I'm trying to retrieve all unique tags (both manual and dynamic) in Microsoft Defender for Endpoint and then identify the currently active devices associated with each tag.

I have tried below

Machines API (/api/machines): This only returns manual tags (machine.tags) and does not include dynamic tags.

Advanced Hunting – DeviceInfo table: This seems to contain both manual and dynamic tags, but:

  • There are duplicate entries for the same device.
  • It's not clear how to filter for active devices only or how to get a clean mapping of tag → devices. 

I need guidance on how to

Retrieve all unique tags (manual + dynamic)?
Map these tags to the list of currently active devices (without duplicates)?
Is there any API or KQL query that can provide this cleanly?

Any advice, best practices, or sample queries would be greatly appreciated!

1 Reply

  • Hello vinaygowlla​,

    I built the following query which:

    • allows you to choose the devices of your interest based on OSPlatform values
    • allows you to set the threshold based on last seen for what you count as an active device
    • builds a table where in each row's first column is the DeviceName and then following columns represent the tags (both Dynamic and Manual)
    • each device in each row gets a "tick" emoji where the tag is present, this helps for readability in the results

    I hope this helps with your request.

    // Define which devices are of interest based on OSPlatform value
    let OS = dynamic(["Windows10","Windows11"]);
    // Set the threshold for what counts as an active device
    // Devices not seen in the last 7 days (or choose otherwise) will be excluded
    let ActiveThresholdDays = 7;
    DeviceInfo
    | where OSPlatform has_any (OS)
    | extend LastSeen = Timestamp
    // Normalize the dynamic and manual tags columns
    | extend DynamicTagsArray = iif(isnull(DeviceDynamicTags), 
        dynamic([]), todynamic(DeviceDynamicTags))
    | extend ManualTagsArray  = iif(isnull(DeviceManualTags),  
        dynamic([]), todynamic(DeviceManualTags))
    // Combine both manual and dynamic tags into a single array per device
    | extend AllTags = array_concat(DynamicTagsArray, ManualTagsArray)
    // Exclude devices with no tags
    | where array_length(AllTags) > 0  
    | mv-expand Tag=AllTags
    | extend Tag = tostring(Tag)
    | extend DaysSinceLastSeen = datetime_diff("day", now(), LastSeen)
    | where DaysSinceLastSeen <= ActiveThresholdDays
    | summarize HasTag=any(true) by DeviceName, Tag
    // Replace the boolean flag (1) with an emoji for readability in the results
    // If the device has tag, mark with ✅, otherwise leave blank
    | extend HasTagMark = iif(HasTag == true, "✅", "")
    | evaluate pivot(Tag, any(HasTagMark), DeviceName)

     

    If I have answered your question, please mark your post as Solved

    If you like my response, please consider giving it a like

     

Resources