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
Stage 0: let
let StaleThreshold = 30;
let DevicesHealth = Unifi_SiteManager_Devices_CL <inlined as stages below>;
let HostsHealth = Unifi_SiteManager_Hosts_CL <inlined as stages below>;
let SitesHealth = Unifi_SiteManager_Sites_CL <inlined as stages below>;
let ISPHealth = Unifi_SiteManager_ISPMetrics_CL <inlined as stages below>;
Stage 1: union
union of 4 branches
Stage 2: source
Unifi_SiteManager_Devices_CL
Stage 3: where
| where TimeGenerated > ago(2h)
Stage 4: summarize
| summarize LastReceived = max(TimeGenerated)
Stage 5: extend
| extend TableName = "Unifi_SiteManager_Devices_CL", TableDisplay = "Devices"
Stage 6: source
Unifi_SiteManager_Hosts_CL
Stage 7: where
| where TimeGenerated > ago(2h)
Stage 8: summarize
| summarize LastReceived = max(TimeGenerated)
Stage 9: extend
| extend TableName = "Unifi_SiteManager_Hosts_CL", TableDisplay = "Hosts"
Stage 10: source
Unifi_SiteManager_Sites_CL
Stage 11: where
| where TimeGenerated > ago(2h)
Stage 12: summarize
| summarize LastReceived = max(TimeGenerated)
Stage 13: extend
| extend TableName = "Unifi_SiteManager_Sites_CL", TableDisplay = "Sites"
Stage 14: source
Unifi_SiteManager_ISPMetrics_CL
Stage 15: where
| where TimeGenerated > ago(2h)
Stage 16: summarize
| summarize LastReceived = max(TimeGenerated)
Stage 17: extend
| extend TableName = "Unifi_SiteManager_ISPMetrics_CL", TableDisplay = "ISP Metrics"
Stage 18: extend
extend MinutesSinceData, Status
Status =if
/* macro: (datetime_diff('minute', now(), LastReceived) <= 15) */"Healthy"elif
/* macro: (datetime_diff('minute', now(), LastReceived) <= 30) */"Delayed"elif
/* macro: (datetime_diff('minute', now(), LastReceived) <= 60) */"Stale"else
"Critical"Stage 19: where
where MinutesSinceData >= 30
Stage 20: extend
extend TimeGenerated
Stage 21: project
project LastReceived, MinutesSinceData, Status, TableDisplay, TableName, TimeGenerated
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 |