Agent Foskett Case File • Microsoft Sentinel • Analytics Rules • Threat Hunting • SecurityAlert • KQL

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?

Agent Foskett investigating a Microsoft Sentinel hunting query that found activity missed by an analytics rule
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.

✓ Reproduce the hunt
✓ Reconstruct the rule window
✓ Validate alert creation

The KQL was not automatically innocent

The obvious conclusion was that Sentinel had missed something.

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

First, Agent Foskett preserved the exact hunting result. The example looks for a suspicious PowerShell pattern in Defender XDR data made available to Sentinel. The important point is to preserve the event timestamp and evidence the rule was expected to detect.
hunting-query.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 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 asc

Now reconstruct what the rule actually evaluated

Scheduled analytics rules have a query frequency and a lookup period.

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

This example narrows the same logic to a specific historical window. In a real investigation, Agent Foskett would use the analytics rule's configured lookup period and schedule rather than inventing a generic interval.
rule-window-test.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 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,
          ProcessCommandLine

The event timestamp was inside the window — but that still was not enough

The event's own timestamp fit the reconstructed period.

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

Agent Foskett compared the source timestamp with 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.
detection-ingestion-delay.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 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,
          ProcessCommandLine

Then inspect the rule itself

Agent Foskett compared the saved hunting query with the analytics rule configuration.

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

Before declaring a missed detection, Agent Foskett searched 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

The exact provider and alert name depend on the analytics rule. Agent Foskett searched a broad enough window to determine whether Sentinel had produced the expected alert at all.
expected-securityalert.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 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 asc

No alert changed the investigation

Now Agent Foskett had three separate facts.

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

When Microsoft Sentinel auditing and health monitoring are enabled for supported resources, 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

Agent Foskett searched the rule's health events around the detection window for execution failures or other clues. The resource name should be replaced with the actual analytics-rule identifier used in the workspace.
analytics-rule-health.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 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 asc

The failure can live outside the query text

A rule can fail to produce the expected alert even when its KQL looks correct during a later hunt.

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

Suspicious activity occursThe source records an event that matches the hunting logic.
Scheduled rule evaluatesThe rule runs according to its configured frequency and lookup period.
No expected alert appearsSecurityAlert contains no matching detection for the period.
Historical hunt succeedsThe analyst later searches a wider window and finds the activity.
Execution conditions comparedData availability, schedule, threshold and rule configuration are reconstructed.
Detection corrected and validatedThe rule is tested against controlled evidence and monitored for future execution.

What Agent Foskett checked

Exact KQLWas the analytics rule actually running the same logic as the successful hunt?
Query frequencyHow often did the scheduled rule execute?
Lookup periodWhat historical window did each execution evaluate?
Ingestion delayWas the event available when the relevant rule execution ran?
Alert evidenceDid SecurityAlert contain the expected detection anywhere?
Rule healthDid SentinelHealth record execution or resource problems where monitoring was enabled?

What the evidence can and cannot prove

A successful historical hunt proves that the queried data matches the KQL at the time the hunt is run.

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

The KQL worked.

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.
Investigation principle: When hunting finds activity a scheduled detection missed, compare the execution conditions — not just the KQL text.
The hunt found it. The rule didn't.
Compare the query, the window, the ingestion and the execution.
Continue the Investigation

Final thought

Threat hunting asks whether evidence exists.

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! 🔎
Develop IT.. Protect IT.. GEMXIT.
GEMXIT PTY LTD | GEMXIT UK LTD
Talk to GEMXIT

The Hunting Query Found It — The Detection Rule Didn't

This Agent Foskett investigation examines why a Microsoft Sentinel hunting query can find suspicious activity that a scheduled analytics rule failed to alert on.

Microsoft Sentinel Hunting Query vs Analytics Rule

Use KQL, SecurityAlert, SentinelHealth, ingestion_time(), query windows and analytics-rule configuration to reconstruct a missed detection and distinguish successful historical hunting from reliable scheduled detection.

Investigating Missed Microsoft Sentinel Detections

Learn why identical or similar KQL can behave differently when execution timing, lookup periods, ingestion delay, thresholds and rule health are considered.