Detection rules › Kusto
Hunt for Defender for Identity not installed but eligible
This query shows you which servers are eligible for Defender for identity but does not have the Defender for Identity agent installed. The query seach the eligible servers via Defender for Endpoint (requirement for this query to work), and is based on the server roles that MDE recongnizes.
Telemetry coverage
Rule body
let device_roles = dynamic(["EntraConnectServer", "AzureADConnectServer", "ActiveDirectoryCertificateServicesServer", "DomainController", "ADFS"]);
let mdi_servers = (
DeviceTvmSoftwareInventory
| where SoftwareName == "azure_advanced_threat_protection_sensor"
| distinct MdiDeviceName=tolower(DeviceName)
);
let mdi_eligible_servers = (
ExposureGraphNodes
| extend DeviceRoles= parse_json(NodeProperties)["rawData"]["deviceRole"]
| extend CriticalityRuleNames = parse_json(NodeProperties)["rawData"]["criticalityLevel"]["ruleNames"]
| where DeviceRoles has_any (device_roles) or
CriticalityRuleNames has_any (device_roles)
| distinct NodeName=tolower(NodeName), tostring(DeviceRoles), tostring(CriticalityRuleNames)
);
mdi_servers
| join kind=rightouter mdi_eligible_servers on $left.MdiDeviceName == $right.NodeName
| extend Issue = iff(isempty(MdiDeviceName), "This server is eligible for MDI but does not have MDI installed", "None")
| where Issue != "None"
Stages and Predicates
Parameters
let device_roles = dynamic(["EntraConnectServer", "AzureADConnectServer", "ActiveDirectoryCertificateServicesServer", "DomainController", "ADFS"]);
let mdi_servers is inlined into the numbered stages below.
Let binding: mdi_eligible_servers
let mdi_eligible_servers = (
ExposureGraphNodes
| extend DeviceRoles= parse_json(NodeProperties)["rawData"]["deviceRole"]
| extend CriticalityRuleNames = parse_json(NodeProperties)["rawData"]["criticalityLevel"]["ruleNames"]
| where DeviceRoles has_any (device_roles) or
CriticalityRuleNames has_any (device_roles)
| distinct NodeName=tolower(NodeName), tostring(DeviceRoles), tostring(CriticalityRuleNames)
);
Stage 1: source
let mdi_servers
Stage 2: source
let mdi_eligible_servers
Stage 3: source
DeviceTvmSoftwareInventory
Stage 4: where
where SoftwareName =~ "azure_advanced_threat_protection_sensor"
Stage 5: distinct
distinct
Stage 6: join
join kind=rightouter (mdi_eligible_servers) on MdiDeviceName, NodeName
Stage 7: extend
extend Issue
Issue =if
isempty(MdiDeviceName)"This server is eligible for MDI but does not have MDI installed"else
"None"Stage 8: where
where Issue !~ "None"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
CriticalityRuleNames | match |
| field:"CriticalityRuleNames" kind:match |
DeviceRoles | match |
| field:"DeviceRoles" kind:match |
Issue | ne |
| field:"Issue" kind:ne value:"None" |
SoftwareName | eq |
| field:"SoftwareName" kind:eq value:"azure_advanced_threat_protection_sensor" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Issue | extend |