Detection rules › Kusto
UniFi Site Manager: Data Connector Health
Identifies when UniFi Site Manager data connectors stop sending data, ensuring visibility into the UniFi estate is maintained.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Rule body
id: 1fb5c195-9bff-e18b-3e8f-5123f46bff9d
name: "UniFi Site Manager: Data Connector Health"
description: |
Identifies when UniFi Site Manager data connectors stop sending data, ensuring visibility into the UniFi estate is maintained.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: UniFiSiteManagerConnectorDefinition
dataTypes:
- Unifi_SiteManager_Sites_CL
- Unifi_SiteManager_Devices_CL
- Unifi_SiteManager_Hosts_CL
- Unifi_SiteManager_ISPMetrics_CL
queryFrequency: 15m
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1562
subTechniques:
- T1562.006
query: |
// UniFi Data Connector Health Monitoring
let StaleThreshold = 30;
let DevicesHealth = Unifi_SiteManager_Devices_CL
| where TimeGenerated > ago(2h)
| summarize LastReceived = max(TimeGenerated)
| extend TableName = "Unifi_SiteManager_Devices_CL", TableDisplay = "Devices";
let HostsHealth = Unifi_SiteManager_Hosts_CL
| where TimeGenerated > ago(2h)
| summarize LastReceived = max(TimeGenerated)
| extend TableName = "Unifi_SiteManager_Hosts_CL", TableDisplay = "Hosts";
let SitesHealth = Unifi_SiteManager_Sites_CL
| where TimeGenerated > ago(2h)
| summarize LastReceived = max(TimeGenerated)
| extend TableName = "Unifi_SiteManager_Sites_CL", TableDisplay = "Sites";
let ISPHealth = Unifi_SiteManager_ISPMetrics_CL
| where TimeGenerated > ago(2h)
| summarize LastReceived = max(TimeGenerated)
| extend TableName = "Unifi_SiteManager_ISPMetrics_CL", TableDisplay = "ISP Metrics";
union DevicesHealth, HostsHealth, SitesHealth, ISPHealth
| extend
MinutesSinceData = datetime_diff('minute', now(), LastReceived),
Status = case(
datetime_diff('minute', now(), LastReceived) <= 15, "Healthy",
datetime_diff('minute', now(), LastReceived) <= 30, "Delayed",
datetime_diff('minute', now(), LastReceived) <= 60, "Stale",
"Critical"
)
| where MinutesSinceData >= StaleThreshold
| extend TimeGenerated = now()
| project
TimeGenerated,
TableName,
TableDisplay,
LastReceived,
MinutesSinceData,
Status
entityMappings:
- entityType: CloudApplication
fieldMappings:
- identifier: Name
columnName: TableDisplay
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: PT4H
matchingMethod: AllEntities
kind: Scheduled
version: 1.0.0
Stages and Predicates
Parameters
let StaleThreshold = 30;
union (4 sources)
Each leg below queries one source; the rule matches if any leg does. Sources: DevicesHealth, HostsHealth, SitesHealth, ISPHealth
Leg 1: DevicesHealth
Leg 2: HostsHealth
Leg 3: SitesHealth
Leg 4: ISPHealth
Applied to the combined result
| extend
MinutesSinceData = datetime_diff('minute', now(), LastReceived),
Status = case(
datetime_diff('minute', now(), LastReceived) <= 15, "Healthy",
datetime_diff('minute', now(), LastReceived) <= 30, "Delayed",
datetime_diff('minute', now(), LastReceived) <= 60, "Stale",
"Critical"
) | where MinutesSinceData >= StaleThreshold | extend TimeGenerated = now() | project
TimeGenerated,
TableName,
TableDisplay,
LastReceived,
MinutesSinceData,
Status
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
MinutesSinceData | ge |
| field:"MinutesSinceData" kind:ge value:"30" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
LastReceived | project |
MinutesSinceData | project |
Status | project |
TableDisplay | project |
TableName | project |
TimeGenerated | project |