In this section

0.2 The SecOps Range and the Console

Module 0

Introduction

Every lesson in this course proves its configuration on the SecOps Range, a month of records from one organization's Microsoft tenant, queried from a console on the page. The range is where the course's numbers come from and where you check them, so the time spent learning how it works and what it holds is repaid in every module.

This lesson covers what the range is, its window, the two time columns, how the console works, where to check a table's columns, why every number comes from a query, what the range is not, and the working habit the course expects. Its build is the first one you can do without a tenant.

1

What the Range Is

One organization's month, with five investigations inside it

The SecOps Range is a dataset built in the shape of a real Microsoft security workspace: the same tables, the same columns, the same kinds of records, for one organization over one month:

What the SecOps Range holds

From the range's manifest

Tables

63, the ones a Microsoft security workspace and the Defender portal hold

Rows

212,661 in all

Window

one month, from 13 February to 15 March 2026

The organization

a mid-sized company with Microsoft 365 E5, devices, Azure and a Sentinel workspace

The investigations

five, inside the ordinary month, at the same scale as everything around them

What it is for

Proving each configuration the course teaches against records with a known answer.

It holds the tables a Sentinel workspace and the Defender portal's advanced hunting hold: sign-ins and directory changes from Entra ID, mailbox and file activity from Office 365, device events from Defender for Endpoint, mail and click records from Defender for Office 365, cloud app activity, identity events from Defender for Identity, Azure activity, alerts and incidents, Sentinel's own health and audit tables, and the logs of a perimeter firewall and some Linux servers.

The organization is ordinary on purpose. Most of the month is ordinary work: people signing in, sending mail, opening files, administrators changing settings, alerts that mean nothing, incidents closed as benign.

The five investigations the course works on are inside that month, at the same scale as everything around them, which is what makes them realistic to find. An attack in an empty dataset is easy to see; one inside 31 days of an organization's ordinary noise is the job.

The range exists for one reason: to prove configurations against records with a known answer. When a lesson says a Conditional Access policy would have refused a sign-in, the range holds the sign-in. When it says an eviction left a foothold, the range holds the foothold. You can check every claim the course makes, because the evidence for it is in the tables.

It is not a simulation that reacts. Nothing you run changes it, and nothing arrives after the month ends. That makes it a fixed reference: the same query returns the same result for every student, every time, which is what lets the prose quote numbers.

Treat the range as one organization's real month, with the course's five investigations inside its ordinary noise, and every claim the course makes checkable against it.

2

The Window

Thirteen February to fifteen March

The first thing to know about any dataset is its window: when the records start and when they stop. Every Sentinel-side table on the range, with its row count and the first and last record:

union withsource = Table SigninLogs, AuditLogs, OfficeActivity,
    SecurityIncident, SecurityAlert, AzureActivity, AzureDiagnostics,
    CommonSecurityLog, Syslog
| summarize Rows = count(), FirstSeen = min(TimeGenerated),
    LastSeen = max(TimeGenerated) by Table
| order by Rows desc

Every table runs from 13 February to 15 March 2026. OfficeActivity is the largest of these, with 16,357 rows, then SigninLogs with 8,207. SecurityIncident holds 1,304 rows, more than the number of incidents, because each change to an incident writes a new row; queries that want one row per incident take the latest with arg_max.

AzureActivity ends earlier than the rest, on 14 March, because Azure control-plane changes are rarer than sign-ins and the last one fell then. A table whose last record is earlier than the others is not necessarily broken; it is a reason to ask whether that table's source stopped, which is exactly the question a later module teaches you to ask of a real workspace.

The window matters every time you read a result. A query with no time filter covers the whole month. A query with a filter covers only its window, and a negative result from it says nothing about the days outside. Lessons state their windows, and when you change a query, the window is the first thing to check.

The window also fixes what "now" means. On the range, the present is the end of the month, and a lesson that says something is still open means it is open at 15 March. In your own tenant, the window is your workspace's retention, and the same queries cover whatever it holds.

The same check applies to every dataset you meet after this course: a vendor export, a log a colleague sends, a workspace you inherit. Its first and last record, per table, take one query to read and settle what any later result can mean.

Know a dataset's window before reading anything from it, and read every negative result as bounded by the window the query covered.

3

Two Time Columns

TimeGenerated and Timestamp

The Defender portal's advanced hunting tables use a different time column. The same summary for seven of them:

union withsource = Table DeviceProcessEvents, DeviceNetworkEvents,
    EmailEvents, UrlClickEvents, CloudAppEvents, IdentityLogonEvents, AlertInfo
| summarize Rows = count(), FirstSeen = min(Timestamp),
    LastSeen = max(Timestamp) by Table
| order by Rows desc

The window is the same, and the tables are large: EmailEvents holds 13,092 rows, DeviceProcessEvents 9,587. The difference is in the query. These tables record time in Timestamp, where the Sentinel tables use TimeGenerated:

Two time columns, two families of tables

The first thing every cross-service query handles

TimeGenerated

Sentinel and Azure Monitor tables: SigninLogs, AuditLogs, OfficeActivity, SecurityAlert

Timestamp

Defender XDR advanced hunting tables: Device, Email, Url, CloudApp, Identity events

In a union of both

name the column each branch uses, or the query fails or returns nothing

All times

UTC, in both families

In the prose

times are given in the range's UTC, to the minute

Why

The two families come from different products, and kept their own conventions.

That split is the first thing every cross-service query handles. A union of SigninLogs and DeviceNetworkEvents cannot filter on one time column, because each table has only its own; each branch filters on its own column and, where the results are combined, projects them into one name.

The course's queries do this throughout, and the pattern is worth recognizing early: a branch that says where TimeGenerated for a Sentinel table and where Timestamp for a Defender one.

In a tenant where Sentinel is onboarded to the Defender portal, both families are queryable from the same hunting page, and the split still applies, because it is in the tables.

In Log Analytics, the Defender tables that are streamed into the workspace also carry TimeGenerated, set when the workspace received the row; Timestamp remains the time the event happened. On the range each family carries its own column, as the queries above use.

All times on the range are UTC, in both families, and the prose gives times in UTC to the minute. When a lesson says the first alert fired at 08:33 on 9 March, that is the TimeGenerated of the row.

The split is easy to forget in the middle of an investigation, when a query that worked on one table is copied to another. Reading the error, which names the column, is usually enough to fix it, and after a few modules the habit of checking each branch's time column becomes automatic.

In every cross-service query, filter each table on its own time column, and read every time on the range as UTC.

4

The Console

Run, change, reset

Every query block in the course is live. It runs in the page against the range, and you can change it:

From a query block to a resultThe blockthe query as shippedEditchange anythingRunCtrl+Enter or the buttonResultfirst 50 rows shownResetback to the shipped queryThe result is the evidenceEvery number in the prose came from it.Change the query and the number moves with it.

Nothing you do in the console changes the range; reset is always one click away.

The controls are few, and they are the same on every page:

The console on every lesson page

From the console's own code

Run

the button, or Ctrl+Enter in the query

Results

the first 50 rows shown, with the total; a chart view where the result suits one

Reset

restores the query as shipped and clears the result

Limits

queries up to 8,000 characters; the range's tables only

Errors

shown in the panel, with the engine's message

Nothing breaks

The range is read-only; any change you make is to your copy of the query.

The result panel shows the first 50 rows and the total, so a query that returns thousands of rows still shows how many there were. Where the result is a count over time or over categories, the chart view draws it.

The panel also shows how long the query took, which is a useful signal: a query that takes much longer than the others is usually doing more work than it needs.

Errors appear in the panel with the engine's message. Most are one of three kinds: a column that does not exist on the table, a time column from the wrong family, or a syntax error such as a missing pipe. The message names the problem, and the fix is usually in the line it names.

Change queries freely. The range is read-only, and nothing you run changes it or anyone else's view of it. Reset restores the query exactly as the lesson shipped it and clears the result, so the cost of an experiment is one click.

The fastest way to understand a result is to change the query and see what moves: remove a filter, widen the window, add a column, group by something else.

The console runs KQL, the Kusto Query Language used by Sentinel, Log Analytics and Defender's advanced hunting. It supports the operators the course uses and most others. A few operators your tenant supports are not available on the range; where a lesson needs one, it uses an equivalent, and the query you run in your tenant can use either.

Run every query as you read, change it at least once, and use reset whenever you want the lesson's original back.

5

Checking a Table's Columns

Before reading an empty result as an answer

A query can only find what a table records, and the most common mistake with a new table is filtering on a column that is not there. Every table has a schema, the list of its columns and their types, and there are three places to read it:

Where to check a table's columns

Three places, one habit

On the range

every table carries a schema with its columns and their types

In your tenant

the schema pane beside the query editor in the Defender portal or Log Analytics

Microsoft Learn

each table's reference page: columns, types and descriptions

The habit

check the column exists before reading an empty result as an answer

A missing column

is an error in your tenant and an empty result in some tools

The trap

A filter on a column that does not exist proves nothing about the data.

On the range, each table carries its schema, and the console's errors name a column that does not exist. In your tenant, the query editors in the Defender portal and Log Analytics both show the schema beside the query, table by table.

Microsoft Learn has a reference page for each table, with each column's type and description, which is where to check what a column means as well as whether it exists.

The habit to build is checking before trusting an empty result. A query that returns nothing because the attacker did nothing and one that returns nothing because the filter named a column the table does not have look identical in the result panel. The difference shows only if you look: the column in the schema, and the same filter shown returning a row for something known.

Column names also differ between tables that hold similar things. A user is UserPrincipalName in SigninLogs, UserId in OfficeActivity, AccountUpn in UrlClickEvents and AccountDisplayName in some CloudAppEvents rows. A device is DeviceName in Defender tables and Computer in some Sentinel ones. Cross-service queries project these into one name, which is why the course's unions often begin each branch with a project.

Values matter as much as names. A column can exist and hold a value in a different form from the one you filter on: a product name the table still keeps in its legacy form, an address in a different case, a time in a different column. Checking a few rows of a table before writing a filter catches most of these.

Read a table's columns and a few of its rows before filtering on them, and never read an empty result until the filter is shown able to match.

6

Every Number Comes from a Query

The rules the course follows, and how you check them

The course has one rule about numbers, and it is the reason the range exists: every number in a lesson comes from a query you can run. The rules in full:

The rules every number in this course follows

Checkable, every time

A count

comes from a query on the page, run as you read

A time

comes from a row, to the minute, in UTC

A negative

comes with the search shown able to return a positive

A Microsoft statement

carries the date it was checked against Microsoft Learn

Anything else

is labeled as an inference, with what it rests on

What you do

Doubt a number, rerun the query.

A count in the prose is the result of a query on the same page, run when the page was built, and it is the number you get when you run it. A time is from a row, in UTC.

A negative, "no device contacted the attacker", comes with the search that shows it, and with evidence that the same search returns rows for something else, so an empty result means something. A statement about a Microsoft product, its licensing or its defaults carries the date it was checked, because those change. Anything that is not one of these is labeled an inference, with what it rests on.

The rules exist because the alternative fails in a specific way. A number recalled from memory, carried forward from an earlier draft or estimated from a pattern looks exactly as plausible as a measured one.

Nobody can tell them apart by reading. The only way to know a number is right is to have produced it from the record, and the only way for a reader to trust it is to be able to produce it too.

The same rules apply to your own work. Every build in this course asks for evidence: a query result, an export, a dated note. A configuration you say you made is a claim; the export or the query result that shows it is evidence. The course exam asks for the same thing, and so will anyone who reviews your work in a real SOC.

When you doubt a number in a lesson, rerun the query. If the result differs, the lesson is wrong and you have found something worth reporting. That has happened while the course was being written, and each time the record won.

Treat every number as a claim to check, produce your own from the record, and label anything else as what it is.

7

What the Range Is Not

Limits to keep in mind

The range is a teaching dataset, and it has limits worth stating plainly:

What the range is not

Limits to keep in mind

Your tenant

its scale, licenses and settings are one organization's, chosen for teaching

Live

a fixed month; nothing arrives after 15 March

Complete

the tables a lesson needs, not every table a tenant could hold

Current in its names

product names in some tables are the legacy values those tables keep

The same engine

the console runs most KQL; a few operators your tenant supports are not available

The rule

Run a lesson's query in your own tenant before relying on it there.

It is not your tenant. Its organization has the licenses, the device count, the policies and the habits chosen to make the course's lessons visible. Your tenant will differ in scale and in configuration, and a query that finds one thing on the range may find nothing, or a great deal, in yours. That is the point of the builds: each one runs the lesson's method on your own records.

It is not live. The month is fixed, and nothing arrives after 15 March. A real workspace keeps filling; a query that is quiet today may not be quiet tomorrow, and the course's lessons on watching and rechecking assume a workspace that keeps moving.

It is not complete. The range holds the tables the course needs, which is most of what a Microsoft security workspace holds, and not every table a tenant could have. Where a lesson reaches a table the range does not hold, it says so and shows the portal or PowerShell route instead.

Some names are not current. The alert table and a few others keep product names from before Microsoft's renames, as they do in a real tenant. And the engine is not identical to your tenant's: a few operators are not available, and the course uses equivalents. A query copied from the range to your tenant runs; one copied the other way may need an operator replaced.

None of these limits changes what the range is good for. It holds an organization's month with a known answer, which is exactly what a method needs to be learned and checked against, before it meets records where nobody knows the answer yet.

Use the range to learn and to check, and run every lesson's method in your own tenant before relying on it there.

8

The Working Habit

Change the query and read what moves

The course expects one habit from the first page: when a query result matters, change the query and see what moves. A result read once is a fact you have been shown; a result you have changed three ways is one you understand.

The changes that teach the most are simple, and the same five work on almost any query:

Five changes that teach the most

Each answers a question the original did not ask

Narrow the window

which rows remain, and which day they cluster on

Remove a filter

how much it was excluding

Group differently

what a total was made of

Count distinct

many people, or one person many times

Break it on purpose

the error the engine gives, before you meet it for real

Then

Reset, and note what moved.

Each change answers a question the original query did not ask, and the answers are often the point of the lesson.

The habit also catches mistakes, yours and the course's. A filter that excluded more than intended, a join that dropped rows, a time column from the wrong family: each shows up as a result that moves in a way you did not expect. The build for this lesson practices exactly that, on the two queries above, before any lesson depends on it.

Breaking a query on purpose is part of the habit. Using Timestamp on a Sentinel table, or a misspelled column, shows you the error the engine gives, which is the error you will meet on your own; knowing it in advance turns a confusing moment into a quick fix.

Finally, keep what you learn. The build cards ask for a record of each build; for this lesson, the record is what moved when you changed each query and why. That record, kept through the course, becomes the most useful reference you have when you write the same queries against your own tenant.

The habit costs a minute per query and pays back the first time a result in your own tenant is not what you expected, because you will already know which change to make first to find out why.

Change every query that matters to you, break one on purpose, and keep a note of what moved and why.

Do

Practice

Run, change and reset the range's queries
your tenant

The build works the console on this lesson's two queries, and needs nothing but the page.

BuildRun, change and reset the range's queriesYour tenant
Requirement

A short record of working the console: each of this lesson's two queries run as shipped, changed three ways, and reset, with what moved each time and why.

RoleNone: the range is in the page
LicenseNone
CostNone
ChangesNone
  1. Admin center

    Run both queries

    As shipped; note the window and the largest table.

  2. Admin center

    Narrow the window

    Add a where on the time column for one week; note what moves.

  3. Admin center

    Break one

    Use Timestamp on a Sentinel table; read the error.

  4. Admin center

    Add a column

    dcount of an account or device column by table.

  5. Admin center

    Reset

    Back to the shipped query.

Success criteria
  • Each change produced the result you predicted, or you can say why it did not.
  • The broken query's error names the missing column.
Evidence to keep
  • The record of the five runs, with the results that changed.
Rollback

Reset restores every query.

Without a tenant

In your own tenant, run the second query in Advanced hunting and compare its tables and counts with the range's.

The SecOps Range is one organization's month, from 13 February to 15 March 2026, in 63 tables and 212,661 rows, with the course's five investigations inside it. Sentinel tables record time in TimeGenerated and Defender tables in Timestamp, and every cross-service query filters each on its own. The console runs every query on the page, shows the first 50 rows, and resets to the shipped query in one click.

Every number in the course comes from a query you can run, every negative from a search shown able to find something, and every Microsoft statement carries its date. If you do one thing this week, change one of this lesson's queries three ways and write down what moved.

Resources

Microsoft revises these pages; where a page and this lesson disagree, the page is newer.