Detection rules › Kusto
UniFi Site Manager: Multiple Devices Offline
Identifies when multiple UniFi devices go offline simultaneously, typically signaling a power outage, upstream connectivity failure, or infrastructure issue requiring investigation.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule body
id: 9283b576-5350-fca1-3979-dacb6acd1d16
name: "UniFi Site Manager: Multiple Devices Offline"
description: |
Identifies when multiple UniFi devices go offline simultaneously, typically signaling a power outage, upstream connectivity failure, or infrastructure issue requiring investigation.
severity: High
status: Available
requiredDataConnectors:
- connectorId: UniFiSiteManagerConnectorDefinition
dataTypes:
- Unifi_SiteManager_Devices_CL
queryFrequency: 15m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1489
- T1499
subTechniques:
- T1499.002
query: |
let prev_offline_ids = Unifi_SiteManager_Devices_CL
| where TimeGenerated between (ago(45m) .. ago(15m))
| summarize arg_max(TimeGenerated, *) by Id
| where Status == "offline"
| distinct Id;
Unifi_SiteManager_Devices_CL
| where TimeGenerated > ago(15m)
| summarize arg_max(TimeGenerated, *) by Id
| where Status == "offline"
| extend IsNew = Id !in (prev_offline_ids)
| summarize
OfflineDeviceCount = count(),
NewlyOfflineCount = countif(IsNew),
OfflineDevices = make_list(coalesce(Name, Id)),
NewlyOfflineDevices = make_list_if(coalesce(Name, Id), IsNew),
ProductLines = make_set(ProductLine)
| where OfflineDeviceCount >= 3
| where NewlyOfflineCount > 0
| extend
TimeGenerated = now(),
OfflineDeviceList = strcat_array(OfflineDevices, ", "),
NewlyOfflineList = strcat_array(NewlyOfflineDevices, ", "),
AffectedProductLines = strcat_array(ProductLines, ", ")
| extend Activity = strcat(NewlyOfflineCount, ' device(s) newly offline (', OfflineDeviceCount, ' total offline): ', NewlyOfflineList)
| project
TimeGenerated,
OfflineDeviceCount,
NewlyOfflineCount,
OfflineDeviceList,
NewlyOfflineList,
AffectedProductLines,
Activity
entityMappings:
- entityType: CloudApplication
fieldMappings:
- identifier: Name
columnName: AffectedProductLines
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: true
lookbackDuration: PT4H
matchingMethod: AllEntities
kind: Scheduled
version: 1.0.3
Stages and Predicates
Let binding: prev_offline_ids
let prev_offline_ids = Unifi_SiteManager_Devices_CL
| where TimeGenerated between (ago(45m) .. ago(15m))
| summarize arg_max(TimeGenerated, *) by Id
| where Status == "offline"
| distinct Id;
Stage 1: source
Unifi_SiteManager_Devices_CL
Stage 2: where
| where TimeGenerated > ago(15m)
Stage 3: summarize
| summarize arg_max(TimeGenerated, *) by Id
Stage 4: where
| where Status == "offline"
Stage 5: extend
| extend IsNew = Id !in (prev_offline_ids)
Stage 6: summarize
| summarize
OfflineDeviceCount = count(),
NewlyOfflineCount = countif(IsNew),
OfflineDevices = make_list(coalesce(Name, Id)),
NewlyOfflineDevices = make_list_if(coalesce(Name, Id), IsNew),
ProductLines = make_set(ProductLine)
Stage 7: where
| where OfflineDeviceCount >= 3
Stage 8: where
| where NewlyOfflineCount > 0
Stage 9: extend
| extend
TimeGenerated = now(),
OfflineDeviceList = strcat_array(OfflineDevices, ", "),
NewlyOfflineList = strcat_array(NewlyOfflineDevices, ", "),
AffectedProductLines = strcat_array(ProductLines, ", ")
Stage 10: extend
| extend Activity = strcat(NewlyOfflineCount, ' device(s) newly offline (', OfflineDeviceCount, ' total offline): ', NewlyOfflineList)
Stage 11: project
| project
TimeGenerated,
OfflineDeviceCount,
NewlyOfflineCount,
OfflineDeviceList,
NewlyOfflineList,
AffectedProductLines,
Activity
Indicators
These rows show field, operator, and value matches.
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Activity | project |
AffectedProductLines | project |
NewlyOfflineCount | project |
NewlyOfflineList | project |
OfflineDeviceCount | project |
OfflineDeviceList | project |
TimeGenerated | project |