// Non-onboarded/discovered devices + who saw them + network details + IPs
DeviceInfo
| where Timestamp > ago(30d)
| where OnboardingStatus != "Onboarded" // all not onboarded/discovered
| where isempty(MergedToDeviceId) // Remove invalidated/merged devices
| summarize arg_max(Timestamp, *) by DeviceId
| invoke SeenBy() // Add detecting onboarded devices
| join kind=leftouter (
DeviceNetworkInfo
| where Timestamp > ago(30d)
| summarize arg_max(Timestamp, *) by DeviceId // Latest network info
) on DeviceId
| extend ConnectedNetworksParsed = parse_json(ConnectedNetworks)
| extend IPAddressesParsed = parse_json(IPAddresses)
| summarize
IPsSeen = make_set(IPAddressesParsed.Address),
DetectingDevices = make_set(SeenBy), // Onboarded devices that saw it
NetworkNames = make_set(ConnectedNetworksParsed.Name), // e.g., WiFi SSID or network name
NetworkCategories = make_set(ConnectedNetworksParsed.Category), // e.g., Enterprise, Public
LastSeen = max(Timestamp),
FirstSeen = min(Timestamp)
by NonOnboardedName = DeviceName, OSPlatform, DeviceId
| project-reorder NonOnboardedName, IPsSeen, DetectingDevices, NetworkNames, NetworkCategories, OSPlatform, LastSeen, FirstSeen
| order by LastSeen desc
Cleaner Version
// Non-onboarded/discovered devices + who saw them + network details + IPs (excluding Unknown OS, iOS, Android, Linux; remove with no detecting devices)
DeviceInfo
| where Timestamp > ago(30d)
| where OnboardingStatus != "Onboarded" // all not onboarded/discovered
| where isempty(MergedToDeviceId) // Remove invalidated/merged devices
| summarize arg_max(Timestamp, *) by DeviceId
| where isnotempty(OSPlatform) and OSPlatform != "Unknown" and OSPlatform !in ("iOS", "Android", "Linux")
| invoke SeenBy() // Add detecting onboarded devices
| join kind=leftouter (
DeviceNetworkInfo
| where Timestamp > ago(30d)
| summarize arg_max(Timestamp, *) by DeviceId // Latest network info
) on DeviceId
| extend IPAddresses = iff(isempty(IPAddresses) or isnull(IPAddresses), tostring(pack_array(pack_dictionary("IPAddress", PublicIP, "PrefixSize", "0", "AddressSpace", "Public"))), IPAddresses)
| extend ConnectedNetworks = iff(isempty(ConnectedNetworks) or isnull(ConnectedNetworks), '[]', ConnectedNetworks)
| mv-apply IPAddressesParsed = parse_json(IPAddresses) on (
extend IPAddress = tostring(IPAddressesParsed.IPAddress)
| summarize IPsSeen = make_set(IPAddress) by DeviceId, NonOnboardedName = DeviceName, OSPlatform, tostring(SeenBy), ConnectedNetworks, Timestamp
)
| mv-apply ConnectedNetworksParsed = parse_json(ConnectedNetworks) on (
extend Name = tostring(ConnectedNetworksParsed.Name),
Category = tostring(ConnectedNetworksParsed.Category)
| summarize NetworkNames = make_set(Name),
NetworkCategories = make_set(Category) by DeviceId, NonOnboardedName, OSPlatform, tostring(SeenBy), tostring(IPsSeen), Timestamp
)
| summarize
IPsSeen = make_set(IPsSeen),
DetectingDevices = make_set(SeenBy), // Onboarded devices that saw it
NetworkNames = make_set(NetworkNames),
NetworkCategories = make_set(NetworkCategories),
LastSeen = max(Timestamp),
FirstSeen = min(Timestamp)
by NonOnboardedName, OSPlatform, DeviceId
| where array_length(DetectingDevices) > 0 // Remove devices with no detecting devices
| project-reorder NonOnboardedName, IPsSeen, DetectingDevices, NetworkNames, NetworkCategories, OSPlatform, LastSeen, FirstSeen
| order by LastSeen desc
