Query Details

Sentinel Health Scheduled Analytics Rule Runs Anomaly

Query

let query_frequency = 1h;
let query_period = 14d;
let scan_step = 5m;
let consecutive_failures_threshold = 2;
let _ScheduledRunTimeSeries = (start_time: datetime, end_time: datetime) {
    let _Auxiliar = toscalar(
        SentinelHealth
        | where TimeGenerated between (start_time .. end_time)
        | where OperationName == "Scheduled analytics rule run"
        | make-series Count = count() default=0 on TimeGenerated step scan_step
        | extend
            TimeGenerated = array_slice(TimeGenerated, 0, toint(-(consecutive_failures_threshold * query_frequency / scan_step))),
            Count = array_slice(Count, 0, toint(-(consecutive_failures_threshold * query_frequency / scan_step)))
        | extend series_periods_detect(
            Count,
            0.0,
            toint(24h / scan_step),
            1)
        | summarize Period = take_any(toint(series_periods_detect_Count_periods[0]))
        );
    let _PeriodStep = scan_step * abs(coalesce(_Auxiliar, 1));
    SentinelHealth
    | where TimeGenerated between (start_time .. end_time)
    | where OperationName == "Scheduled analytics rule run"
    // _PeriodStep cannot be used with make-series, so summarize has to be used instead
    // | make-series Count = count() default=0 on TimeGenerated step _PeriodStep
    // summarize does not generate zero values for count(), baseline noise has to be added, it will be "deleted" afterwards
    | union (range TimeGenerated from start_time to end_time step _PeriodStep)
    | summarize Count = count() by bin_at(TimeGenerated, _PeriodStep, end_time)
    | extend Count = Count - 1
    | summarize TimeGenerated = make_list(TimeGenerated), Count = make_list(Count)
    | extend
        TimeGenerated = array_slice(TimeGenerated, 0, -2),
        Count = array_slice(Count, 0, -2)
    | extend series_decompose_anomalies(Count)
    | where array_sum(array_slice(series_decompose_anomalies_Count_ad_flag, -(consecutive_failures_threshold), -1)) == (-1 * consecutive_failures_threshold)
};
_ScheduledRunTimeSeries(ago(query_period), now())
// Uncomment the following line if you want only one alert (during the first query_frequency of the anomaly)
| where not(toscalar(_ScheduledRunTimeSeries(ago(query_period), ago(query_frequency)) | count) > 0)
| render timechart

Explanation

This KQL query is designed to monitor the execution of scheduled analytics rule runs in the SentinelHealth data over a specified period and detect anomalies in their execution frequency. Here's a simplified breakdown:

  1. Parameters Setup:

    • query_frequency: The frequency at which the query is run (1 hour).
    • query_period: The total time period over which the data is analyzed (14 days).
    • scan_step: The time interval for scanning data points (5 minutes).
    • consecutive_failures_threshold: The number of consecutive failures needed to trigger an anomaly (2).
  2. Auxiliary Calculation:

    • The query calculates an auxiliary value _Auxiliar to determine the periodicity of the scheduled runs within the specified time frame.
  3. Data Processing:

    • The query retrieves data from the SentinelHealth table for the specified time range and operation name ("Scheduled analytics rule run").
    • It uses a combination of summarize and make-series to count the occurrences of scheduled runs, adjusting for noise by adding and then subtracting a baseline value.
  4. Anomaly Detection:

    • The query uses series_decompose_anomalies to identify anomalies in the count of scheduled runs.
    • It checks if there are consecutive anomalies (as defined by consecutive_failures_threshold) indicating potential issues with the scheduled runs.
  5. Output:

    • The result is a time series chart (render timechart) showing when anomalies occurred.
    • An optional filter can be applied to ensure only the first occurrence of an anomaly within the query frequency is alerted.

In essence, this query helps identify periods where scheduled analytics rule runs are failing or not occurring as expected, potentially indicating issues that need attention.

Details

Jose Sebastián Canós profile picture

Jose Sebastián Canós

Released: March 12, 2026

Tables

SentinelHealth

Keywords

SentinelHealth

Operators

lettoscalarwherebetweenmake-seriescountdefaultonstepextendarray_slicetointseries_periods_detectsummarizetake_anycoalesceabsunionrangefromtobin_atbymake_listseries_decompose_anomaliesarray_sumagonownotrender

Actions