Analytics rules are the core detection engine in Microsoft Sentinel, continuously monitoring your security data for threats and suspicious activities. This guide covers creating custom scheduled analytics rules using Kusto Query Language (KQL) to detect threats specific to your environment.
Prerequisites
Before creating analytics rules, ensure you have:
- Microsoft Sentinel workspace with data connectors configured
- Microsoft Sentinel Contributor role or higher
- Basic KQL knowledge for writing detection queries
- Understanding of your data sources and their table schemas
- Threat scenarios you want to detect defined
Understanding Analytics Rule Types
Microsoft Sentinel supports several analytics rule types:
| Rule Type | Use Case | Latency |
|---|---|---|
| Scheduled | Custom KQL-based detections | 5 min - 14 days |
| NRT (Near Real-Time) | Critical immediate threats | ~1 minute |
| Microsoft Security | Alerts from Microsoft Defender products | Real-time |
| Fusion | ML-based multi-stage attack detection | Automatic |
| Anomaly | Built-in behavioral anomaly detection | Automatic |
This guide focuses on Scheduled rules, the most flexible and commonly customized type.
Step 1: Design Your Detection Query
Before creating a rule in the UI, design and test your KQL query in the Logs blade.
Basic Query Structure
// 1. Specify the data table
SigninLogs
// 2. Filter the time range (use parameter in actual rule)
| where TimeGenerated > ago(1h)
// 3. Apply detection logic
| where ResultType != 0 // Failed sign-ins
| where AppDisplayName != "Windows Sign In"
// 4. Aggregate or summarize
| summarize FailedAttempts = count() by UserPrincipalName, IPAddress
// 5. Apply threshold
| where FailedAttempts > 10
// 6. Project relevant fields for the alert
| project UserPrincipalName, IPAddress, FailedAttempts
Common Detection Patterns
Brute Force Detection:
SigninLogs
| where TimeGenerated > ago(1h)
| where ResultType == 50126 // Invalid username or password
| summarize
FailedAttempts = count(),
DistinctUsers = dcount(UserPrincipalName)
by IPAddress, bin(TimeGenerated, 10m)
| where FailedAttempts > 20
| project TimeGenerated, IPAddress, FailedAttempts, DistinctUsers
Suspicious Process Execution:
SecurityEvent
| where TimeGenerated > ago(1h)
| where EventID == 4688 // Process creation
| where NewProcessName has_any ("powershell.exe", "cmd.exe", "wscript.exe")
| where CommandLine has_any ("-enc", "-encodedcommand", "downloadstring", "invoke-expression")
| project TimeGenerated, Computer, Account, NewProcessName, CommandLine
Anomalous Sign-in Location:
SigninLogs
| where TimeGenerated > ago(24h)
| where ResultType == 0 // Successful sign-ins
| summarize
Countries = make_set(LocationDetails.countryOrRegion),
CountryCount = dcount(LocationDetails.countryOrRegion)
by UserPrincipalName
| where CountryCount > 2
| project UserPrincipalName, Countries, CountryCount
Test Your Query
- Go to Logs in Microsoft Sentinel
- Paste your query and click Run
- Verify results match expected detections
- Adjust thresholds and filters as needed
- Note the query run time - optimize if over 30 seconds
Step 2: Create a Scheduled Analytics Rule
Access the Analytics Rules Wizard
- In Microsoft Sentinel, go to Configuration > Analytics
- Click Create > Scheduled query rule
Configure General Settings
Step 1: General
| Field | Recommendation |
|---|---|
| Name | Clear, descriptive name (e.g., "Brute Force Attack - Multiple Failed Sign-ins") |
| Description | Detailed explanation of what the rule detects and why |
| Severity | Match to threat level: Low, Medium, High, Informational |
| MITRE ATT&CK | Map to relevant tactics and techniques |
| Status | Set to Enabled when ready for production |
Example configuration:
- Name: Brute Force Attack - Failed Sign-ins from Single IP
- Description: Detects when a single IP address has more than 20 failed sign-in attempts within 10 minutes, indicating a potential brute force or password spraying attack.
- Severity: Medium
- Tactics: Credential Access
- Techniques: T1110 - Brute Force
Set the Rule Query
Step 2: Set rule logic
- Paste your tested KQL query in the Rule query field:
SigninLogs
| where ResultType == 50126
| summarize
FailedAttempts = count(),
TargetedUsers = make_set(UserPrincipalName),
UserCount = dcount(UserPrincipalName)
by IPAddress, bin(TimeGenerated, 10m)
| where FailedAttempts > 20
| extend AlertTime = TimeGenerated
| project AlertTime, IPAddress, FailedAttempts, UserCount, TargetedUsers
- Configure Alert enrichment options if needed
Configure Entity Mapping
Entity mapping links query results to Sentinel entities, enabling investigation features:
- Click Add new entity
- Map your query columns to entity types:
| Entity Type | Identifier | Query Column |
|---|---|---|
| IP | Address | IPAddress |
| Account | FullName | UserPrincipalName |
| Host | HostName | Computer |
Example mappings for the brute force rule:
- IP Entity: Address = IPAddress
- Account Entity: FullName = TargetedUsers (if single user)
Set Query Scheduling
Configure how often the rule runs and the data lookback period:
| Setting | Description | Recommendation |
|---|---|---|
| Query frequency | How often the rule runs | 1 hour for most detections |
| Lookup data from | Time window to search | Should be >= frequency |
| Start running | When to begin | Automatically |
Important: The lookup period should be at least as long as your query frequency to avoid gaps. For a 1-hour frequency, use at least 1-hour lookback.
Configure Alert Threshold
Define when alerts should trigger:
- Generate alert when number of query results: Is greater than 0
- Adjust threshold based on your detection logic
For rules that use summarize, the threshold applies to the number of result rows, not the aggregated counts.
Set Event Grouping
Control how alerts are grouped:
| Option | Behavior |
|---|---|
| Group all events into a single alert | One alert with all matching events |
| Trigger an alert for each event | Separate alert per result row |
For most detections, grouping all events provides cleaner incident management.
Step 3: Configure Incident Settings
Step 3: Incident settings
Enable Incident Creation
- Toggle Create incidents from alerts to Enabled
- Configure Alert grouping:
- Group related alerts into incidents: Enabled
- Grouping method: By entities or by time period
- Re-open closed matching incidents: Configure based on workflow
Alert Grouping Options
| Method | Use Case |
|---|---|
| By entities | Group alerts affecting same user, IP, or host |
| By alert name | Group all alerts from the same rule |
| By custom field | Group by specific query output column |
Step 4: Configure Automated Response
Step 4: Automated response
Link automation rules or playbooks to respond automatically:
- Under Automation rules, click Add new
- Configure the automation rule:
- Trigger: When alert is created
- Actions: Run playbook, change severity, assign owner, add tags
- Alternatively, attach existing playbooks directly
Example automation actions:
- Automatically assign incidents to the SOC team
- Add "Brute-Force" tag for categorization
- Run enrichment playbook to gather threat intelligence
Step 5: Review and Create
Step 5: Review and create
- Review all configuration settings
- Click Create to save the rule
- The rule will begin running at the next scheduled interval
Step 6: Monitor Rule Performance
View Rule Execution History
- Go to Analytics > Rule templates tab
- Select your custom rule
- Click View rule details
- Review the Last run and Status columns
Check for Errors
Common rule issues:
- Query timeout: Optimize KQL query performance
- No results: Verify data exists in the specified tables
- Too many results: Adjust thresholds or add filters
Review Generated Incidents
- Go to Threat management > Incidents
- Filter by your rule name
- Verify alerts are being grouped correctly
- Check entity mapping is working as expected
Example: Complete Suspicious PowerShell Rule
Here's a complete example of a production-ready analytics rule:
General Settings:
- Name: Suspicious PowerShell Command Execution
- Severity: High
- MITRE: Execution (T1059.001)
Rule Query:
let SuspiciousCommands = dynamic([
"downloadstring", "downloadfile", "invoke-expression",
"iex", "-encodedcommand", "-enc", "bypass",
"hidden", "noprofile", "unrestricted"
]);
SecurityEvent
| where EventID == 4688
| where NewProcessName endswith "\\powershell.exe" or
NewProcessName endswith "\\pwsh.exe"
| where CommandLine has_any (SuspiciousCommands)
| project
TimeGenerated,
Computer,
Account,
CommandLine,
ParentProcessName,
NewProcessName
| extend
HostName = tostring(split(Computer, ".")[0]),
DomainName = tostring(split(Account, "\\")[0]),
UserName = tostring(split(Account, "\\")[1])
Entity Mapping:
- Host: HostName, DomainName
- Account: Name = UserName, Domain = DomainName
Scheduling:
- Frequency: 5 minutes
- Lookback: 10 minutes
Best Practices for Analytics Rules
| Practice | Benefit |
|---|---|
| Test queries thoroughly before enabling | Prevents false positives and missed detections |
| Use specific filters over broad patterns | Reduces noise and improves accuracy |
| Map entities for every rule | Enables investigation features and correlation |
| Start with built-in templates | Leverage Microsoft's security research |
| Document rule logic in descriptions | Aids future tuning and handoffs |
| Review rule performance weekly | Catch issues before they impact security |
| Version control your queries | Track changes and enable rollback |
Next Steps
After creating your analytics rules:
- Fine-tune rules based on initial alert quality
- Create automation rules for incident handling
- Build workbooks to visualize detection metrics
- Set up playbooks for automated response
- Establish a rule review cadence for continuous improvement
Additional Resources
- KQL Quick Reference
- Microsoft Sentinel Analytics Rules
- MITRE ATT&CK Framework
- Sentinel GitHub - Rule Templates
Need help building effective detection rules? Inventive HQ specializes in SIEM engineering and can help you develop custom analytics rules tailored to your threat landscape. Contact us for a free consultation.