Forum Discussion
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