Home›Detection Engineering›KQL Library
Library
KQL Library
Investigation KQL for Microsoft Sentinel & Defender — copy, run, tune to your tenant.
The twin to the PowerShell Library: the queries I actually reach for during an investigation, grouped by what you're hunting. Each one says what it finds, when to run it, and the gotcha that bites. Filter by category or search by keyword.
# Seed set — column names and result codes vary by tenant and workspace. Treat these as starting points: verify the schema and tune the thresholds before you trust an alert built on one.
Many failures, then a success (spray → hit)
IdentitySigninLogs
| where TimeGenerated > ago(24h)
| summarize Failures = countif(ResultType != 0),
Successes = countif(ResultType == 0),
IPs = dcount(IPAddress)
by UserPrincipalName
| where Failures >= 10 and Successes >= 1
| sort by Failures desc
Successful legacy-auth sign-ins (MFA bypass)
IdentitySigninLogs
| where TimeGenerated > ago(7d)
| where ResultType == 0
| where ClientAppUsed in ("Other clients","IMAP4","POP3","SMTP","MAPI","Exchange ActiveSync")
| summarize count() by UserPrincipalName, ClientAppUsed, AppDisplayName
| sort by count_ desc
One account, two countries, one hour
IdentitySigninLogs
| where TimeGenerated > ago(1d)
| where ResultType == 0
| summarize Countries = dcount(Location), List = make_set(Location), IPs = make_set(IPAddress)
by UserPrincipalName, bin(TimeGenerated, 1h)
| where Countries >= 2
Inbox rule that hides or forwards mail
Email & collabCloudAppEvents
| where TimeGenerated > ago(7d)
| where ActionType in ("New-InboxRule","Set-InboxRule")
| where tostring(RawEventData.Parameters) has_any
("DeleteMessage","ForwardTo","RedirectTo","ForwardAsAttachmentTo","MoveToFolder")
| project TimeGenerated, AccountDisplayName = tostring(RawEventData.UserId),
ActionType, Rule = tostring(RawEventData.Parameters)
Sudden mass external send
Email & collabEmailEvents
| where TimeGenerated > ago(24h)
| where EmailDirection == "Outbound"
| summarize Recipients = dcount(RecipientEmailAddress), Messages = count()
by SenderFromAddress, bin(TimeGenerated, 1h)
| where Recipients >= 50
| sort by Recipients desc
Encoded / hidden PowerShell
EndpointDeviceProcessEvents
| where TimeGenerated > ago(7d)
| where FileName in~ ("powershell.exe","pwsh.exe")
| where ProcessCommandLine has_any ("-enc","-EncodedCommand","FromBase64String","-w hidden","-windowstyle hidden")
| project TimeGenerated, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName
| sort by TimeGenerated desc
Office app spawning a shell
EndpointDeviceProcessEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","wscript.exe","cscript.exe","mshta.exe","rundll32.exe","regsvr32.exe")
| project TimeGenerated, DeviceName, InitiatingProcessFileName, FileName, ProcessCommandLine
New secret added to an app or service principal
PersistenceAuditLogs
| where TimeGenerated > ago(30d)
| where OperationName in~ ("Add service principal credentials",
"Update application - Certificates and secrets management",
"Add app role assignment to service principal")
| project TimeGenerated, OperationName,
Actor = tostring(InitiatedBy.user.userPrincipalName),
Target = tostring(TargetResources[0].displayName)
| sort by TimeGenerated desc
Someone added to a privileged role
PersistenceAuditLogs
| where TimeGenerated > ago(7d)
| where OperationName == "Add member to role"
| extend Role = tostring(TargetResources[0].displayName)
| where Role has_any ("Admin","Administrator")
| project TimeGenerated, Role,
Actor = tostring(InitiatedBy.user.userPrincipalName),
Added = tostring(TargetResources[0].userPrincipalName)
OAuth app consent (illicit-consent phishing)
Cloud & exfilAuditLogs
| where TimeGenerated > ago(14d)
| where OperationName in~ ("Consent to application","Add delegated permission grant","Add OAuth2PermissionGrant")
| project TimeGenerated, OperationName,
Actor = tostring(InitiatedBy.user.userPrincipalName),
App = tostring(TargetResources[0].displayName)
| sort by TimeGenerated desc
Bulk file downloads (staging exfil)
Cloud & exfilCloudAppEvents
| where TimeGenerated > ago(24h)
| where ActionType == "FileDownloaded"
| summarize Downloads = count(), Files = dcount(tostring(RawEventData.ObjectId))
by Account = tostring(RawEventData.UserId), bin(TimeGenerated, 1h)
| where Downloads >= 200
| sort by Downloads desc
Intune config change out of hours
IntuneIntuneAuditLogs
| where TimeGenerated > ago(7d)
| extend Hour = datetime_part("hour", TimeGenerated)
| where Hour >= 20 or Hour < 7
| where OperationName has_any ("Create","Delete","Patch")
| project TimeGenerated, OperationName, Identity, Result = tostring(ResultType)
| sort by TimeGenerated desc
Devices falling out of compliance
IntuneIntuneDeviceComplianceOrg | where TimeGenerated > ago(1d) | where ComplianceState != "Compliant" | summarize Devices = dcount(DeviceId) by ComplianceState, OS | sort by Devices desc
No queries match that filter yet.