Reading width
Wide uses the full column for everything, text, diagrams, code, and exercises. Narrow keeps the standard reading width.
Text size
Scales the body text. Headings and code blocks keep their size.
In this section
0.4 Your First Hunt
The first hunt in this course is a question about normal, and that is deliberate. Every later hunt looks for something that does not fit, and "does not fit" only means something against a description of what does. The most useful description to start with is the simplest one: who signs in, from where, and how, on an ordinary working day.
Sign-ins are a good first subject for three reasons. They are the front door to everything else in a Microsoft 365 tenant, so a hunter who knows them well can see an unusual entry quickly.
They are recorded in one table with a small number of columns that matter. And Northgate's sign-ins have a strong shape, because most of its staff work from its offices, which makes departures easy to see once the shape is written down.
This lesson runs the hunt the way every hunt in the course runs: a brief, a sequence of steps each with a query and a decision, and a record at the end. It reads Tuesday 10 March, an ordinary working day, and then checks that the day is typical of the month before writing the baseline down.
Choosing the day is itself part of the method. A Monday after a holiday, a day with a known incident, or a day when a feed was down would each give a misleading picture. A mid-week day in the middle of the month, checked afterwards against every other working day, is the safest single sample.
SigninLogs is the only table this hunt needs, and keeping a first hunt to one table is deliberate. Every column the hunt reads, the address, the result, the authentication requirement, the device detail, is in the same row, so there are no joins to get wrong and no second time column to remember. Later hunts join tables; this one learns one table well.
The question has a clear end. When the day's sign-ins are described by volume, source and method, and the description is shown to hold across the month, the hunt is done. A question without an end would keep finding interesting rows and never close.
Count the day's sign-ins
Sign-ins, accounts, successes and failures on 10 MarchThe day's whole interactive sign-in record, summarized in a single row of counts:
SigninLogs
| where TimeGenerated >= datetime(2026-03-10) and TimeGenerated < datetime(2026-03-11)
| summarize SignIns = count(), Accounts = dcount(UserPrincipalName),
Succeeded = countif(ResultType == "0"), Failed = countif(ResultType != "0"),
Earliest = min(TimeGenerated), Latest = max(TimeGenerated)
The day's interactive sign-ins, totaled in one row:
SignIns Accounts Succeeded Failed Earliest Latest
------- -------- --------- ------ -------- ------
367 92 353 14 00:26 23:23
367 interactive sign-ins by 92 accounts, 353 of them successful, between 00:26 and 23:23 UTC. Microsoft's reference describes ResultType as a code where "0 indicates success; other values are failures", and the failures here are a few dozen at most: mistyped passwords and expired sessions, which every tenant has.
Microsoft's portal would show the same day in local time if the viewer's settings said so; the course keeps UTC throughout. Northgate is in the UK, and in March UK time is UTC, so the clock times here are also Northgate's office times. In another season or another country that would not hold, and a baseline of working hours would need converting before it was compared with a person's day.
The numbers already describe a working organization: most accounts sign in at least once, most attempts succeed, and the day starts early and ends late because some people do.
A count like this is the first query of almost every baseline, and it is worth running before anything more specific.
It catches the obvious problems early: a day with half the usual sign-ins might be a holiday or a broken feed, and a day with twice as many might be a bulk password reset or something worse. Here the numbers are unremarkable, which is what lets the next steps read the detail with confidence.
The two times matter as well. The earliest sign-in sets the start of the working day as the data sees it, and the latest sets its end. A sign-in well outside that span is not necessarily wrong, since people work late and travel, but it is a departure, and a hunt for sessions that do not fit a person's day starts from exactly these edges.
Check coverage The volume is known. Check where the sign-ins came from.
Split the sign-ins by source
Office range, partner guests and everything else0.2 found that most staff sign-ins come from Northgate's office range, 203.0.113.0/24, and that partner guests sign in from their own networks. The day's sign-ins in those three groups:
SigninLogs
| where TimeGenerated >= datetime(2026-03-10) and TimeGenerated < datetime(2026-03-11)
| extend Source = case(UserPrincipalName has "#EXT#", "partner guest",
IPAddress startswith "203.0.113.", "office range", "elsewhere")
| summarize SignIns = count(), Accounts = dcount(UserPrincipalName),
Addresses = dcount(IPAddress) by Source
| order by SignIns desc
The day's sign-ins in the three source groups:
Source SignIns Accounts Addresses
------------- ------- -------- ---------
office range 339 88 9
elsewhere 15 9 5
partner guest 13 4 13
92% of the day's sign-ins came from the office range, through just 9 addresses, the office's internet connections. 15 came from elsewhere by 9 staff accounts, and 13 from 4 partner guests.
A three-way split like this is the simplest useful model of where sign-ins come from, and it is enough for a first baseline. Later hunts refine it, by country, by autonomous system, by device, but they start from the same question.
The office group's handful of addresses is worth noticing too. An office reaches the internet through a few public addresses shared by everyone inside, so dozens of people appear behind each one.
That makes the office range a strong signal that a sign-in came from inside the building or its network, and a weak signal of which person or device it was. A hunt that needs the device reads DeviceDetail, not the address.
The "elsewhere" group is the part of the baseline that needs the most care. It is small, and in a hunt it is where unusual sign-ins would first appear, so it has to be understood well enough that an ordinary member of it is not mistaken for an intruder, and an intruder is not mistaken for an ordinary member. Step 5 comes back to it across the whole month.
The partner guests are a separate group for a reason. They are not Northgate staff, their companies manage their devices and networks, and Northgate cannot expect them to sign in from its office. Lumping them with staff signing in from elsewhere would inflate that group with people who are supposed to be outside, and hide the staff who are not. Splitting them out keeps each group's pattern clean.
The case expression that builds the three groups is a small piece of KQL that does a lot of work. It tests the guest marker first, so a guest is never counted as office or elsewhere even if their address happened to match, and only then tests the address. Order matters in a case expression, and putting the most specific test first is a habit worth forming.
Check coverage The sources are known. Check how each group authenticated.
Check how each group authenticated
MFA and managed devices on successful sign-ins, by sourceWhere a sign-in came from is half the picture; how it was authenticated is the other half. Microsoft describes AuthenticationRequirement as "the highest level of authentication needed through all the sign-in steps", and DeviceDetail as the device "Includes information such as deviceId, OS, and browser", including whether it is managed and compliant. The day's successful sign-ins by source:
SigninLogs
| where TimeGenerated >= datetime(2026-03-10) and TimeGenerated < datetime(2026-03-11)
| where ResultType == "0"
| extend Source = case(UserPrincipalName has "#EXT#", "partner guest",
IPAddress startswith "203.0.113.", "office range", "elsewhere")
| extend Managed = tostring(parse_json(DeviceDetail).isCompliant) == "true"
| summarize SignIns = count(),
MFA = countif(AuthenticationRequirement == "multiFactorAuthentication"),
ManagedDevice = countif(Managed) by Source
| order by SignIns desc
Successful sign-ins by source, with how many used MFA and a managed device:
Source SignIns MFA ManagedDevice
------------- ------- --- -------------
office range 325 297 108
elsewhere 15 15 0
partner guest 13 13 0
The office group mostly uses MFA; 28 of its successful sign-ins required only a single factor, and the data does not record why those sign-ins were held to less.
Only 33% of the office group's sign-ins came from a device the tenant marks managed and compliant, which fits 0.3's finding that only sixteen staff accounts have a sensor-equipped laptop: most staff sign in from devices Northgate does not manage, even in the office.
The elsewhere group uses unmanaged devices and passes MFA every time. The partner guests also pass MFA every time, on their own companies' devices, which Northgate's tenant does not manage.
That last point corrects an easy assumption. Being in the office range says where a sign-in came from, not what device made it, and on most of Northgate's office sign-ins the device is not one the tenant manages.
Unmanaged is not the same as suspicious. A managed device is one Northgate's device management enrolls and checks; an unmanaged one is anything else, including a person's own laptop or phone.
Many organizations allow staff to reach mail and files from personal devices with MFA, and the pattern here fits that policy. What the baseline records is the pattern, so that a later hunt can tell an ordinary unmanaged sign-in from one that breaks the pattern in some other way.
This split is the baseline's most useful line. A successful single-factor sign-in from outside the office range would break it at once, and so would an elsewhere sign-in on a managed Northgate laptop from a range nobody has used before. Neither happened on 10 March.
The figure puts the two halves together. Reading across a row, a hunter sees a group's whole authentication pattern at once: where it signs in from, how often it succeeds, whether it uses a second factor and on what kind of device. A sign-in that does not fit any row is the thing a hunt would look at first.
The method split also tells a hunter which tables to reach for next. A sign-in on a managed laptop can be followed onto the laptop itself, in the endpoint tables, where 0.3 showed sixteen staff accounts have activity.
A sign-in on an unmanaged device cannot; the trail stops at the cloud, and anything the person did is visible only through the services they used. Knowing that in advance shapes what a hunt can promise.
Check coverage One day is described. Check that it is typical of the month before writing it down.
Test the day against the month
Volume and office share on every working dayA baseline from one day is a sample of one. The same measures on every working day in the window:
SigninLogs
| where TimeGenerated >= datetime(2026-02-13 12:00)
and TimeGenerated < datetime(2026-03-15 12:00)
| where dayofweek(TimeGenerated) between (1d .. 5d)
| summarize SignIns = count(), Accounts = dcount(UserPrincipalName),
OfficeShare = round(100.0 * countif(IPAddress startswith "203.0.113.")
/ count(), 0)
by Day = startofday(TimeGenerated)
| summarize WorkingDays = count(), MinSignIns = min(SignIns),
MaxSignIns = max(SignIns),
MinOffice = min(OfficeShare), MaxOffice = max(OfficeShare)
The range of each measure across the month's working days:
WorkingDays MinSignIns MaxSignIns MinOffice MaxOffice
----------- ---------- ---------- --------- ---------
31 72 425 76 96
Across 31 working days, the daily count ranged from 72 to 425, and the office share from 76% to 96%. Tuesday 10 March, with 367 sign-ins and 92% from the office, sits inside both ranges. The day was typical, and its description holds for the month.
The test itself is a common hunting pattern worth learning now: summarize per day, then summarize the days. The inner summarize turns rows into one row per day; the outer one turns the days into their range. The same two-step shape answers questions like how many processes a device runs per day, or how many mail items an account reads, and it appears in many of the course's baselines.
The range matters more than the average. An average would say what a typical day looks like; the range says how far an ordinary day can move without anything being wrong. A later hunt that sees a day with an office share well outside this range has found something worth explaining, and one inside it has not.
The weekends are left out on purpose. Northgate's sign-ins on Saturdays and Sundays are fewer and come from a different mix of places, and folding them in would widen the range until it described nothing in particular. A baseline is most useful when it describes one kind of day well, and a separate weekend baseline is easy to build with the same query and a different day filter.
Check coverage The pattern holds. Check the group that will matter most in later hunts: the staff who sign in from elsewhere.
Describe the outside group across the month
Staff sign-ins from 192.0.2.0/24 over the whole windowEvery staff sign-in from outside the office on 10 March came from one range, 192.0.2.0/24. The same range across the whole month:
SigninLogs
| where TimeGenerated >= datetime(2026-02-13 12:00)
and TimeGenerated < datetime(2026-03-15 12:00)
| where UserPrincipalName !has "#EXT#" and IPAddress startswith "192.0.2."
| summarize SignIns = count(), Accounts = dcount(UserPrincipalName),
Addresses = dcount(IPAddress), Days = dcount(startofday(TimeGenerated)),
Countries = make_set(Location),
MFA = countif(AuthenticationRequirement == "multiFactorAuthentication")
The outside group summarized over the whole month:
SignIns Accounts Addresses Days Countries MFA
------- -------- --------- ---- ---------- ---
315 16 71 31 GB, IE, SE 315
315 sign-ins by 16 staff accounts from 71 addresses in the range, on 31 days, from GB, IE, SE, and every one with MFA. Sixteen accounts is a small share of the workforce, and their sign-ins come from three countries. A hunter meeting this group for the first time in the middle of an investigation might find it alarming: unmanaged devices, foreign addresses, every day.
Meeting it here, as part of the baseline, removes the alarm without removing the attention. A member of the group signing in as usual is ordinary; a member of the group signing in without MFA, or from a range the group never uses, is not.
That is a regular population, not an event: the same accounts, the same range, every week. It may be staff working from home or abroad, or an outsourced team; the data shows the pattern and the lesson does not need to name its cause to use it.
The outside group is not the whole of "elsewhere" across the month. On some days, staff sign-ins also arrive from addresses outside both ranges:
SigninLogs
| where TimeGenerated >= datetime(2026-02-13 12:00)
and TimeGenerated < datetime(2026-03-15 12:00)
| where UserPrincipalName !has "#EXT#"
| where not(IPAddress startswith "203.0.113.")
| where not(IPAddress startswith "192.0.2.")
| summarize SignIns = count(), Succeeded = countif(ResultType == "0"),
Accounts = dcount(UserPrincipalName), Addresses = dcount(IPAddress),
Days = dcount(startofday(TimeGenerated))
Staff sign-ins from outside both known ranges, across the month:
SignIns Succeeded Accounts Addresses Days
------- --------- -------- --------- ----
161 43 26 12 20
161 sign-ins by 26 accounts from 12 addresses on 20 days. These are the rows the baseline cannot explain, and they are exactly where the course's identity hunts begin, in Modules 1 and 6. This lesson counts them and leaves them there: a baseline's job is to separate the ordinary from the rest, and the rest is the next hunt's subject.
The baseline, written down:
Baseline: who signs in to Northgate on a working day
BaselineVolume
72 to 425 sign-ins on each of 31 working days; about 92 accounts
Elsewhere
A regular group of 16 staff from 192.0.2.0/24 (GB, IE, SE), unmanaged devices, MFA on every sign-in; 161 other staff sign-ins in the month from other addresses, left for later hunts
Partners
Guests from their own networks, never from the office range, MFA on every sign-in
Failures
A few a day; 205 of the month's 336 failures came from the office range
Use
A sign-in outside these patterns is worth a look: a new range, a single-factor success from elsewhere, a member account with no history
Office range 76 to 96% of sign-ins from 203.0.113.0/24, a handful of addresses, mostly MFA, about a third on managed devices
A baseline is a description precise enough that a departure from it can be seen. Each line here comes from a query in this lesson.
Each line of the baseline is phrased so that a single query can test a later day or account against it. That is the practical difference between a baseline and a description. "Most people work from the office" cannot be tested; "between 76 and 96% of sign-ins from 203.0.113.0/24 on a working day" can, and a day outside it stands out without anyone having to remember what normal felt like.
The last line, Use, is the part that turns the baseline into hunting. It names the departures that would matter: a new range, a single-factor success from outside the office, a member account with no history. Each of those is the seed of a hypothesis a later hunt can test, and Module 6 tests several of them on this same table.
The hunt record
Outcome: explainedThe record closes the first hunt. Explained means the question is answered and the answer is a description, not a finding.
On Tuesday 10 March, 92 accounts signed in 367 times: 92% from the office range, 33% of those on a managed device, 15 from elsewhere, all from 192.0.2.0/24 with MFA on unmanaged devices, and 13 by partner guests with MFA. Every working day in the month falls between 72 and 425 sign-ins with 76 to 96% from the office. The outside group is 16 staff on 31 days, every sign-in with MFA.
- Day: 367 sign-ins, 92 accounts, 14 failed
- Sources: office 339, elsewhere 15, partners 13
- Method: elsewhere 15 of 15 with MFA, 0 managed
- Month: 31 working days, 76 to 96% office
- Outside group: 16 accounts, 71 addresses, 192.0.2.0/24
Interactive sign-ins only. Non-interactive sign-ins, token refreshes and sign-ins by applications are in other tables and are not part of this baseline.
- Use the baseline in identity hunts (Module 6)
- See how hunts are practiced in the course's tools (0.5)
The baseline will also need maintaining. Organizations change: a new office opens, a partner is added, a policy requires managed devices for everyone. Each change moves the pattern, and a baseline from last quarter will start flagging ordinary activity. The record's window and query make that easy to handle, because rerunning the hunt on a new month produces the new baseline in minutes and the two can be compared directly.
The hunt found nothing, and it was a success. It turned a vague sense that "most people work from the office" into a measured pattern with ranges, a named outside group and a method split, all of which a later hunt can test against in one query. That is what most hunting produces most of the time, and it is what makes the rare real finding visible.
The record's bounds deserve a second look, because they are the part most first hunts leave out. The baseline covers interactive sign-ins only. A token stolen and replayed, or an application signing in on its own, appears in the non-interactive and service principal logs, and this baseline says nothing about them. Writing that limit down is what stops a later reader from assuming the baseline covers more than it does.
This hunt also shows the minimum a record needs, even when nothing was found. It states the finding as a description with numbers, lists the queries' key results as bookmarks, gives the bounds, and points to where the result will be used.
A colleague who reads it without the lesson can rerun every query and get the same baseline, and that is the test every record in the course is written to pass.
Microsoft's description of Sentinel hunts lists "suspicious behavior" and "detection gaps" among the common starting points for a hypothesis. Both need a baseline. A behavior is suspicious against what is normal for the place, and a gap is visible only once the normal coverage is known. The rest of the course builds many baselines like this one, and this is the pattern for all of them.
Practice
Does the baseline hold on another day? hands onThe lesson read Tuesday 10 March. Rebuild the source split for a different working day of your choice and compare it with the baseline.
Write down the day you chose, its office share and its outside sources. Then write whether it fits the baseline, and why.
Read the evidence
Reading the baselineThe check gives you the day's sign-ins by source and method. Decide what each line supports.
Resources
Publishers revise these pages; where a page and this lesson disagree, the page is newer.