Module 6: Create queries for Microsoft Sentinel using Kusto Query Language (KQL) | SC-200

0.0(0)
Studied by 1 person
call kaiCall Kai
learnLearn
examPractice Test
spaced repetitionSpaced Repetition
heart puzzleMatch
flashcardsFlashcards
GameKnowt Play
Card Sorting

1/70

encourage image

There's no tags or description

Looks like no tags are added yet.

71 Terms

1
New cards

What is a KQL query?

A read-only request to process data and return results. It never modifies data.

2
New cards

How does data flow in a KQL query?

Start with a table, then pass the results through operators using the pipe ( | ). The LEFT side runs first; its result flows to the next operator on the right.

3
New cards

What does the pipe ( | ) do?

Passes the table result from one step to the next operator.

4
New cards

Tabular expression statement

The most common KQL statement. Its input and output are both tables.

5
New cards

Two common Sentinel tables used in the module

SecurityEvent (Windows security events) and SecurityAlert (alerts).

6
New cards

Is KQL case-sensitive for table names, column names and operators?

Yes. where, let, search must be lowercase. 'Let' or 'Search' with a capital letter fails. SecurityEvent must be spelled exactly.

7
New cards

search operator

Quick way to hunt for a term across all tables and columns. Example: search "new". Easy, but slower than where on a specific table.

8
New cards

Search only specific tables

search in (SecurityEvent, App*) "new" → searches SecurityEvent plus every table whose name starts with App.

9
New cards

where operator

Filters rows down to only those that match a condition. Example: SecurityEvent | where TimeGenerated > ago(1h)

10
New cards

Filter to the last 1 hour

where TimeGenerated > ago(1h) (use 1h, not 1hr. Units: d = days, h = hours, m = minutes, s = seconds)

11
New cards

Two ways to combine filters

1) One line with and / or: where TimeGenerated > ago(1h) and EventID == 4624 2) Stack several where lines (easier to read).

12
New cards

Event ID 4624

Successful logon.

13
New cards

Event ID 4625

Failed logon.

14
New cards

Event ID 4634

Logoff (used with 4624 in the logon/logoff join example).

15
New cards

Event ID 4688

A new process was created.

16
New cards

Event ID 4672

Special privileges assigned to a new logon (privileged / admin-level logon).

17
New cards

Filter on several values at once

Use in: where EventID in (4624, 4625)

18
New cards

== vs =~

== is case-SENSITIVE equals. =~ is case-INSENSITIVE equals. Example: AccountType =~ "user" matches User, USER, user.

19
New cards

!= vs !~

!= is case-sensitive not-equal. !~ is case-insensitive not-equal.

20
New cards

let statement

Declares a variable you can reuse. Ends with a semicolon. Example: let timeOffset = 1h; let discardEventID = 4688;

21
New cards

let can store a whole dataset

let LowActivityAccounts = SecurityEvent | summarize cnt = count() by Account | where cnt < 1000; then: LowActivityAccounts | where Account contains "sql"

22
New cards

Filter a window between 2 hours ago and 1 hour ago (timeOffset = 1h)

where TimeGenerated > ago(timeOffset*2) and TimeGenerated < ago(timeOffset)

23
New cards

extend operator

Adds a NEW calculated column. All original columns stay. Example: extend StartDir = substring(ProcessName, 0, string_size(ProcessName) - string_size(Process))

24
New cards

order by / sort by

Sorts rows. Add asc or desc per column. Default is desc. Example: order by StartDir desc, Process asc

25
New cards

project

Choose which columns to show. Can also rename and add computed columns. Example: SecurityEvent | project Computer, Account

26
New cards

project-away

REMOVES the columns you list. Keeps everything else.

27
New cards

project-keep

KEEPS only the columns you list (removes the rest).

28
New cards

project-rename

Renames columns. Example: project-rename NewName = OldName

29
New cards

project-reorder

Changes the order of columns in the output.

30
New cards

summarize operator

Groups rows and calculates aggregates (count, dcount, avg, sum, max, min). Example: summarize count() by Account

31
New cards

count() vs countif()

count() = number of rows per group. countif(condition) = only rows that meet the condition.

32
New cards

dcount()

Estimated number of DISTINCT (unique) values. Example: summarize dcount(IpAddress)

33
New cards

avg(), sum(), max(), min()

Average, total, highest and lowest value across each group. Each also has an -if version (avgif, sumif, maxif, minif).

34
New cards

Password spray detection idea (module example)

SigninLogs | where ResultDescription has "Invalid password" | summarize applicationCount = dcount(AppDisplayName) by UserPrincipalName, IPAddress | where applicationCount >= threshold

35
New cards

arg_max(TimeGenerated, *)

Returns the full row with the LATEST TimeGenerated per group (most recent record). Example: summarize arg_max(TimeGenerated, *) by Computer

36
New cards

arg_min(TimeGenerated, *)

Returns the full row with the EARLIEST TimeGenerated per group.

37
New cards

make_list()

Builds an array of ALL values per group (duplicates kept). Example: summarize make_list(Account) by Computer

38
New cards

make_set()

Builds an array of UNIQUE values per group (no duplicates).

39
New cards

make_list vs make_set (memory trick)

List = Lots (keeps duplicates). Set = Single copy of each (unique only).

40
New cards

render operator

Turns results into a chart. Goes at the END, usually after summarize. Example: summarize count() by Account | render barchart

41
New cards

render chart types in the module

areachart, barchart, columnchart, piechart, scatterchart, timechart

42
New cards

bin()

Groups time into buckets. Example: summarize count() by bin(TimeGenerated, 1m) | render timechart

43
New cards

union operator

STACKS rows from two or more tables into one result. Example: SecurityEvent | union SigninLogs

44
New cards

union kind=outer vs kind=inner

outer (default) keeps all columns from all tables. inner keeps only columns that exist in every table.

45
New cards

union withsource=SourceTable

Adds a column showing which table each row came from.

46
New cards

SecurityEvent | union SigninLogs | summarize count()

Returns ONE row: the total row count of both tables combined.

47
New cards

SecurityEvent | union (SigninLogs | summarize count() | project count_)

Returns ALL SecurityEvent rows plus ONE extra row holding the SigninLogs count. The summarize only applies inside the parentheses.

48
New cards

union App* | summarize count() by Type

Counts rows for every table whose name starts with App. The Type column holds the table name.

49
New cards

join operator

Combines COLUMNS from two tables where a key matches (for example Account). Syntax: T1 | join kind=inner (T2) on Account

50
New cards

union vs join (memory trick)

union = add ROWS (stack on top). join = add COLUMNS (side by side on a matching key).

51
New cards

Default join kind

innerunique: removes duplicate keys from the LEFT table, then does an inner join. Write kind=inner if you want every match.

52
New cards

join kind=inner

Only rows that match in BOTH tables.

53
New cards

join kind=leftouter / rightouter / fullouter

leftouter = all left rows + matches from right (blank if no match). rightouter = the reverse. fullouter = all rows from both.

54
New cards

join kind=leftanti / rightanti

leftanti = left rows with NO match in the right table. Great for finding what's missing. rightanti = the reverse.

55
New cards

join kind=leftsemi / rightsemi

leftsemi = left rows that DO have a match; only left columns returned. rightsemi = the reverse.

56
New cards

Join when key column names differ

on $left.ColumnA == $right.ColumnB

57
New cards

Join best practice

Filter both tables first and put the smaller table on the LEFT.

58
New cards

extract() function

Pulls part of a string using regex. Format: extract(regex, captureGroup, text). capturegroup 0 = entire match, 1 = for the value matched by regex.

59
New cards

extract("x=([0-9.]+)", 1, "hello x=45.6|wo") returns?

"45.6"

60
New cards

tolower()

Converts text to lowercase. Often used before matching or extracting so case doesn't matter.

61
New cards

parse operator

Extracts values from a string expression into one or more calculated columns. Example: parse EventText with * "resourceName=" resourceName ", totalSlices=" totalSlices:long * (* = skip text, :long / :date sets the type)

62
New cards

extract vs parse

extract = regex, one value per call. parse = fixed text pattern, several columns at once.

63
New cards

Read a field inside a Nested (JSON) column

Dot notation.

Example: SigninLogs | extend OS = DeviceDetail.operatingSystem

64
New cards

parse_json()

Converts a JSON string into a dynamic object so you can read its fields. Example: extend AuthDetails = parse_json(AuthenticationDetails)

65
New cards

AuthDetails[0].authenticationMethod

[0] = first item in the JSON array, then .authenticationMethod reads that field. Bracket form also works: AuthDetails[0]["authenticationMethod"]

66
New cards

externaldata operator

Reads data from outside storage (for example an Azure Blob file with a SAS token) into the query. You define the schema, e.g. externaldata (UserID:string) [@"https://…"]

67
New cards

KQL functions / parsers

Save a query as a function (e.g. PrivLogins), then call it by name like a table. Used to build reusable parsers.

68
New cards

has vs contains

has = matches a whole term, faster (uses the index). contains = matches any substring, slower. Prefer has when possible.

69
New cards

Where do you write KQL in Microsoft Sentinel?

Microsoft Sentinel > Logs (KQL mode). In the Defender portal, use Advanced hunting.

70
New cards

Tables ending in _CL

Custom log tables (as in the lab demo, e.g. SecurityEvent_CL). Older custom columns get a type suffix: _s = string, _d = number, _b = boolean, _t = datetime.

71
New cards