Introduction
Microsoft Sentinel is a cloud-native Security Information and Event Management (SIEM) and Security Orchestration, Automation and Response (SOAR) solution that provides intelligent security analytics across your enterprise. This project implements a production-grade Sentinel deployment with comprehensive data collection, threat detection, investigation workbooks, and automated response playbooks.
What You'll Build
- Centralized SIEM: Single pane of glass for security monitoring
- Data Connectors: Integration with Microsoft 365, Azure, and third-party sources
- Analytics Rules: Detection of threats aligned with MITRE ATT&CK framework
- Custom Workbooks: Security dashboards for visibility and reporting
- SOAR Playbooks: Automated incident response with Logic Apps
- Threat Intelligence: TI platform integration for enrichment
Who This Is For
- Security engineers implementing enterprise SIEM
- SOC analysts building detection and response capabilities
- Cloud architects designing security monitoring
- MSPs providing managed security services
Prerequisites
Knowledge Requirements
- Azure administration fundamentals
- Security operations concepts
- KQL (Kusto Query Language) basics
- Understanding of MITRE ATT&CK framework
Azure Requirements
- Azure subscription with Owner or Contributor access
- Log Analytics workspace permissions
- Microsoft Defender for Cloud (recommended)
- Microsoft 365 E5 or Security add-on (for M365 integration)
Planning Requirements
- Data retention requirements defined
- Compliance requirements identified
- Alert routing and escalation procedures
- Integration requirements documented
Architecture Overview
┌─────────────────────────────────────────────────────────────────────────────┐
│ MICROSOFT SENTINEL ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ DATA SOURCES │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │
│ │ │Microsoft │ │ Azure │ │ On-Prem │ │ Third-Party │ │ │
│ │ │ 365 │ │ Services │ │ Systems │ │ Sources │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ • Entra │ │ • Azure │ │ • AD DS │ │ • Palo Alto │ │ │
│ │ │ • Defender│ │ AD │ │ • DNS │ │ • Fortinet │ │ │
│ │ │ • Office │ │ • Key │ │ • DHCP │ │ • CrowdStrike │ │ │
│ │ │ • Teams │ │ Vault │ │ • Syslog │ │ • AWS/GCP │ │ │
│ │ │ │ │ • NSG │ │ • CEF │ │ • Custom APIs │ │ │
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────────┬─────────┘ │ │
│ └────────┼─────────────┼─────────────┼─────────────────┼─────────────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ DATA CONNECTORS │ │
│ │ • Native Connectors (API-based) │ │
│ │ • Azure Monitor Agent (AMA) │ │
│ │ • Syslog/CEF Collectors │ │
│ │ • REST API Ingestion │ │
│ └────────────────────────────┬────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ LOG ANALYTICS WORKSPACE │ │
│ │ ┌────────────────────────────────────────────────────────────┐ │ │
│ │ │ DATA TABLES │ │ │
│ │ │ SecurityEvent │ SigninLogs │ AuditLogs │ CommonSecurityLog │ │ │
│ │ │ AzureActivity │ OfficeActivity │ ThreatIntelligence │ ... │ │ │
│ │ └────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Data Retention: 90 days (hot) → Archive → Purge │ │
│ └────────────────────────────┬────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ MICROSOFT SENTINEL │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Analytics │ │ Workbooks │ │ Hunting │ │ Notebooks │ │ │
│ │ │ Rules │ │ (Dashboards)│ │ Queries │ │ (Jupyter) │ │ │
│ │ └──────┬──────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ INCIDENTS │ │ │
│ │ │ Alert Correlation → Incident Creation → Investigation │ │ │
│ │ │ │ │ │ │
│ │ └──────────────────────────┼───────────────────────────────────┘ │ │
│ └──────────────────────────────┼───────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ SOAR / AUTOMATION │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │ │
│ │ │ Automation │ │ Logic │ │ Integrations │ │ │
│ │ │ Rules │ │ Apps │ │ • ServiceNow │ │ │
│ │ │ │ │ (Playbooks) │ │ • Teams/Slack │ │ │
│ │ │ Auto-assign │ │ │ │ • Email │ │ │
│ │ │ Auto-enrich │ │ Block IP │ │ • Defender Actions │ │ │
│ │ │ Auto-close │ │ Isolate Host │ │ • Custom Webhooks │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
Part 1: Log Analytics Workspace Setup
1.1 Create Log Analytics Workspace
Deploy the workspace using Azure CLI:
# Set variables
RESOURCE_GROUP="rg-sentinel-prod"
LOCATION="eastus"
WORKSPACE_NAME="law-sentinel-prod"
# Create resource group
az group create \
--name $RESOURCE_GROUP \
--location $LOCATION
# Create Log Analytics workspace
az monitor log-analytics workspace create \
--resource-group $RESOURCE_GROUP \
--workspace-name $WORKSPACE_NAME \
--location $LOCATION \
--retention-time 90 \
--sku PerGB2018
# Get workspace ID
WORKSPACE_ID=$(az monitor log-analytics workspace show \
--resource-group $RESOURCE_GROUP \
--workspace-name $WORKSPACE_NAME \
--query id -o tsv)1.2 Configure Workspace Settings
# Enable workspace features
az monitor log-analytics workspace update \
--resource-group $RESOURCE_GROUP \
--workspace-name $WORKSPACE_NAME \
--retention-time 90 \
--daily-quota-gb 10
# Configure data collection endpoints
az monitor data-collection endpoint create \
--name "dce-sentinel-prod" \
--resource-group $RESOURCE_GROUP \
--location $LOCATION \
--public-network-access Enabled1.3 Terraform Deployment (Alternative)
# providers.tf
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
}
provider "azurerm" {
features {}
}
# main.tf
resource "azurerm_resource_group" "sentinel" {
name = "rg-sentinel-prod"
location = "eastus"
}
resource "azurerm_log_analytics_workspace" "sentinel" {
name = "law-sentinel-prod"
location = azurerm_resource_group.sentinel.location
resource_group_name = azurerm_resource_group.sentinel.name
sku = "PerGB2018"
retention_in_days = 90
daily_quota_gb = 10
tags = {
environment = "production"
purpose = "sentinel-siem"
}
}
resource "azurerm_sentinel_log_analytics_workspace_onboarding" "sentinel" {
workspace_id = azurerm_log_analytics_workspace.sentinel.id
customer_managed_key_enabled = false
}Part 2: Enable Microsoft Sentinel
2.1 Onboard Sentinel
# Enable Sentinel on the workspace
az sentinel onboard create \
--resource-group $RESOURCE_GROUP \
--workspace-name $WORKSPACE_NAME
# Verify Sentinel is enabled
az sentinel show \
--resource-group $RESOURCE_GROUP \
--workspace-name $WORKSPACE_NAME2.2 Configure Sentinel Settings
Navigate to Microsoft Sentinel → Settings → Settings:
{
"entityAnalytics": {
"enabled": true,
"ueba": {
"enabled": true,
"dataSources": ["AuditLogs", "SigninLogs", "SecurityEvents"]
}
},
"anomalies": {
"enabled": true
},
"playbooks": {
"tenantId": "<your-tenant-id>",
"subscriptionId": "<your-subscription-id>"
}
}Part 3: Data Connectors
3.1 Microsoft 365 Defender Connector
# Enable Microsoft 365 Defender connector
az sentinel data-connector create \
--resource-group $RESOURCE_GROUP \
--workspace-name $WORKSPACE_NAME \
--data-connector-id "MicrosoftThreatProtection" \
--kind "MicrosoftThreatProtection" \
--tenant-id "<tenant-id>" \
--data-types-to-connect "[Alerts, Incidents, DeviceEvents]"3.2 Azure Active Directory / Entra ID
# Enable Azure AD connector
az sentinel data-connector create \
--resource-group $RESOURCE_GROUP \
--workspace-name $WORKSPACE_NAME \
--data-connector-id "AzureActiveDirectory" \
--kind "AzureActiveDirectory" \
--tenant-id "<tenant-id>" \
--data-types "[SigninLogs, AuditLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs, AADManagedIdentitySignInLogs, AADProvisioningLogs]"3.3 Azure Activity Logs
# Enable Azure Activity connector at subscription level
az monitor diagnostic-settings create \
--name "sentinel-activity-logs" \
--subscription "<subscription-id>" \
--logs '[{"category": "Administrative", "enabled": true}, {"category": "Security", "enabled": true}, {"category": "Alert", "enabled": true}, {"category": "Policy", "enabled": true}]' \
--workspace $WORKSPACE_ID3.4 Windows Security Events
Deploy Azure Monitor Agent and Data Collection Rule:
# Create Data Collection Rule for Windows Security Events
az monitor data-collection rule create \
--name "dcr-windows-security" \
--resource-group $RESOURCE_GROUP \
--location $LOCATION \
--data-flows '[{
"streams": ["Microsoft-SecurityEvent"],
"destinations": ["law-sentinel-prod"]
}]' \
--data-sources '{
"windowsEventLogs": [{
"name": "SecurityEvents",
"streams": ["Microsoft-SecurityEvent"],
"xPathQueries": ["Security!*[System[(EventID=4624 or EventID=4625 or EventID=4648 or EventID=4672 or EventID=4688 or EventID=4698 or EventID=4720 or EventID=4728 or EventID=4732)]]"]
}]
}' \
--destinations '{
"logAnalytics": [{
"workspaceResourceId": "'$WORKSPACE_ID'",
"name": "law-sentinel-prod"
}]
}'3.5 Syslog/CEF Connector
Configure Linux syslog collector:
# Install Azure Monitor Agent on Linux
wget https://github.com/microsoft/OMS-Agent-for-Linux/releases/download/OMSAgent_v1.14.23-0/omsagent-1.14.23-0.universal.x64.sh
sudo sh omsagent-1.14.23-0.universal.x64.sh --upgrade -w <workspace-id> -s <workspace-key>
# Configure CEF collection
sudo wget -O cef_installer.py https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/DataConnectors/CEF/cef_installer.py
sudo python3 cef_installer.py <workspace-id> <workspace-key>3.6 Third-Party Connectors
FortiGate Syslog Configuration:
config log syslogd setting
set status enable
set server "10.1.0.50"
set port 514
set format cef
set facility local7
end
config log syslogd filter
set severity information
set forward-traffic enable
set local-traffic enable
set multicast-traffic enable
set sniffer-traffic enable
set anomaly enable
set voip enable
end
Part 4: Analytics Rules
4.1 Scheduled Analytics Rule - Brute Force Detection
{
"displayName": "Brute Force Attack Against Azure AD",
"description": "Identifies multiple failed sign-in attempts followed by a successful sign-in from the same IP address.",
"severity": "Medium",
"enabled": true,
"query": "let failureThreshold = 10;\nlet timeRange = 1h;\nlet successThreshold = 1;\nSigninLogs\n| where TimeGenerated >= ago(timeRange)\n| where ResultType != 0\n| summarize FailureCount = count(), FailedAccounts = make_set(UserPrincipalName) by IPAddress, bin(TimeGenerated, 5m)\n| where FailureCount >= failureThreshold\n| join kind=inner (\n SigninLogs\n | where TimeGenerated >= ago(timeRange)\n | where ResultType == 0\n | project SuccessTime = TimeGenerated, IPAddress, UserPrincipalName, AppDisplayName\n) on IPAddress\n| where SuccessTime > TimeGenerated\n| project IPAddress, FailureCount, FailedAccounts, SuccessfulUser = UserPrincipalName, SuccessTime, AppDisplayName",
"queryFrequency": "PT5M",
"queryPeriod": "PT1H",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0,
"suppressionDuration": "PT1H",
"suppressionEnabled": true,
"tactics": ["CredentialAccess", "InitialAccess"],
"techniques": ["T1110"],
"entityMappings": [
{
"entityType": "Account",
"fieldMappings": [
{"identifier": "FullName", "columnName": "SuccessfulUser"}
]
},
{
"entityType": "IP",
"fieldMappings": [
{"identifier": "Address", "columnName": "IPAddress"}
]
}
]
}4.2 PowerShell Script for Rule Deployment
# Connect to Azure
Connect-AzAccount
# Variables
$resourceGroup = "rg-sentinel-prod"
$workspaceName = "law-sentinel-prod"
# Create Analytics Rule - Suspicious PowerShell Commands
$ruleName = "Suspicious-PowerShell-Commands"
$ruleQuery = @"
SecurityEvent
| where EventID == 4688
| where Process has_any ("powershell.exe", "pwsh.exe")
| where CommandLine has_any (
"-enc", "-encodedcommand", "-e ",
"bypass", "-ep bypass",
"downloadstring", "downloadfile",
"invoke-webrequest", "iwr",
"invoke-expression", "iex",
"frombase64string",
"hidden", "-w hidden"
)
| extend AccountName = tostring(split(SubjectUserName, '@')[0])
| project TimeGenerated, Computer, AccountName, CommandLine, ParentProcessName
"@
New-AzSentinelAlertRule `
-ResourceGroupName $resourceGroup `
-WorkspaceName $workspaceName `
-Kind Scheduled `
-DisplayName "Suspicious PowerShell Command Execution" `
-Description "Detects suspicious PowerShell command patterns often used in attacks" `
-Severity High `
-Query $ruleQuery `
-QueryFrequency (New-TimeSpan -Minutes 5) `
-QueryPeriod (New-TimeSpan -Hours 1) `
-TriggerOperator GreaterThan `
-TriggerThreshold 0 `
-Enabled
# Create Analytics Rule - Privilege Escalation
$privEscQuery = @"
SecurityEvent
| where EventID == 4672
| where SubjectUserName !endswith "$"
| where SubjectUserName !in ("SYSTEM", "LOCAL SERVICE", "NETWORK SERVICE")
| summarize PrivilegeCount = count(), Privileges = make_set(PrivilegeList) by SubjectUserName, Computer, bin(TimeGenerated, 1h)
| where PrivilegeCount > 5
"@
New-AzSentinelAlertRule `
-ResourceGroupName $resourceGroup `
-WorkspaceName $workspaceName `
-Kind Scheduled `
-DisplayName "Unusual Privilege Elevation Activity" `
-Description "Detects accounts receiving special privileges at an unusual rate" `
-Severity Medium `
-Query $privEscQuery `
-QueryFrequency (New-TimeSpan -Minutes 15) `
-QueryPeriod (New-TimeSpan -Hours 2) `
-TriggerOperator GreaterThan `
-TriggerThreshold 0 `
-Enabled4.3 MITRE ATT&CK Aligned Rules
| Tactic | Technique | Rule Name | Detection Logic |
|---|---|---|---|
| Initial Access | T1078 | Impossible Travel | Sign-ins from geographically distant locations |
| Execution | T1059 | Suspicious Scripts | PowerShell/CMD with encoded commands |
| Persistence | T1098 | Account Modification | Service principal credential added |
| Privilege Escalation | T1078.004 | Cloud Account Abuse | Role assignment to sensitive groups |
| Defense Evasion | T1562 | Security Tool Disabled | Defender/AV disabled events |
| Credential Access | T1110 | Brute Force | Multiple failed authentications |
| Discovery | T1087 | Account Enumeration | Graph API bulk queries |
| Lateral Movement | T1021 | RDP/SSH from Internet | Remote access from external IPs |
| Collection | T1530 | Storage Access | Blob storage enumeration |
| Exfiltration | T1567 | Cloud Storage Upload | Large data uploads to external clouds |
Part 5: Custom Workbooks
5.1 Security Overview Workbook
{
"version": "Notebook/1.0",
"items": [
{
"type": 1,
"content": {
"json": "# Security Operations Overview\n---\nReal-time security metrics and incident tracking"
}
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "SecurityIncident\n| where TimeGenerated >= ago(7d)\n| summarize Incidents = count() by Severity\n| order by Severity asc",
"size": 1,
"title": "Incidents by Severity (Last 7 Days)",
"queryType": 0,
"visualization": "piechart"
}
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "SecurityIncident\n| where TimeGenerated >= ago(30d)\n| summarize Incidents = count() by bin(TimeGenerated, 1d), Severity\n| render timechart",
"size": 0,
"title": "Incident Trend (Last 30 Days)",
"queryType": 0,
"visualization": "timechart"
}
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "SecurityIncident\n| where TimeGenerated >= ago(24h)\n| where Status == 'New'\n| project Title, Severity, CreatedTime = TimeGenerated, Owner = OwnerName\n| order by Severity asc, CreatedTime desc\n| take 20",
"size": 0,
"title": "Open Incidents (Last 24 Hours)",
"queryType": 0,
"visualization": "table"
}
}
],
"fallbackResourceIds": [],
"fromTemplateId": "sentinel-SecurityOverview"
}5.2 Sign-in Analysis Workbook
// Failed Sign-ins by Location
SigninLogs
| where TimeGenerated >= ago(7d)
| where ResultType != 0
| summarize FailedAttempts = count() by Location
| top 10 by FailedAttempts desc
| render barchart
// Sign-in Risk Analysis
SigninLogs
| where TimeGenerated >= ago(24h)
| summarize
TotalSignIns = count(),
RiskySignIns = countif(RiskLevelDuringSignIn in ("medium", "high")),
MFARequired = countif(AuthenticationRequirement == "multiFactorAuthentication"),
LegacyAuth = countif(ClientAppUsed !in ("Browser", "Mobile Apps and Desktop clients"))
| extend RiskPercentage = round(100.0 * RiskySignIns / TotalSignIns, 2)
// Conditional Access Policy Impact
SigninLogs
| where TimeGenerated >= ago(7d)
| mv-expand ConditionalAccessPolicies
| extend PolicyName = tostring(ConditionalAccessPolicies.displayName)
| extend PolicyResult = tostring(ConditionalAccessPolicies.result)
| summarize count() by PolicyName, PolicyResult
| render columnchartPart 6: Threat Hunting
6.1 Hunting Queries
// Hunt: Suspicious Service Principal Activity
let timeRange = 7d;
AuditLogs
| where TimeGenerated >= ago(timeRange)
| where OperationName has_any ("Add service principal", "Add application", "Update application")
| where Result == "success"
| extend InitiatedBy = tostring(InitiatedBy.user.userPrincipalName)
| extend AppName = tostring(TargetResources[0].displayName)
| extend AppId = tostring(TargetResources[0].id)
| project TimeGenerated, InitiatedBy, OperationName, AppName, AppId
| order by TimeGenerated desc
// Hunt: Lateral Movement via RDP
let timeRange = 24h;
SecurityEvent
| where TimeGenerated >= ago(timeRange)
| where EventID == 4624
| where LogonType == 10
| where IpAddress !startswith "10." and IpAddress !startswith "192.168." and IpAddress !startswith "172."
| summarize RDPSessions = count(), Computers = make_set(Computer) by IpAddress, Account
| where RDPSessions > 3
| order by RDPSessions desc
// Hunt: Data Exfiltration Indicators
let timeRange = 7d;
let threshold = 1000000000; // 1 GB
AzureDiagnostics
| where TimeGenerated >= ago(timeRange)
| where ResourceType == "STORAGEACCOUNTS"
| where OperationName == "GetBlob"
| summarize TotalBytes = sum(requestBodySize_d) by CallerIpAddress, AccountName = Resource
| where TotalBytes > threshold
| extend TotalGB = round(TotalBytes / 1000000000.0, 2)
| order by TotalGB desc
// Hunt: Persistence via Scheduled Tasks
SecurityEvent
| where TimeGenerated >= ago(7d)
| where EventID == 4698
| parse EventData with * '<Data Name="TaskName">' TaskName '</Data>' *
| parse EventData with * '<Data Name="SubjectUserName">' SubjectUserName '</Data>' *
| where SubjectUserName !endswith "$"
| project TimeGenerated, Computer, SubjectUserName, TaskName
| order by TimeGenerated desc6.2 Bookmarked Evidence
// Create bookmark from hunting results
// In Sentinel UI: Hunting → Queries → Run Query → Add Bookmark
// Example: Bookmark suspicious sign-in
SigninLogs
| where TimeGenerated >= ago(1h)
| where RiskLevelDuringSignIn == "high"
| where ResultType == 0
| project
TimeGenerated,
UserPrincipalName,
IPAddress,
Location,
AppDisplayName,
RiskDetail,
ConditionalAccessStatusPart 7: SOAR Playbooks
7.1 Playbook: Block IP in Azure Firewall
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Get_Incident": {
"inputs": {
"body": {
"incidentArmId": "@triggerBody()?['object']?['id']"
},
"host": {
"connection": {
"name": "@parameters('$connections')['azuresentinel']['connectionId']"
}
},
"method": "post",
"path": "/Incidents"
},
"type": "ApiConnection"
},
"For_Each_Entity": {
"actions": {
"Condition_Is_IP": {
"actions": {
"Add_IP_to_Blocklist": {
"inputs": {
"body": {
"properties": {
"action": {
"type": "Deny"
},
"destinationAddresses": ["*"],
"destinationPorts": ["*"],
"protocols": ["Any"],
"sourceAddresses": ["@{items('For_Each_Entity')?['properties']?['address']}"]
}
},
"method": "PUT",
"uri": "https://management.azure.com/subscriptions/@{variables('SubscriptionId')}/resourceGroups/@{variables('ResourceGroup')}/providers/Microsoft.Network/azureFirewalls/@{variables('FirewallName')}/networkRuleCollections/BlockedIPs/rules/@{guid()}?api-version=2021-05-01"
},
"type": "Http"
}
},
"expression": {
"and": [
{
"equals": ["@items('For_Each_Entity')?['kind']", "Ip"]
}
]
},
"type": "If"
}
},
"foreach": "@body('Get_Incident')?['properties']?['relatedEntities']",
"type": "Foreach"
},
"Add_Comment_to_Incident": {
"inputs": {
"body": {
"incidentArmId": "@triggerBody()?['object']?['id']",
"message": "Automated Response: Malicious IP addresses have been blocked in Azure Firewall."
},
"host": {
"connection": {
"name": "@parameters('$connections')['azuresentinel']['connectionId']"
}
},
"method": "post",
"path": "/Incidents/Comment"
},
"type": "ApiConnection"
}
},
"triggers": {
"Microsoft_Sentinel_incident": {
"inputs": {
"body": {
"callback_url": "@{listCallbackUrl()}"
},
"host": {
"connection": {
"name": "@parameters('$connections')['azuresentinel']['connectionId']"
}
},
"path": "/incident-creation"
},
"type": "ApiConnectionWebhook"
}
}
}
}7.2 Playbook: Isolate Compromised Device
# PowerShell function for Logic App
# Isolates device using Microsoft Defender for Endpoint API
param(
[string]$DeviceId,
[string]$IncidentId,
[string]$Comment
)
# Get access token
$tenantId = $env:TENANT_ID
$clientId = $env:CLIENT_ID
$clientSecret = $env:CLIENT_SECRET
$body = @{
grant_type = "client_credentials"
client_id = $clientId
client_secret = $clientSecret
scope = "https://api.securitycenter.microsoft.com/.default"
}
$tokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" -Method Post -Body $body
$accessToken = $tokenResponse.access_token
# Isolate the device
$isolateBody = @{
Comment = "Automated isolation due to incident $IncidentId. $Comment"
IsolationType = "Full"
} | ConvertTo-Json
$headers = @{
"Authorization" = "Bearer $accessToken"
"Content-Type" = "application/json"
}
$response = Invoke-RestMethod `
-Uri "https://api.securitycenter.microsoft.com/api/machines/$DeviceId/isolate" `
-Method Post `
-Headers $headers `
-Body $isolateBody
return $response7.3 Playbook: Enrich IOCs with Threat Intelligence
{
"definition": {
"actions": {
"For_Each_IP": {
"actions": {
"Query_VirusTotal": {
"inputs": {
"headers": {
"x-apikey": "@parameters('VirusTotalApiKey')"
},
"method": "GET",
"uri": "https://www.virustotal.com/api/v3/ip_addresses/@{items('For_Each_IP')}"
},
"type": "Http"
},
"Query_AbuseIPDB": {
"inputs": {
"headers": {
"Key": "@parameters('AbuseIPDBApiKey')"
},
"method": "GET",
"uri": "https://api.abuseipdb.com/api/v2/check?ipAddress=@{items('For_Each_IP')}&maxAgeInDays=90"
},
"type": "Http"
},
"Update_Incident_with_TI": {
"inputs": {
"body": {
"incidentArmId": "@triggerBody()?['object']?['id']",
"message": "Threat Intelligence Enrichment:\n\nVirusTotal: @{body('Query_VirusTotal')?['data']?['attributes']?['reputation']}\nAbuseIPDB Score: @{body('Query_AbuseIPDB')?['data']?['abuseConfidenceScore']}"
}
},
"type": "ApiConnection"
}
},
"foreach": "@variables('IPAddresses')",
"type": "Foreach"
}
}
}
}Part 8: Automation Rules
8.1 Auto-Assignment Rules
{
"displayName": "Auto-Assign High Severity Incidents",
"order": 1,
"triggeringLogic": {
"isEnabled": true,
"triggersOn": "Incidents",
"triggersWhen": "Created",
"conditions": [
{
"conditionType": "Property",
"conditionProperties": {
"propertyName": "IncidentSeverity",
"operator": "Equals",
"propertyValues": ["High"]
}
}
]
},
"actions": [
{
"actionType": "ModifyProperties",
"actionConfiguration": {
"owner": {
"objectId": "<senior-analyst-guid>",
"email": "senior.analyst@yourdomain.com",
"userPrincipalName": "senior.analyst@yourdomain.com"
},
"status": "Active"
}
},
{
"actionType": "RunPlaybook",
"actionConfiguration": {
"playbookResourceId": "/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Logic/workflows/Send-Teams-Notification"
}
}
]
}8.2 Auto-Close Rules
{
"displayName": "Auto-Close Known False Positives",
"order": 10,
"triggeringLogic": {
"isEnabled": true,
"triggersOn": "Incidents",
"triggersWhen": "Created",
"conditions": [
{
"conditionType": "Property",
"conditionProperties": {
"propertyName": "IncidentTitle",
"operator": "Contains",
"propertyValues": ["Test-", "Scheduled-Scan-", "Maintenance-"]
}
}
]
},
"actions": [
{
"actionType": "ModifyProperties",
"actionConfiguration": {
"status": "Closed",
"classification": "BenignPositive",
"classificationReason": "ConfirmedActivity"
}
},
{
"actionType": "AddIncidentTask",
"actionConfiguration": {
"title": "Auto-closed - Review if unexpected",
"description": "This incident was auto-closed based on known patterns. Review if this closure was unexpected."
}
}
]
}Part 9: Cost Optimization
9.1 Data Collection Optimization
// Analyze data ingestion by table
Usage
| where TimeGenerated >= ago(30d)
| where IsBillable == true
| summarize TotalGB = sum(Quantity) / 1000 by DataType
| order by TotalGB desc
| take 20
// Find noisy events
SecurityEvent
| where TimeGenerated >= ago(7d)
| summarize EventCount = count() by EventID
| order by EventCount desc
| take 209.2 Basic Logs Configuration
Configure lower-cost basic logs for high-volume tables:
# Set table to Basic tier
az monitor log-analytics workspace table update \
--resource-group $RESOURCE_GROUP \
--workspace-name $WORKSPACE_NAME \
--name "ContainerLog" \
--plan Basic
# Set retention separately
az monitor log-analytics workspace table update \
--resource-group $RESOURCE_GROUP \
--workspace-name $WORKSPACE_NAME \
--name "ContainerLog" \
--retention-time 30 \
--total-retention-time 909.3 Commitment Tiers
| Daily Ingestion | Recommended Tier | Cost Savings |
|---|---|---|
| < 100 GB | Pay-as-you-go | N/A |
| 100-500 GB | 100 GB/day commitment | ~20% |
| 500-1000 GB | 500 GB/day commitment | ~30% |
| > 1000 GB | 1000 GB/day commitment | ~35% |
Part 10: Verification and Testing
10.1 Validate Data Connectors
// Check data connector health
SentinelHealth
| where TimeGenerated >= ago(1d)
| where OperationName == "Data collection"
| summarize LastHeartbeat = max(TimeGenerated), Status = arg_max(TimeGenerated, *) by SentinelResourceId
| project SentinelResourceId, LastHeartbeat, Status
// Verify data flow per connector
union withsource=TableName *
| where TimeGenerated >= ago(1h)
| summarize RecordCount = count() by TableName
| order by RecordCount desc10.2 Test Analytics Rules
// Simulate brute force for testing
// Run this query manually - should trigger alert
SigninLogs
| take 1
| extend TestIP = "192.0.2.1"
| extend TestUser = "testuser@domain.com"
| project IPAddress = TestIP, UserPrincipalName = TestUser, ResultType = "50126"10.3 Test Playbooks
# Trigger test incident for playbook validation
$incidentBody = @{
properties = @{
title = "Test Incident for Playbook Validation"
severity = "Medium"
status = "New"
description = "This is a test incident to validate SOAR playbook execution."
}
}
New-AzSentinelIncident `
-ResourceGroupName $resourceGroup `
-WorkspaceName $workspaceName `
-IncidentId (New-Guid).Guid `
@incidentBodyTroubleshooting
Common Issues and Solutions
| Issue | Symptoms | Resolution |
|---|---|---|
| Data not appearing | Empty tables after connector setup | Check connector permissions, verify agent health |
| High latency | Delayed alerts | Check query complexity, optimize KQL |
| Playbook failures | Actions not executing | Verify Logic App permissions, check API limits |
| Missing entities | Entities not extracted | Review entity mappings in analytics rules |
| Cost overruns | Unexpected charges | Implement data filtering, use Basic logs tier |
Diagnostic Queries
// Check for data gaps
let interval = 5m;
SigninLogs
| where TimeGenerated >= ago(24h)
| summarize Count = count() by bin(TimeGenerated, interval)
| where Count < 10
| order by TimeGenerated desc
// Analyze query performance
AzureDiagnostics
| where ResourceType == "MICROSOFT.OPERATIONALINSIGHTS/WORKSPACES"
| where OperationName == "Query"
| extend QueryHash = tostring(parse_json(tostring(parse_json(Properties).QueryHash)))
| summarize AvgDuration = avg(DurationMs), Count = count() by QueryHash
| order by AvgDuration desc
| take 10Security Considerations
Best Practices
- Least Privilege: Grant minimum required permissions for data connectors
- Encryption: Enable customer-managed keys for sensitive data
- Network Security: Use Private Link for workspace access
- Audit Logging: Enable diagnostic settings on Sentinel itself
- Playbook Security: Use managed identities, store secrets in Key Vault
Compliance Mapping
| Framework | Sentinel Capability |
|---|---|
| PCI-DSS | Log retention, access controls, monitoring |
| HIPAA | Audit trails, incident response |
| SOC 2 | Continuous monitoring, alerting |
| NIST CSF | Detect, Respond, Recover functions |
Next Steps
After completing this Sentinel implementation:
- Advanced Hunting: Develop custom hunting queries for your environment
- Machine Learning: Enable ML-based anomaly detection
- UEBA: Implement User and Entity Behavior Analytics
- Threat Intelligence: Integrate additional TI feeds