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 timechartExplanation
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:
-
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).
-
Auxiliary Calculation:
- The query calculates an auxiliary value
_Auxiliarto determine the periodicity of the scheduled runs within the specified time frame.
- The query calculates an auxiliary value
-
Data Processing:
- The query retrieves data from the
SentinelHealthtable for the specified time range and operation name ("Scheduled analytics rule run"). - It uses a combination of
summarizeandmake-seriesto count the occurrences of scheduled runs, adjusting for noise by adding and then subtracting a baseline value.
- The query retrieves data from the
-
Anomaly Detection:
- The query uses
series_decompose_anomaliesto 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.
- The query uses
-
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.
- The result is a time series chart (
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
Released: March 12, 2026
Tables
SentinelHealth
Keywords
SentinelHealth
Operators
lettoscalarwherebetweenmake-seriescountdefaultonstepextendarray_slicetointseries_periods_detectsummarizetake_anycoalesceabsunionrangefromtobin_atbymake_listseries_decompose_anomaliesarray_sumagonownotrender