Detection rules › Kusto

UniFi Site Manager: ISP Downtime

Status
available
Severity
high
Time window
30m
Group by
SiteId, ispAsn, ispName, metricTime
Source
github.com/Azure/Azure-Sentinel

Identifies when the ISP connection experiences downtime, impacting business operations and requiring documentation for SLA purposes.

MITRE ATT&CK coverage

Rule body

id: b54123ef-cfa7-769e-a959-f437404a1192
name: "UniFi Site Manager: ISP Downtime"
description: |
  Identifies when the ISP connection experiences downtime, impacting business operations and requiring documentation for SLA purposes.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: UniFiSiteManagerConnectorDefinition
    dataTypes:
      - Unifi_SiteManager_ISPMetrics_CL
queryFrequency: 15m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
relevantTechniques:
  - T1489
  - T1499
subTechniques:
  - T1499.002
query: |
  // UniFi ISP Downtime Detection
  let MinDowntimeSeconds = 60;
  Unifi_SiteManager_ISPMetrics_CL
  | where TimeGenerated > ago(30m)
  | mv-expand period = Periods
  | extend
      metricTime = todatetime(period.metricTime),
      downtime = toint(period.data.wan.downtime),
      uptime = todouble(period.data.wan.uptime),
      ispName = tostring(period.data.wan.ispName),
      ispAsn = tostring(period.data.wan.ispAsn)
  // De-duplicate Periods: each poll returns the same hour buckets, so collapse to latest value per metricTime
  | summarize arg_max(TimeGenerated, downtime, uptime, ispAsn) by tostring(SiteId), ispName, metricTime
  | where metricTime > ago(30m)
  | where downtime > 0
  | summarize
      TotalDowntimeSeconds = sum(downtime),
      EventCount = count(),
      AvgUptime = round(avg(uptime), 2),
      FirstSeen = min(metricTime),
      LastSeen = max(metricTime)
      by SiteId, ispName, ispAsn
  | where TotalDowntimeSeconds >= MinDowntimeSeconds
  | extend
      TimeGenerated = now(),
      DowntimeMinutes = round(TotalDowntimeSeconds / 60.0, 2)
  | project
      TimeGenerated,
      SiteId = SiteId,
      ISPName = ispName,
      ISPAsn = ispAsn,
      TotalDowntimeSeconds,
      DowntimeMinutes,
      AvgUptimePct = AvgUptime,
      EventCount,
      FirstSeen,
      LastSeen
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: SiteId
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: ISPName
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: true
    lookbackDuration: PT4H
    matchingMethod: AllEntities
kind: Scheduled
version: 1.0.1

Stages and Predicates

Parameters

let MinDowntimeSeconds = 60;

Stage 1: source

Unifi_SiteManager_ISPMetrics_CL

Stage 2: where

| where TimeGenerated > ago(30m)

Stage 3: mv-expand

| mv-expand period = Periods

Stage 4: extend

| extend
    metricTime = todatetime(period.metricTime),
    downtime = toint(period.data.wan.downtime),
    uptime = todouble(period.data.wan.uptime),
    ispName = tostring(period.data.wan.ispName),
    ispAsn = tostring(period.data.wan.ispAsn)

Stage 5: summarize

| summarize arg_max(TimeGenerated, downtime, uptime, ispAsn) by tostring(SiteId), ispName, metricTime

Stage 6: where

| where metricTime > ago(30m)

Stage 7: where

| where downtime > 0

Stage 8: summarize

| summarize
    TotalDowntimeSeconds = sum(downtime),
    EventCount = count(),
    AvgUptime = round(avg(uptime), 2),
    FirstSeen = min(metricTime),
    LastSeen = max(metricTime)
    by SiteId, ispName, ispAsn

Stage 9: where

| where TotalDowntimeSeconds >= MinDowntimeSeconds

Stage 10: extend

| extend
    TimeGenerated = now(),
    DowntimeMinutes = round(TotalDowntimeSeconds / 60.0, 2)

Stage 11: project

| project
    TimeGenerated,
    SiteId = SiteId,
    ISPName = ispName,
    ISPAsn = ispAsn,
    TotalDowntimeSeconds,
    DowntimeMinutes,
    AvgUptimePct = AvgUptime,
    EventCount,
    FirstSeen,
    LastSeen

Indicators

These rows show field, operator, and value matches.

Output fields

These fields are emitted when the rule matches.

FieldSource
AvgUptimePctproject
DowntimeMinutesproject
EventCountproject
FirstSeenproject
ISPAsnproject
ISPNameproject
LastSeenproject
SiteIdproject
TimeGeneratedproject
TotalDowntimeSecondsproject