3 min read
Azure Sentinel: Cloud-Native SIEM
Azure Sentinel is Microsoft’s cloud-native SIEM. Collect security data at cloud scale, detect threats with AI, and respond with automation.
Architecture
Data Sources → Log Analytics → Sentinel → Alerts/Incidents
│
├── Analytics Rules
├── Workbooks
├── Hunting Queries
└── Playbooks (Logic Apps)
Enabling Sentinel
# Create Log Analytics workspace
az monitor log-analytics workspace create \
--resource-group myRG \
--workspace-name sentinel-workspace \
--location eastus
# Enable Sentinel
az sentinel workspace create \
--resource-group myRG \
--workspace-name sentinel-workspace
Data Connectors
Azure AD
az sentinel data-connector create \
--resource-group myRG \
--workspace-name sentinel-workspace \
--name AzureActiveDirectory \
--data-types SignInLogs AuditLogs
Microsoft 365
{
"kind": "Office365",
"properties": {
"tenantId": "{tenant-id}",
"dataTypes": {
"exchange": {"state": "Enabled"},
"sharePoint": {"state": "Enabled"},
"teams": {"state": "Enabled"}
}
}
}
Syslog (Linux)
# Install Log Analytics agent
wget https://raw.githubusercontent.com/Microsoft/OMS-Agent-for-Linux/master/installer/scripts/onboard_agent.sh
sh onboard_agent.sh -w <workspace-id> -s <key> -d opinsights.azure.com
Analytics Rules
Scheduled Query Rule
{
"kind": "Scheduled",
"displayName": "Multiple Failed Logins",
"description": "Detects multiple failed login attempts",
"severity": "Medium",
"enabled": true,
"query": "SigninLogs | where ResultType != 0 | summarize FailedAttempts = count() by UserPrincipalName, bin(TimeGenerated, 1h) | where FailedAttempts > 10",
"queryFrequency": "PT1H",
"queryPeriod": "PT1H",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0,
"tactics": ["CredentialAccess"],
"incidentConfiguration": {
"createIncident": true,
"groupingConfiguration": {
"enabled": true,
"lookbackDuration": "PT5H",
"matchingMethod": "AllEntities"
}
}
}
KQL Detection Queries
// Brute force detection
SigninLogs
| where TimeGenerated > ago(1h)
| where ResultType == 50126 // Invalid username or password
| summarize FailedAttempts = count(), TargetAccounts = dcount(UserPrincipalName)
by IPAddress, bin(TimeGenerated, 5m)
| where FailedAttempts > 20 or TargetAccounts > 5
| project TimeGenerated, IPAddress, FailedAttempts, TargetAccounts
// Impossible travel
let threshold = 500; // km/hr
SigninLogs
| where TimeGenerated > ago(1d)
| project UserPrincipalName, Location, Latitude = todouble(LocationDetails.geoCoordinates.latitude),
Longitude = todouble(LocationDetails.geoCoordinates.longitude), TimeGenerated
| order by UserPrincipalName, TimeGenerated
| serialize
| extend PrevLocation = prev(Location), PrevLat = prev(Latitude), PrevLon = prev(Longitude),
PrevTime = prev(TimeGenerated), PrevUser = prev(UserPrincipalName)
| where UserPrincipalName == PrevUser
| extend Distance = geo_distance_2points(Longitude, Latitude, PrevLon, PrevLat) / 1000
| extend TimeDiff = datetime_diff('hour', TimeGenerated, PrevTime)
| extend Speed = Distance / TimeDiff
| where Speed > threshold
Playbooks (Automated Response)
{
"definition": {
"triggers": {
"Microsoft_Sentinel_incident": {
"type": "ApiConnectionWebhook",
"inputs": {
"host": {
"connection": { "name": "@parameters('$connections')['azuresentinel']['connectionId']" }
},
"body": { "callback_url": "@{listCallbackUrl()}" },
"path": "/incident-creation"
}
}
},
"actions": {
"Block_IP_in_Firewall": {
"type": "ApiConnection",
"inputs": { }
},
"Send_Teams_Alert": {
"type": "ApiConnection",
"inputs": { }
},
"Add_comment_to_incident": {
"type": "ApiConnection",
"inputs": { }
}
}
}
}
Hunting Queries
// Hunt for suspicious PowerShell
SecurityEvent
| where EventID == 4688
| where Process has "powershell.exe"
| where CommandLine has_any ("-enc", "-e ", "bypass", "hidden", "downloadstring")
| project TimeGenerated, Computer, Account, CommandLine
Sentinel: AI-powered security operations at cloud scale.