Agent Foskett Academy • Lesson 8 • Finding Time Patterns with bin()

Finding Time Patterns with bin()

Security investigations are not only about what happened.

They are also about when it happened.

A single failed login may not matter. A hundred failed logins within two minutes changes the investigation completely.

Attackers often generate bursts of activity: password spray attempts, MFA fatigue prompts, email floods, suspicious sign-in spikes, endpoint execution bursts and sudden outbound data transfers.

The bin() function helps defenders group telemetry into time windows so timing patterns become easier to identify.

In this lesson, you will learn how to use bin() in KQL to organise Microsoft Defender XDR and Sentinel telemetry into meaningful time ranges for investigation.

Because sometimes the pattern is hidden in the timing.

Agent Foskett Academy lesson explaining how to find time patterns in KQL with bin
Lesson overview

Learn how to use bin() to group security telemetry into time intervals and identify spikes, bursts and suspicious behavioural patterns.

Understanding time-based grouping
Using bin() with security telemetry
Finding spikes and attack bursts
🕒 Attackers often reveal themselves through timing patterns.
bin() helps defenders group telemetry into time windows so unusual behaviour becomes easier to detect.
View KQL Hunting Guide →

Why bin() matters

Without time grouping, investigations can become overwhelming because every event appears individually.

The bin() function groups timestamps into fixed intervals such as minutes, hours or days.

This allows defenders to identify sudden spikes, bursts of activity, repeated attack timing, authentication floods and large event surges.
Find spikesTime grouping helps defenders see when activity suddenly increases.
Reveal burstsAttack behaviour often appears as short bursts of repeated activity rather than one obvious event.
Build timelinesbin() helps turn raw rows into investigation timelines that are easier to explain and follow.

The basic bin() pattern

The bin() function is usually used with summarize. You count events, then group them by a timestamp rounded into a fixed time interval.
signin-events-over-time.kql
  1. 1
  2. 2
  3. 3
  4. 4
SigninLogs
| where TimeGenerated > ago(24h)
| summarize SignInCount=count() by bin(TimeGenerated, 1h)
| order by TimeGenerated asc
Plain-English translation:

Count sign-in events in 1-hour blocks over the last 24 hours.

Understanding bin()

bin() rounds each timestamp down into a matching time bucket. That bucket becomes the group used by summarize.
bin() creates time windowsbin() groups timestamps into intervals such as 5 minutes, 1 hour or 1 day.
Patterns become visibleGrouping events by time helps defenders identify unusual spikes and repeated bursts of activity.
Perfect for investigationsTime grouping is useful for sign-in analysis, phishing campaigns, endpoint activity and authentication attacks.

Choosing the right time window

The interval you choose matters. A very small time window may create too much detail. A very large time window may hide the spike you are trying to find.
5 minutesUseful for fast activity such as password sprays, MFA prompt bursts or sudden endpoint events.
1 hourUseful for daily investigation patterns such as sign-in trends, email surges or cloud activity.
1 dayUseful for longer trend reviews, weekly baselines and slow-moving operational patterns.

Detecting failed sign-in bursts

Failed sign-ins are easier to understand when grouped over time. A small number of failures spread across a day may be normal. A large burst in a short window may indicate attack activity.
failed-signin-bursts.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
SigninLogs
| where TimeGenerated > ago(24h)
| where ResultType != 0
| summarize FailedAttempts=count() by bin(TimeGenerated, 15m)
| order by TimeGenerated asc
Plain-English translation:

Count failed sign-ins in 15-minute blocks to identify bursts of authentication failures.

Grouping by time and user

Sometimes you need to know not only when the spike happened, but which user was involved. You can group by both bin() and another field.
failed-signins-by-user-over-time.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
SigninLogs
| where TimeGenerated > ago(24h)
| where ResultType != 0
| summarize FailedAttempts=count() by bin(TimeGenerated, 15m), UserPrincipalName
| where FailedAttempts > 5
| order by FailedAttempts desc
Time windowbin(TimeGenerated, 15m) creates 15-minute blocks.
User groupingUserPrincipalName shows which account generated the failed attempts.
Investigation shortlistThe where line after summarize reduces the output to higher-volume suspicious windows.

Email bursts over time

bin() is also useful for email investigations. Sudden spikes in email volume may indicate phishing campaigns, compromised senders, noisy systems or bulk external delivery.
email-volume-over-time.kql
  1. 1
  2. 2
  3. 3
  4. 4
EmailEvents
| where Timestamp > ago(24h)
| summarize EmailCount=count() by bin(Timestamp, 30m)
| order by Timestamp asc
Plain-English translation:

Count email events in 30-minute blocks to see when email activity increased.

Endpoint activity bursts

Endpoint behaviour can also appear in bursts. A suspicious process may execute repeatedly in a short time window, or a script may launch across multiple devices during an incident.
endpoint-process-bursts.kql
  1. 1
  2. 2
  3. 3
  4. 4
DeviceProcessEvents
| where Timestamp > ago(24h)
| summarize ProcessCount=count() by bin(Timestamp, 30m)
| order by Timestamp asc
Execution timingLook for sudden increases in process activity.
Hunt follow-upAfter finding the spike, drill into FileName, DeviceName, InitiatingProcessFileName and command lines.
Behaviour over filenameThe timing pattern may be suspicious even when the process name looks normal.

Combining where, summarize, bin() and order by

By this stage, your KQL query can follow a practical investigation pattern: choose a table, filter the data, group by time and sort the timeline.
full-bin-investigation-pattern.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
EmailEvents
| where Timestamp > ago(24h)
| where Subject contains "invoice"
| summarize EmailCount=count() by bin(Timestamp, 1h)
| order by Timestamp asc
Plain-English translation:

Find invoice-related emails, count them by hour and show the timeline from oldest to newest.

Beginner mistakes with bin()

bin() is simple once it clicks, but there are a few common mistakes that can make the results confusing.
Using the wrong timestamp fieldDifferent tables use different time fields, such as Timestamp or TimeGenerated.
Choosing a window that is too largeA 1-day bucket may hide a 10-minute attack burst.
Forgetting to sort the timelineUse order by TimeGenerated asc or Timestamp asc so the output reads like a timeline.

Investigator mindset

Attackers rarely generate perfectly normal timing patterns.

A user might fail a login once.

A password spray attack may generate hundreds of failures in minutes.

The timing itself becomes evidence.
When did the spike happen?Use bin() to identify the exact time window where activity increased.
What happened during the spike?After finding the window, drill into the detailed rows from that timeframe.
Was it normal?Compare the spike with expected user, system or business behaviour before assuming compromise.
The logs already knew when the attack accelerated.
bin() helps defenders reveal suspicious timing patterns hiding inside large security datasets.
Continue Learning

What you learned

In this lesson, you learned how bin() helps defenders group Microsoft security telemetry into time windows and reveal time-based investigation patterns.
bin() groups timebin() rounds timestamps into fixed intervals such as 15 minutes, 1 hour or 1 day.
summarize counts each windowUsing summarize with bin() helps measure how much activity occurred during each time block.
timing reveals behaviourSpikes and bursts can show password spray attempts, phishing campaigns, endpoint activity or suspicious sign-in patterns.

Next lesson coming soon

The next Agent Foskett Academy lesson will introduce distinct, helping defenders find unique users, devices, IP addresses, senders, domains and other values inside Microsoft security telemetry.
Lesson 9 — Finding Unique Values with distinct Learn how distinct helps defenders reduce repeated rows and identify unique accounts, IPs, devices, domains and senders.
Keep building the investigation After grouping activity over time, the next step is learning how to remove repetition and focus on unique evidence.

Finding Time Patterns with bin() in KQL

Agent Foskett Academy Lesson 8 teaches defenders how to use bin() inside Microsoft Defender XDR and Microsoft Sentinel investigations to group telemetry into time windows.

Learn KQL bin() for Microsoft Defender XDR

KQL bin() helps defenders identify spikes, bursts, repeated behaviour and suspicious timing patterns across Microsoft security telemetry.

KQL Time Pattern Analysis for Security Investigations

Time-based KQL analysis helps defenders investigate SigninLogs, EmailEvents, DeviceProcessEvents, DeviceNetworkEvents, suspicious sign-ins, phishing campaigns, endpoint bursts, password spray attempts and Microsoft security telemetry.