Detection rules › Kusto

UniFi Site Manager: Data Connector Health

Status
available
Severity
medium
Time window
2h
Source
github.com/Azure/Azure-Sentinel

Identifies when UniFi Site Manager data connectors stop sending data, ensuring visibility into the UniFi estate is maintained.

MITRE ATT&CK coverage

TacticTechniques
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.

Output fields

These fields are emitted when the rule matches.

FieldSource
LastReceivedproject
MinutesSinceDataproject
Statusproject
TableDisplayproject
TableNameproject
TimeGeneratedproject