Detection rules › Kusto

UniFi Site Manager: ISP SLA Breach

Status
available
Severity
medium
Time window
1h
Group by
SiteId, ispAsn, ispName, metricTime, siteIdStr
Source
github.com/Azure/Azure-Sentinel

Identifies when ISP uptime falls below the SLA threshold. Useful for tracking SLA compliance and supporting ISP accountability conversations.

MITRE ATT&CK coverage

TacticTechniques
Impact

Rule body

id: fecd4ab1-b24e-8413-9164-e3621c8d7caa
name: "UniFi Site Manager: ISP SLA Breach"
description: |
  Identifies when ISP uptime falls below the SLA threshold. Useful for tracking SLA compliance and supporting ISP accountability conversations.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: UniFiSiteManagerConnectorDefinition
    dataTypes:
      - Unifi_SiteManager_ISPMetrics_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
relevantTechniques:
  - T1499
subTechniques:
  - T1499.002
query: |
  // UniFi ISP SLA Breach Detection
  let SLAThreshold = 99.9;
  Unifi_SiteManager_ISPMetrics_CL
  | where TimeGenerated > ago(1h)
  | mv-expand period = Periods
  | extend
      metricTime = todatetime(period.metricTime),
      uptime = todouble(period.data.wan.uptime),
      downtime = toint(period.data.wan.downtime),
      ispName = tostring(period.data.wan.ispName),
      ispAsn = tostring(period.data.wan.ispAsn),
      siteIdStr = tostring(SiteId)
  // De-duplicate Periods: each poll returns the same hour buckets, so collapse to latest value per metricTime
  | summarize arg_max(TimeGenerated, uptime, downtime, ispAsn) by siteIdStr, ispName, metricTime
  | where metricTime > ago(1h)
  | summarize
      AvgUptime = round(avg(uptime), 3),
      MinUptime = round(min(uptime), 3),
      TotalDowntimeSeconds = sum(downtime),
      MeasurementCount = count()
      by SiteId = siteIdStr, ispName, ispAsn
  | where AvgUptime < SLAThreshold
  | extend 
      TimeGenerated = now(),
      SLATarget = SLAThreshold,
      UptimeGap = round(SLAThreshold - AvgUptime, 3)
  | project
      TimeGenerated,
      SiteId = SiteId,
      ISPName = ispName,
      ISPAsn = ispAsn,
      AvgUptimePct = AvgUptime,
      MinUptimePct = MinUptime,
      SLATargetPct = SLATarget,
      UptimeGapPct = UptimeGap,
      TotalDowntimeSeconds,
      MeasurementCount
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: SiteId
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: ISPName
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: P1D
    matchingMethod: AllEntities
kind: Scheduled
version: 1.0.1

Stages and Predicates

Parameters

let SLAThreshold = 99.9;

Stage 1: source

Unifi_SiteManager_ISPMetrics_CL

Stage 2: where

| where TimeGenerated > ago(1h)

Stage 3: mv-expand

| mv-expand period = Periods

Stage 4: extend

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

Stage 5: summarize

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

Stage 6: where

| where metricTime > ago(1h)

Stage 7: summarize

| summarize
    AvgUptime = round(avg(uptime), 3),
    MinUptime = round(min(uptime), 3),
    TotalDowntimeSeconds = sum(downtime),
    MeasurementCount = count()
    by SiteId = siteIdStr, ispName, ispAsn

Stage 8: where

| where AvgUptime < SLAThreshold

Stage 9: extend

| extend 
    TimeGenerated = now(),
    SLATarget = SLAThreshold,
    UptimeGap = round(SLAThreshold - AvgUptime, 3)

Stage 10: project

| project
    TimeGenerated,
    SiteId = SiteId,
    ISPName = ispName,
    ISPAsn = ispAsn,
    AvgUptimePct = AvgUptime,
    MinUptimePct = MinUptime,
    SLATargetPct = SLATarget,
    UptimeGapPct = UptimeGap,
    TotalDowntimeSeconds,
    MeasurementCount

Indicators

These rows show field, operator, and value matches.

Output fields

These fields are emitted when the rule matches.

FieldSource
AvgUptimePctproject
ISPAsnproject
ISPNameproject
MeasurementCountproject
MinUptimePctproject
SLATargetPctproject
SiteIdproject
TimeGeneratedproject
TotalDowntimeSecondsproject
UptimeGapPctproject