The Hunting Query Found It — The Detection Rule Didn't
The hunting query worked.
Agent Foskett ran it across the last seven days and found the activity immediately.
The event matched.
The entities were there.
The evidence was clear.
There was only one problem.
The same KQL was supposed to be running as a Microsoft Sentinel scheduled analytics rule.
No alert had fired.
No incident had been created.
The query could see the activity.
So why couldn't the detection?

Same Logic. Different Execution.
A successful hunting query does not prove a scheduled rule evaluated the same data at the right time with the same conditions.
The KQL was not automatically innocent
Agent Foskett resisted it.
A hunting query and a scheduled analytics rule can contain similar KQL while operating under different time windows, schedules, thresholds and rule settings.
“The query finds it now” does not prove “the rule should have found it then.”
Reproduce the hunting result
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
let HuntStart = ago(7d);
DeviceProcessEvents
| where Timestamp >= HuntStart
| where FileName =~ "powershell.exe"
| where ProcessCommandLine has_any
("-EncodedCommand", "FromBase64String")
| project Timestamp,
DeviceName,
AccountName,
FileName,
ProcessCommandLine,
SHA1
| order by Timestamp ascNow reconstruct what the rule actually evaluated
The hunt searched seven days of history in one execution.
The detection did not.
Agent Foskett identified the suspicious event's timestamp and recreated the rule's intended evaluation window around that time.
Historical hunting sees what exists now. Scheduled detection sees what was available inside each execution window.
Test the rule-sized window
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
let WindowStart = datetime(2026-10-05 02:00:00);
let WindowEnd = datetime(2026-10-05 02:10:00);
DeviceProcessEvents
| where Timestamp between (WindowStart .. WindowEnd)
| where FileName =~ "powershell.exe"
| where ProcessCommandLine has_any
("-EncodedCommand", "FromBase64String")
| project Timestamp,
DeviceName,
AccountName,
ProcessCommandLineThe event timestamp was inside the window — but that still was not enough
But Agent Foskett had just investigated another class of problem that mattered here too: ingestion delay.
If the event arrived in the workspace after the scheduled rule had already evaluated that window, a later historical hunt could find evidence the rule never had available at execution time.
Event time and data availability time are different questions.
Check when the matching event became queryable
ingestion_time(). This does not by itself reproduce every aspect of rule execution, but it helps determine whether the data arrived late enough to explain a missed evaluation.- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
let WindowStart = datetime(2026-10-05 02:00:00);
let WindowEnd = datetime(2026-10-05 02:10:00);
DeviceProcessEvents
| where Timestamp between (WindowStart .. WindowEnd)
| where FileName =~ "powershell.exe"
| where ProcessCommandLine has_any
("-EncodedCommand", "FromBase64String")
| extend IngestionTime = ingestion_time(),
IngestionDelay = ingestion_time() - Timestamp
| project Timestamp,
IngestionTime,
IngestionDelay,
DeviceName,
ProcessCommandLineThen inspect the rule itself
Was the rule enabled?
Had the KQL drifted from the hunting version?
What were the query frequency and lookup period?
Was there a result threshold?
Were suppression or incident settings involved?
Had the rule recently been changed?
A detection is more than its KQL.
Check whether any alert was created
SecurityAlert around the relevant period.The alert might have existed under a different incident, been closed, or simply been overlooked in the portal.
Absence had to be proven against the alert data, not assumed from the current incident list.
Search for the expected alert
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
let StartTime = datetime(2026-10-05 01:30:00);
let EndTime = datetime(2026-10-05 03:30:00);
SecurityAlert
| where TimeGenerated between (StartTime .. EndTime)
| where ProductName has "Sentinel"
| where AlertName =~ "Suspicious Encoded PowerShell"
| project TimeGenerated,
SystemAlertId,
AlertName,
AlertSeverity,
CompromisedEntity,
Entities
| order by TimeGenerated ascNo alert changed the investigation
The historical hunt matched the event.
The event belonged to the period the rule was intended to cover.
No expected alert appeared in
SecurityAlert.That narrowed the problem — but still did not identify the cause.
A missed alert is the symptom. Rule execution, data timing and configuration explain the failure.
Look for rule health and execution evidence
SentinelHealth can provide analytics-rule health and execution information.Agent Foskett used this as another source of evidence while recognising that health telemetry depends on configuration, supported resource types and retention.
Inspect analytics-rule health
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
let RuleName = "Suspicious Encoded PowerShell";
let StartTime = datetime(2026-10-05 01:30:00);
let EndTime = datetime(2026-10-05 03:30:00);
SentinelHealth
| where TimeGenerated between (StartTime .. EndTime)
| where SentinelResourceType has "Analytics"
| where SentinelResourceName has RuleName
| project TimeGenerated,
SentinelResourceName,
OperationName,
Status,
Description,
Reason,
ExtendedProperties
| order by TimeGenerated ascThe failure can live outside the query text
The investigation must consider scheduling, lookup windows, ingestion delay, thresholds, rule state, configuration changes and execution health.
The detection pipeline includes the query — but it does not end with the query.
Build the missed-detection timeline
What Agent Foskett checked
What the evidence can and cannot prove
It does not automatically prove the same data was available to a scheduled analytics rule during an earlier execution.
SecurityAlert can establish whether an expected alert exists in the workspace, while SentinelHealth can provide additional rule-health evidence where supported and enabled.To explain a missed detection, reconstruct the rule as it executed then — not merely the query as it works now.
The lesson
That was not the end of the investigation.
The hunt and the detection had different execution contexts.
Agent Foskett compared the query, the data, the timing and the rule configuration until the gap could be explained.
A good hunting query is not automatically a reliable detection rule.
Related investigations
Final thought
Detection engineering asks whether the system will recognise that evidence reliably when it matters.
Those are related questions.
They are not identical.
Agent Foskett found the event twice — once in the logs, and once in the gap between hunting and detection.
The Logs Already Knew! 🔎

