Connect Microsoft Defender for Identity alerts to Microsoft Sentinel, create identity-focused analytics rules, build automated playbooks that disable compromised accounts and notify the SOC, deploy identity threat workbooks, and establish an end-to-end automated response workflow for identity-based attacks.
This lab connects Microsoft Defender for Identity (MDI) with Microsoft Sentinel to create a unified identity threat detection and automated response pipeline. You will ingest MDI alerts into Sentinel, build custom analytics rules that detect credential theft, lateral movement, and privilege escalation patterns, create Logic App playbooks that automatically disable compromised accounts and send Teams notifications, and deploy Sentinel workbooks that give your SOC real-time visibility into identity threats across on-premises Active Directory and cloud identities.
A financial services company with 15,000 employees and a hybrid Active Directory environment detects an average of 40 identity-related alerts per day from MDI. Their 6-person SOC manually triages each alert in the Defender XDR portal and copies findings to their Sentinel workspace for correlation. Mean time to contain a compromised account is 4 hours. The CISO wants to reduce this to under 15 minutes by automating the detection-to-response pipeline: MDI detects the threat, Sentinel correlates and enriches the alert, a playbook automatically disables the compromised account, and the SOC is notified via Teams. all within minutes of the initial detection.
Identity is the new perimeter. 80% of breaches involve compromised credentials according to the Verizon DBIR. Manual response to identity threats is too slow; attackers can escalate from initial compromise to domain admin in under 2 hours using tools like Mimikatz, Rubeus, and Impacket. Automating the response pipeline ensures compromised accounts are disabled within minutes, not hours. Integration with Sentinel adds cross-signal correlation that MDI alone cannot provide. combining identity events with endpoint, email, and cloud data for a complete attack picture.
mdi-test-user@contoso.com for safe validation.MDI alerts flow into Sentinel through the Microsoft Defender XDR data connector. This connector ingests incidents, alerts, and advanced hunting data from all Defender products including MDI.
IdentityLogonEvents. authentication events (NTLM, Kerberos, LDAP)IdentityDirectoryEvents. AD object changes (password resets, group changes)IdentityQueryEvents. LDAP and DNS queries (reconnaissance detection)// WHAT: Verify that MDI telemetry is flowing into the Sentinel Log Analytics workspace
// WHY: After connecting the Defender XDR data connector, data may take 5-10 minutes to appear.
// These three queries confirm each identity table is receiving events.
// OUTPUT: Event counts grouped by application/action type. Empty results = data not flowing.
// Query 1: Verify authentication events from MDI sensors
// IdentityLogonEvents captures all Kerberos, NTLM, and LDAP authentication events
// Application = "Active Directory" for on-prem AD auth; LogonType distinguishes interactive/network
IdentityLogonEvents
| where TimeGenerated > ago(1h)
| summarize Count=count() by Application, LogonType
| order by Count desc
// Query 2: Verify directory change events (password resets, group changes, object modifications)
// IdentityDirectoryEvents captures AD object mutations detected by MDI sensors
IdentityDirectoryEvents
| where TimeGenerated > ago(24h)
| summarize Count=count() by ActionType
| order by Count desc
// Query 3: Verify MDI alerts are arriving via the SecurityAlert table
// ProviderName "Azure Advanced Threat Protection" = MDI alerts (legacy name still used in Sentinel)
SecurityAlert
| where TimeGenerated > ago(7d)
| where ProviderName == "Azure Advanced Threat Protection"
| summarize Count=count() by AlertName, AlertSeverity
| order by Count descIdentityLogonEvents table returns no results, check that your MDI sensors are healthy in the Defender XDR > Settings > Identities page. Unhealthy sensors stop sending data.Understand the MDI data tables before building analytics rules. Each table provides different identity signal types that you will use for detection and correlation.
// WHAT: Four key identity hunting queries for proactive threat detection in Sentinel
// WHY: These queries surface threats that MDI's built-in detections may not catch individually.
// Use them as Sentinel analytics rules or ad-hoc hunting queries.
// Query 1: Detect brute force via failed Kerberos authentication
// THRESHOLD: >10 failed attempts from a single account suggests credential guessing/spraying
// FailedAttempts and DistinctDCs help distinguish brute force from a single mistyped password
IdentityLogonEvents
| where TimeGenerated > ago(24h)
| where LogonType == "Kerberos" and ActionType == "LogonFailed"
| summarize FailedAttempts=count(), DistinctDCs=dcount(DestinationDeviceName)
by AccountName, AccountDomain
| where FailedAttempts > 10
| order by FailedAttempts desc
// Query 2: Detect suspicious LDAP queries targeting sensitive AD attributes
// WHY: Attackers enumerate admincount (find admins), serviceprincipalname (Kerberoasting targets),
// and msds-allowedtodelegateto (delegation abuse targets) during reconnaissance
// MITRE ATT&CK: T1087.002 (Account Discovery: Domain Account)
IdentityQueryEvents
| where TimeGenerated > ago(24h)
| where ActionType == "LDAP query"
| where QueryTarget has_any ("admincount", "serviceprincipalname", "msds-allowedtodelegateto")
| project TimeGenerated, AccountName, DeviceName, QueryType, QueryTarget
| order by TimeGenerated desc
// Query 3: Detect modifications to sensitive AD groups (privilege escalation)
// WHY: Adding accounts to Domain Admins or Enterprise Admins is the most direct privilege
// escalation path. This should be rare and always authorized via change management.
IdentityDirectoryEvents
| where TimeGenerated > ago(7d)
| where ActionType == "Group Membership changed"
| where TargetAccountDisplayName has_any ("Domain Admins", "Enterprise Admins",
"Schema Admins", "Account Operators")
| project TimeGenerated, AccountName, ActionType, TargetAccountDisplayName,
AdditionalFields
| order by TimeGenerated desc
// Query 4: Detect Pass-the-Hash via excessive NTLM authentication
// WHY: High NTLM volume from a single account indicates PtH lateral movement - legitimate
// environments use Kerberos. THRESHOLD: >20 NTLM logons from one account in 24h
IdentityLogonEvents
| where TimeGenerated > ago(24h)
| where LogonType == "NTLM" and Protocol == "Ntlm"
| where ActionType == "LogonSuccess"
| summarize NtlmLogons=count(), DistinctDevices=dcount(DeviceName)
by AccountName
| where NtlmLogons > 20
| order by NtlmLogons descIdentityQueryEvents table can be very high-volume. Use targeted filters (ActionType, QueryTarget) to avoid scanning millions of benign LDAP queries. Always include time filters in your queries.Create custom Sentinel analytics rules that detect identity-specific attack patterns. These rules complement MDI’s built-in detections with cross-signal correlation.
MDI. Credential Theft with Lateral Movement// WHAT: Sentinel analytics rule - Detect credential theft followed by lateral movement within 1 hour
// WHY: This correlation rule catches the most dangerous identity attack pattern: compromised
// credentials being immediately used to move laterally. Neither signal alone is definitive,
// but together they indicate an active break-in requiring immediate containment.
// MITRE ATT&CK: T1003 (OS Credential Dumping) + T1021 (Remote Services)
// FREQUENCY: Run every 15 minutes, looking back 1 hour for real-time detection
// THRESHOLD: Any match (>0 results) triggers an incident - this is a high-fidelity signal
// OUTPUT: The account that was compromised, the credential theft alert, and the device they moved to
// Step 1: Find MDI alerts indicating credential theft in the last hour
let credential_alerts = SecurityAlert
| where TimeGenerated > ago(1h)
| where ProviderName == "Azure Advanced Threat Protection"
| where AlertName has_any ("Suspected credential theft", "Suspected NTLM relay",
"Suspected overpass-the-hash", "Suspected identity theft")
| extend AccountName = tostring(parse_json(ExtendedProperties).["User Account"])
| project CredentialAlertTime=TimeGenerated, AlertName, AccountName, AlertSeverity;
// Step 2: Find successful lateral movement logons (RDP or network access to servers)
let lateral_movement = IdentityLogonEvents
| where TimeGenerated > ago(1h)
| where LogonType in ("RemoteInteractive", "Network") // RDP and SMB/WMI access
| where ActionType == "LogonSuccess"
| project LateralTime=TimeGenerated, AccountName, DeviceName, LogonType;
// Step 3: Correlate - same account appears in both credential theft AND lateral movement within 1 hour
credential_alerts
| join kind=inner lateral_movement on AccountName
| where LateralTime between (CredentialAlertTime .. (CredentialAlertTime + 1h))
| project CredentialAlertTime, AlertName, AccountName, LateralTime, DeviceName, LogonTypeAccountName to Account, DeviceName to Host// WHAT: Sentinel analytics rule - Detect non-admin accounts modifying sensitive AD groups
// WHY: Only approved admin accounts should modify Domain Admins, Enterprise Admins, etc.
// A non-admin account making these changes indicates compromised credentials or insider threat.
// MITRE ATT&CK: T1098 (Account Manipulation), T1078 (Valid Accounts)
// FREQUENCY: Every 15 minutes | THRESHOLD: Any match = High severity incident
// OUTPUT: The unauthorized account, which group was modified, and the target DC
let admin_accounts = dynamic(["admin", "svc-admin", "t2-admin"]); // Whitelist of authorized admins
IdentityDirectoryEvents
| where TimeGenerated > ago(15m)
| where ActionType == "Group Membership changed"
| where TargetAccountDisplayName has_any ("Domain Admins", "Enterprise Admins",
"Schema Admins", "Backup Operators", "Account Operators")
| where not(AccountName has_any (admin_accounts)) // Exclude known authorized admin accounts
| project TimeGenerated, AccountName, ActionType, TargetAccountDisplayName,
DestinationDeviceName, AdditionalFieldsCreate a Logic App playbook that automatically disables a compromised user account in Entra ID when a high-severity identity alert fires.
Playbook-MDI-DisableCompromisedUser# Create the resource group for playbooks
az group create --name rg-sentinel-playbooks --location eastus
# Deploy a Logic App with managed identity
az logic workflow create \
--resource-group rg-sentinel-playbooks \
--name Playbook-MDI-DisableCompromisedUser \
--definition @playbook-definition.json \
--mi-system-assigned
# Grant the Logic App's managed identity permission to disable users
LOGIC_APP_PRINCIPAL=$(az logic workflow show \
--resource-group rg-sentinel-playbooks \
--name Playbook-MDI-DisableCompromisedUser \
--query identity.principalId -o tsv)
# Assign User.ReadWrite.All via Microsoft Graph app role
az ad app permission grant \
--id $LOGIC_APP_PRINCIPAL \
--api 00000003-0000-0000-c000-000000000000 \
--scope User.ReadWrite.All# Playbook Logic Flow
#
# 1. TRIGGER: Microsoft Sentinel incident
# โ Fires when an incident is created with identity entities
#
# 2. GET ENTITIES: Extract account entities from the incident
# โ Parse Account name and UPN from incident entities
#
# 3. CONDITION: Check if the alert severity is High or Critical
# โ Only auto-disable for confirmed high-severity threats
#
# 4. ACTION (if High/Critical):
# a. Disable the user account in Entra ID
# โ PATCH https://graph.microsoft.com/v1.0/users/{id}
# โ Body: {"accountEnabled": false}
# b. Revoke all refresh tokens
# โ POST https://graph.microsoft.com/v1.0/users/{id}/revokeSignInSessions
# c. Add a comment to the Sentinel incident
# โ "Account {UPN} was automatically disabled by playbook"
# d. Send a Teams notification to the SOC channel
# โ Include: user, alert name, severity, incident link
#
# 5. ACTION (if Medium/Low):
# a. Add a comment recommending manual review
# b. Send a Teams notification requesting analyst reviewCreate a second playbook that sends rich notifications to your SOC Teams channel whenever an identity incident is created.
Playbook-MDI-TeamsNotificationSOC-Identity-Alerts# Test the Teams webhook directly
$webhookUrl = "https://contoso.webhook.office.com/webhookb2/..."
$card = @{
"@type" = "MessageCard"
"@context" = "http://schema.org/extensions"
summary = "MDI Alert: Credential Theft Detected"
themeColor = "FF0000"
title = "๐จ High Severity: Suspected Credential Theft"
sections = @(@{
activityTitle = "MDI Identity Alert"
activitySubtitle = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss UTC')"
facts = @(
@{ name = "User"; value = "john.doe@contoso.com" },
@{ name = "Alert"; value = "Suspected overpass-the-hash attack" },
@{ name = "Severity"; value = "High" },
@{ name = "Source DC"; value = "DC01.contoso.com" }
)
})
potentialAction = @(@{
"@type" = "OpenUri"
name = "Open in Sentinel"
targets = @(@{ os = "default"; uri = "https://portal.azure.com/#/sentinel" })
})
} | ConvertTo-Json -Depth 10
Invoke-RestMethod -Uri $webhookUrl -Method Post -Body $card -ContentType "application/json"Automation rules connect your analytics rules to your playbooks. They define which incidents trigger which playbooks based on severity, product, and entity type.
Auto-Disable-Compromised-IdentityPlaybook-MDI-DisableCompromisedUserPlaybook-MDI-TeamsNotificationSOC-Identity-Teamauto-response-executedNotify-Identity-Alert-MediumPlaybook-MDI-TeamsNotification only (no auto-disable)Create a Sentinel workbook that provides real-time identity threat dashboards for your SOC.
// Tile 1: MDI Alert Trend (last 30 days)
SecurityAlert
| where ProviderName == "Azure Advanced Threat Protection"
| where TimeGenerated > ago(30d)
| summarize AlertCount=count() by bin(TimeGenerated, 1d), AlertSeverity
| render timechart
// Tile 2: Top Targeted Users
SecurityAlert
| where ProviderName == "Azure Advanced Threat Protection"
| where TimeGenerated > ago(30d)
| extend User = tostring(parse_json(ExtendedProperties).["User Account"])
| summarize AlertCount=count() by User
| top 10 by AlertCount
| render barchart
// Tile 3: Authentication Anomalies
IdentityLogonEvents
| where TimeGenerated > ago(7d)
| where ActionType == "LogonFailed"
| summarize FailedLogons=count() by AccountName, LogonType
| where FailedLogons > 50
| order by FailedLogons desc
// Tile 4: Sensitive Group Modifications
IdentityDirectoryEvents
| where TimeGenerated > ago(30d)
| where ActionType == "Group Membership changed"
| where TargetAccountDisplayName has_any ("Domain Admins", "Enterprise Admins")
| project TimeGenerated, AccountName, TargetAccountDisplayName, ActionType
// Tile 5: Playbook Execution Summary
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.LOGIC"
| where resource_workflowName_s has "MDI"
| where TimeGenerated > ago(30d)
| summarize Executions=count() by resource_workflowName_s, status_s
| render piechartMDI. Identity Threat DashboardCombine on-premises MDI data with cloud-based Entra ID sign-in logs for full identity investigation coverage.
// WHAT: Cross-signal correlation - Link on-premises credential theft (MDI) with cloud sign-ins (Entra ID)
// WHY: When an attacker steals credentials on-prem, they often use them to access cloud services.
// This query finds users who triggered MDI credential theft alerts AND successfully signed into
// cloud apps afterward - indicating a fully compromised hybrid identity.
// Neither MDI nor Entra ID alone sees this complete picture; Sentinel correlation is essential.
// TABLES: SecurityAlert (MDI alerts) joined with SigninLogs (Entra ID cloud sign-ins)
// OUTPUT: Cloud sign-in details for compromised users: app accessed, IP, location, risk level
// Look for: sign-ins from unusual countries, anonymous proxies, or unfamiliar devices
let compromised_users = SecurityAlert
| where ProviderName == "Azure Advanced Threat Protection"
| where AlertSeverity in ("High", "Medium")
| where TimeGenerated > ago(24h)
| extend AccountUPN = tostring(parse_json(ExtendedProperties).["User Account"])
| distinct AccountUPN;
SigninLogs
| where TimeGenerated > ago(24h)
| where UserPrincipalName in (compromised_users)
| where ResultType == "0" // ResultType 0 = successful sign-in (attacker got in)
| project TimeGenerated, UserPrincipalName, AppDisplayName, IPAddress,
LocationDetails, DeviceDetail, RiskLevelDuringSignIn
| order by TimeGenerated desc
// WHAT: Correlate on-prem NTLM activity with Entra ID risky user detections
// WHY: Users flagged as risky by Entra ID Protection AND using NTLM on-prem = high-confidence
// compromised identity requiring immediate containment in both environments
let ntlm_users = IdentityLogonEvents
| where TimeGenerated > ago(24h)
| where Protocol == "Ntlm" and ActionType == "LogonSuccess"
| distinct AccountUpn;
AADRiskyUsers
| where RiskLevel in ("high", "medium")
| where UserPrincipalName in (ntlm_users)
| project UserPrincipalName, RiskLevel, RiskState, RiskDetailValidate the complete detection-to-response pipeline using a controlled test scenario.
mdi-test-user@contoso.comGet-MgUser -UserId mdi-test-user@contoso.com | Select AccountEnabledauto-response-executed tag# Re-enable the test account after validation
Connect-MgGraph -Scopes "User.ReadWrite.All"
# Check account status
Get-MgUser -UserId "mdi-test-user@contoso.com" | Select-Object DisplayName, AccountEnabled
# Re-enable the account
Update-MgUser -UserId "mdi-test-user@contoso.com" -AccountEnabled:$true
# Verify it's re-enabled
Get-MgUser -UserId "mdi-test-user@contoso.com" | Select-Object DisplayName, AccountEnabledFor VIP accounts and service accounts, add an approval step before auto-disabling. This prevents automation from causing outages for critical business accounts.
# Create a Sentinel Watchlist for protected accounts
# Navigate to Sentinel > Watchlists > Create
# Watchlist name: ProtectedIdentities
# Alias: protected_identities
# CSV format:
# UPN,AccountType,Owner,BusinessJustification
# admin@contoso.com,BreakGlass,IT-Security,Emergency access account
# svc-erp@contoso.com,ServiceAccount,ERP-Team,SAP integration service
# ceo@contoso.com,VIP,Executive-Office,CEO account requires manual review
# Use in KQL to check if a user is protected:
let protected = _GetWatchlist('protected_identities') | project UPN;
SecurityAlert
| where ProviderName == "Azure Advanced Threat Protection"
| extend User = tostring(parse_json(ExtendedProperties).["User Account"])
| extend IsProtected = User in (protected)Monitor your automation pipeline to ensure playbooks are running successfully and not failing silently.
// Playbook execution summary (last 7 days)
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.LOGIC"
| where TimeGenerated > ago(7d)
| where resource_workflowName_s has "MDI"
| summarize
TotalRuns=count(),
Succeeded=countif(status_s == "Succeeded"),
Failed=countif(status_s == "Failed")
by resource_workflowName_s
| extend SuccessRate = round(todouble(Succeeded) / TotalRuns * 100, 1)
| project Playbook=resource_workflowName_s, TotalRuns, Succeeded, Failed, SuccessRate
// Incidents with auto-response tag
SecurityIncident
| where TimeGenerated > ago(30d)
| where Labels has "auto-response-executed"
| summarize Count=count() by bin(TimeGenerated, 1d)
| render timechartReview the end-to-end pipeline and plan ongoing operations.
# Disable automation rules (if testing)
# Navigate to Sentinel > Automation > disable the test rules
# Re-enable any disabled test accounts
Connect-MgGraph -Scopes "User.ReadWrite.All"
Update-MgUser -UserId "mdi-test-user@contoso.com" -AccountEnabled:$true
# Delete test playbooks (if no longer needed)
az logic workflow delete \
--resource-group rg-sentinel-playbooks \
--name Playbook-MDI-DisableCompromisedUser --yes| Resource | Description |
|---|---|
| MDI in Microsoft Defender XDR | Integration overview and data flow |
| Microsoft Defender XDR connector for Sentinel | Connect Defender XDR data including MDI to Sentinel |
| Automate responses with playbooks | Build Logic App playbooks for automated incident response |
| Create custom analytics rules | Detect threats with scheduled query rules |
| Automation rules in Sentinel | Orchestrate playbook execution and incident management |
| MDI advanced hunting | Identity-specific KQL tables and hunting scenarios |
| Sentinel watchlists | Manage lookup data like protected account lists |
| Monitor your data with workbooks | Create custom dashboards for identity threat data |