Intermediate ⏱ 90 min 📋 10 Steps

Automate Incident Response with Playbooks

Create Logic App playbooks to auto-enrich incidents with threat intelligence, post formatted notifications to Microsoft Teams, isolate compromised accounts in Entra ID, and build end-to-end automated remediation workflows triggered by Sentinel automation rules.

📋 Overview

About This Lab

In this hands-on lab you will build a production-grade SOAR (Security Orchestration, Automation, and Response) workflow using Microsoft Sentinel playbooks powered by Azure Logic Apps. You will create a multi-action playbook that triggers automatically when a high-severity incident is created, enriches the incident with threat intelligence and geolocation data, posts a formatted adaptive card to a Microsoft Teams SOC channel, disables compromised user accounts in Microsoft Entra ID, revokes active sessions, and writes a complete evidence trail back to the incident timeline. By the end of this lab you will have a fully automated incident response pipeline that reduces mean time to respond from hours to seconds.

🏢 Enterprise Use Case

A healthcare company with 5,000 employees receives over 200 low and medium severity incidents daily across identity, endpoint, and cloud workloads. Their SOC team of 8 analysts cannot keep pace with manual triage: the current mean time to respond (MTTR) is 4 hours. Critical incidents involving compromised credentials sit in queues while analysts manually look up IP reputation, check geolocation, notify the on-call team via email, and navigate to the Entra ID portal to disable accounts. Regulatory requirements (HIPAA) demand that compromised accounts are contained within 15 minutes of detection. Leadership has mandated automated triage, enrichment, notification, and containment workflows to reduce MTTR from 4 hours to under 15 minutes while freeing analysts to focus on high-complexity investigations.

🎯 What You Will Learn

  1. Understand the Sentinel automation architecture: automation rules vs. playbooks vs. Logic Apps
  2. Register and authorize the Microsoft Sentinel Logic App connector
  3. Create a Logic App playbook triggered by Sentinel incidents
  4. Enrich incidents with IP geolocation and threat intelligence lookups
  5. Post formatted adaptive cards to Microsoft Teams channels
  6. Automate account isolation: disable users and revoke sessions in Entra ID
  7. Build automation rules that trigger playbooks based on incident properties
  8. Test playbooks with simulated incidents and debug failures
  9. Monitor playbook run history and handle transient errors
  10. Collect remediation evidence and update incident comments programmatically

🔑 Why This Matters

SOAR automation is the force multiplier that transforms a reactive SOC into a proactive security operation. According to industry research, organizations leveraging automated incident response reduce MTTR by up to 80% and handle 10x more incidents without adding headcount. Without automation, every minute a compromised account remains active is a minute an attacker can exfiltrate patient data, move laterally through the network, or establish persistence. Microsoft Sentinel’s native integration with Azure Logic Apps provides a no-code/low-code platform to orchestrate responses across Microsoft 365, Entra ID, Defender XDR, and hundreds of third-party services: making it the ideal SOAR platform for organizations already invested in the Microsoft security ecosystem.

⚙️ Prerequisites

  • Completed Labs 1 & 2: Microsoft Sentinel deployed with data connectors and analytics rules configured (Lab 01, Lab 02)
  • Microsoft Sentinel Contributor role: Required to create automation rules and assign playbooks to incidents
  • Logic App Contributor role: Required to create and manage Logic App resources in the resource group
  • User Administrator role in Entra ID: Required for the account isolation actions (disable user, revoke sessions)
  • Microsoft Teams: A Teams channel designated for SOC notifications (create a “SOC-Alerts” channel in any team)
  • Azure CLI or PowerShell Az module: For deploying resources via command line (optional but recommended)
  • Microsoft 365 E5 or E5 Security: Provides the necessary Graph API permissions for identity actions
⚠️ Important: The account isolation actions in Steps 6 will disable real user accounts. In a lab environment, use a dedicated test account. Never test containment playbooks against production user accounts without proper change management approval.

Step 1 · Understand Sentinel Automation Architecture

Microsoft Sentinel provides three automation mechanisms that work together. Understanding when to use each is essential before building your first playbook.

Automation Rules

Lightweight rules that run automatically when incidents are created or updated. They can change incident properties (severity, status, owner), suppress noisy alerts, or trigger playbooks. Automation rules execute in order of priority and are evaluated for every incident: think of them as the “traffic controller” for your SOAR pipeline.

Playbooks (Logic Apps)

Full workflow engines powered by Azure Logic Apps. Playbooks can call external APIs, perform complex branching logic, loop through entities, and orchestrate multi-step response actions. They are triggered by automation rules or manually by an analyst from the incident page. Playbooks are where the real SOAR magic happens.

Automation Rules vs. Playbooks: When to Use Which

  • Automation rule only: Simple property changes: auto-assign owner, change severity, close false positives
  • Automation rule + playbook: Complex workflows: enrich incident, notify Teams, isolate accounts, create ServiceNow ticket
  • Playbook only (manual trigger): On-demand analyst actions: run forensic collection, escalate to management, generate report
💡 Pro Tip: Sentinel supports two playbook trigger types: Microsoft Sentinel Incident (receives the full incident object with entities) and Microsoft Sentinel Alert (receives a single alert). For SOAR workflows, always use the incident trigger: it provides correlated entities, severity, and the ability to update the incident directly.

Step 2 · Register the Microsoft Sentinel Logic App Connector

Before creating playbooks, you must grant the Logic App managed identity permissions to interact with Microsoft Sentinel and Microsoft Graph. This step registers the required API connections.

Register the Resource Provider

Ensure the Microsoft.Logic resource provider is registered in your subscription:

# Register the Logic Apps resource provider in your subscription
# PURPOSE: Azure must register the provider before you can create Logic App resources
# This is a one-time operation per subscription
az provider register --namespace Microsoft.Logic

# Verify registration status
# Expected output: "Registered" - if "Registering", wait 1-2 min and re-check
# If "NotRegistered", you may lack subscription-level permissions
az provider show --namespace Microsoft.Logic --query "registrationState" -o tsv

Grant Sentinel Responder Role

The playbook’s managed identity needs the Microsoft Sentinel Responder role to read and update incidents:

# Variables - replace with your actual resource group and workspace names
RG="rg-sentinel-lab"
WORKSPACE="law-sentinel-lab"
SUB_ID=$(az account show --query id -o tsv)  # Get current subscription ID

# After creating the Logic App (Step 3), assign the Sentinel Responder role:
# This grants the playbook’s managed identity permission to read/update incidents
# --query "identity.principalId" : Extract the managed identity’s object ID
LOGIC_APP_PRINCIPAL_ID=$(az logic workflow show \
  --resource-group $RG \
  --name "Sentinel-IncidentEnrich" \
  --query "identity.principalId" -o tsv)

# Assign "Microsoft Sentinel Responder" role to the Logic App’s managed identity
# --assignee-object-id       : The managed identity’s principal ID from above
# --assignee-principal-type  : ServicePrincipal (not User) for managed identities
# --role                     : "Microsoft Sentinel Responder" allows reading & updating incidents
# --scope                    : Resource group scope - limits permissions to this RG only
# Expected output: JSON role assignment object with "provisioningState": "Created"
az role assignment create \
  --assignee-object-id $LOGIC_APP_PRINCIPAL_ID \
  --assignee-principal-type ServicePrincipal \
  --role "Microsoft Sentinel Responder" \
  --scope "/subscriptions/$SUB_ID/resourceGroups/$RG"

Portal Steps

  • Navigate to Azure PortalSubscriptions → your subscription → Resource providers
  • Search for Microsoft.Logic and click Register if not already registered
  • Navigate to Microsoft SentinelSettingsPlaybook permissions
  • Select your resource group and click Apply to grant Sentinel permission to run playbooks in that resource group
💡 Pro Tip: Use a system-assigned managed identity for your Logic Apps instead of API connections with user credentials. Managed identities eliminate credential management overhead, automatically rotate secrets, and follow the principle of least privilege. See Authenticate playbooks to Sentinel.

Step 3 · Create a Logic App Playbook for Incident Enrichment

Now you will create the core Logic App that serves as your incident response playbook. This playbook uses the Microsoft Sentinel Incident trigger to receive the full incident context including mapped entities (IPs, accounts, hosts).

Deploy via Azure CLI

# Create a Consumption-tier Logic App with managed identity
az logic workflow create \
  --resource-group $RG \
  --name "Sentinel-IncidentEnrich" \
  --location "eastus" \
  --mi-system-assigned \
  --definition '{
    "definition": {
      "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
      "contentVersion": "1.0.0.0",
      "triggers": {
        "Microsoft_Sentinel_incident": {
          "type": "ApiConnectionWebhook",
          "inputs": {
            "host": {
              "connection": {
                "name": "@parameters($connections)[azuresentinel][connectionId]"
              }
            },
            "body": {
              "callback_url": "@listCallbackUrl()"
            },
            "path": "/incident-creation"
          }
        }
      },
      "actions": {},
      "parameters": {
        "$connections": {
          "type": "Object"
        }
      }
    }
  }'

Portal Steps

  • Go to Microsoft SentinelAutomationCreatePlaybook with incident trigger
  • Set the name to Sentinel-IncidentEnrich
  • Select your resource group and region (same as your Sentinel workspace)
  • Enable System-assigned managed identity
  • Click Create and continue to designer
  • The Logic App Designer opens with the Sentinel incident trigger pre-configured

Extract Entities from the Incident

Add the Entities: Get IPs and Entities: Get Accounts actions from the Sentinel connector to parse entity arrays from the incident object:

  • Click + New step → search “Microsoft Sentinel” → select Entities · Get IPs
  • Set the Entities List field to the dynamic content Entities from the trigger
  • Add another step: Entities · Get Accounts with the same Entities list
⚠️ Important: Entity extraction actions return arrays. Subsequent steps that reference individual entities (e.g., a single IP address) will automatically be wrapped in a For each loop by the Logic App designer. Plan your workflow accordingly: each IP and each account will be processed in its own loop iteration.

Step 4 · Add Threat Intelligence Enrichment Actions

With IP entities extracted, you will now enrich each IP address with threat intelligence reputation data and geolocation information. This context helps analysts prioritize genuine threats over false positives.

Add an HTTP Action for IP Geolocation

Inside the For each IP loop, add an HTTP action to query a geolocation API:

  • Click Add an action inside the For each loop → search HTTP → select HTTP
  • Method: GET
  • URI: https://ipapi.co/@{items('For_each_IP')?['Address']}/json/
  • Add a Parse JSON action to structure the response

Parse JSON Schema for Geolocation

{
  "type": "object",
  "properties": {
    "ip":       { "type": "string" },
    "city":     { "type": "string" },
    "region":   { "type": "string" },
    "country_name": { "type": "string" },
    "org":      { "type": "string" },
    "asn":      { "type": "string" },
    "latitude": { "type": "number" },
    "longitude":{ "type": "number" }
  }
}

Query Sentinel TI Indicators via KQL

You can also check the IP against threat intelligence indicators already ingested into your Sentinel workspace. Add a Run query and list results action (Azure Monitor Logs connector):

// Query Sentinel TI indicators to check if an IP is known-malicious
// PURPOSE: Match incident IP entities against your threat intelligence database
// WHY: Enriching with TI data helps analysts prioritize: known-bad IP = high confidence
// This runs inside a Logic App "Run query and list results" action
ThreatIntelligenceIndicator
| where TimeGenerated > ago(90d)                    // Search last 90 days of TI data
| where isnotempty(NetworkIP)                        // Only IP-type indicators
| where NetworkIP == "@{items('For_each_IP')?['Address']}"  // Dynamic: current incident IP
| summarize
    ThreatTypes    = make_set(ThreatType),           // e.g., ["malicious-activity", "C2"]
    Confidence     = max(ConfidenceScore),            // Highest confidence score (0-100)
    Sources        = make_set(SourceSystem),          // Which TI feed(s) flagged this IP
    LastSeen       = max(TimeGenerated)               // Most recent sighting in TI feed
| project NetworkIP = "@{items('For_each_IP')?['Address']}",
    ThreatTypes, Confidence, Sources, LastSeen
// Expected output: If match found → threat type, confidence, and source details
// No results = IP not in your TI database (doesn’t mean it’s safe - just unknown)

Build an Enrichment Summary Variable

Before the For each loop, initialize a string variable called EnrichmentSummary. Inside the loop, append geolocation and TI results to this variable. You will use it later to update the incident and post to Teams.

💡 Pro Tip: For production deployments, consider using Microsoft Defender Threat Intelligence (MDTI) for IP reputation lookups instead of free geolocation APIs. MDTI provides richer context including attack infrastructure mapping, reputation scores, and associated threat actors.

Step 5 · Configure Teams Notification Actions

After enrichment, the playbook will post a formatted notification to your SOC Teams channel with all relevant incident details, enabling rapid situational awareness without requiring analysts to open the Sentinel portal.

Add the Teams Connector Action

  • Click + New step (outside any loop, after enrichment completes)
  • Search Microsoft Teams → select Post Adaptive Card in a chat or channel
  • Post as: Flow bot
  • Post in: Channel
  • Team: select your team → Channel: SOC-Alerts

Adaptive Card JSON Template

Paste this adaptive card JSON into the Adaptive Card field, replacing dynamic content where indicated:

{
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.4",
  "body": [
    {
      "type": "Container",
      "style": "emphasis",
      "items": [
        {
          "type": "TextBlock",
          "text": "?? Sentinel Incident #@{triggerBody()?['object']?['properties']?['incidentNumber']}",
          "weight": "Bolder",
          "size": "Large"
        }
      ]
    },
    {
      "type": "FactSet",
      "facts": [
        { "title": "Title:", "value": "@{triggerBody()?['object']?['properties']?['title']}" },
        { "title": "Severity:", "value": "@{triggerBody()?['object']?['properties']?['severity']}" },
        { "title": "Status:", "value": "@{triggerBody()?['object']?['properties']?['status']}" },
        { "title": "Created:", "value": "@{triggerBody()?['object']?['properties']?['createdTimeUtc']}" },
        { "title": "Alerts:", "value": "@{length(triggerBody()?['object']?['properties']?['alerts'])}" }
      ]
    },
    {
      "type": "TextBlock",
      "text": "**Enrichment Results:**",
      "wrap": true
    },
    {
      "type": "TextBlock",
      "text": "@{variables('EnrichmentSummary')}",
      "wrap": true
    }
  ],
  "actions": [
    {
      "type": "Action.OpenUrl",
      "title": "?? Open in Sentinel",
      "url": "@{triggerBody()?['object']?['properties']?['incidentUrl']}"
    }
  ]
}
💡 Pro Tip: Add Action.Submit buttons to your adaptive card to let analysts approve or reject containment actions directly from Teams. Logic Apps can receive the response via an HTTP webhook, creating a human-in-the-loop approval workflow for high-impact actions like account disablement.

Step 6 · Add Account Isolation Actions

This step implements automated containment by disabling compromised user accounts in Microsoft Entra ID and revoking all active sessions. This is the highest-impact automation action: it stops an attacker’s access within seconds of incident creation.

Add Microsoft Graph Actions via HTTP

Inside the For each Account loop, add HTTP actions that call the Microsoft Graph API using the Logic App’s managed identity:

Action 1: Disable the User Account

HTTP Action Configuration:
  Method:  PATCH
  URI:     https://graph.microsoft.com/v1.0/users/@{items('For_each_Account')?['AadUserId']}
           // AadUserId = Entra ID object ID of the compromised user from the incident entity
  Headers: Content-Type: application/json
  Body:    { "accountEnabled": false }
           // Sets the account to disabled - user cannot sign in anywhere
           // Existing sessions remain active until revoked (see Action 2)
  Authentication: Managed Identity
           // Uses the Logic App’s system-assigned managed identity (no credentials stored)
  Audience: https://graph.microsoft.com
           // Token audience for Microsoft Graph API calls
  // Expected result: HTTP 204 No Content = success
  // Requires: User.ReadWrite.All application permission on the managed identity

Action 2: Revoke All Sign-In Sessions

HTTP Action Configuration:
  Method:  POST
  URI:     https://graph.microsoft.com/v1.0/users/@{items('For_each_Account')?['AadUserId']}/revokeSignInSessions
           // Invalidates ALL refresh tokens and session cookies for this user
           // Forces re-authentication on every device and application
           // Combined with account disable, this fully contains a compromised identity
  Authentication: Managed Identity
  Audience: https://graph.microsoft.com
  // Expected result: HTTP 200 with { "value": true } = all sessions revoked
  // Note: Active browser sessions may take up to 1 hour to expire due to token caching

Grant Graph API Permissions to the Managed Identity

The Logic App’s managed identity must have User.ReadWrite.All application permission in Microsoft Graph. Use PowerShell to grant this:

# Connect to Microsoft Graph with admin consent
# Scopes needed: AppRoleAssignment.ReadWrite.All to grant permissions,
#                Application.Read.All to look up the Graph service principal
Connect-MgGraph -Scopes "AppRoleAssignment.ReadWrite.All","Application.Read.All"

# Get the Logic App managed identity service principal
# Replace with the actual principal ID from: az logic workflow show --query identity.principalId
$MIObjectId = "PASTE-LOGIC-APP-PRINCIPAL-ID"

# Get the Microsoft Graph service principal (it’s a well-known enterprise app)
$GraphSP = Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'" | Select-Object -First 1

# Find the User.ReadWrite.All app role (application permission, not delegated)
# This permission allows the managed identity to disable users and update profiles
$AppRole = $GraphSP.AppRoles | Where-Object { $_.Value -eq "User.ReadWrite.All" }

# Assign the User.ReadWrite.All permission to the Logic App’s managed identity
# -ServicePrincipalId : The managed identity receiving the permission
# -PrincipalId        : Same as above (the identity making the API calls)
# -ResourceId         : Microsoft Graph’s service principal ID
# -AppRoleId          : The specific permission being granted
# Expected output: AppRoleAssignment object confirming the grant
New-MgServicePrincipalAppRoleAssignment `
  -ServicePrincipalId $MIObjectId `
  -PrincipalId $MIObjectId `
  -ResourceId $GraphSP.Id `
  -AppRoleId $AppRole.Id
⚠️ Important: In production, implement a safeguard list before the disable action. Add a condition that checks the user’s UPN against a watchlist of protected accounts (C-suite, service accounts, break-glass accounts). Never auto-disable a break-glass admin account: this could lock your entire organization out of Entra ID.

Add a Condition to Protect Critical Accounts

// KQL - Create a watchlist named "ProtectedAccounts" to safeguard critical accounts
// PURPOSE: Prevent automated containment from disabling break-glass or C-suite accounts
// WHY: Auto-disabling a break-glass admin could lock your entire org out of Entra ID
// Upload a CSV with column: UserPrincipalName
// Example entries:
// admin@contoso.com          ← Global admin accounts
// breakglass01@contoso.com   ← Emergency access accounts (NEVER auto-disable)
// ceo@contoso.com            ← Executive accounts requiring manual review

// In the Logic App, before the disable action add a condition:
// Condition: items('For_each_Account')?['UPNSuffix'] is NOT in ProtectedAccounts watchlist
// This ensures protected accounts skip auto-containment and go to manual analyst review

Step 7 · Create an Automation Rule to Trigger the Playbook

With the playbook ready, you need an automation rule that triggers it automatically when matching incidents are created. Automation rules act as the bridge between Sentinel analytics and your SOAR playbooks.

Portal Steps

  • Navigate to Microsoft SentinelAutomationCreateAutomation rule
  • Name: Auto-Enrich-and-Contain-High-Severity
  • Trigger: When incident is created
  • Conditions:
    • Incident provider → EqualsMicrosoft Sentinel
    • Severity → EqualsHigh
    • Tactics → ContainsInitial Access or Credential Access
  • Actions:
    • Action 1: Change statusActive
    • Action 2: Run playbookSentinel-IncidentEnrich
  • Order: 1 (highest priority)
  • Status: Enabled
  • Expiration: None
  • Click Apply

Deploy via Azure CLI

# Get the Logic App resource ID
PLAYBOOK_ID=$(az logic workflow show \
  --resource-group $RG \
  --name "Sentinel-IncidentEnrich" \
  --query "id" -o tsv)

# Create the automation rule via REST API
az rest --method PUT \
  --url "https://management.azure.com/subscriptions/$SUB_ID/resourceGroups/$RG/providers/Microsoft.OperationalInsights/workspaces/$WORKSPACE/providers/Microsoft.SecurityInsights/automationRules/auto-enrich-contain?api-version=2024-03-01" \
  --body "{
    \"properties\": {
      \"displayName\": \"Auto-Enrich-and-Contain-High-Severity\",
      \"order\": 1,
      \"triggeringLogic\": {
        \"isEnabled\": true,
        \"triggersOn\": \"Incidents\",
        \"triggersWhen\": \"Created\",
        \"conditions\": [
          {
            \"conditionType\": \"Property\",
            \"conditionProperties\": {
              \"propertyName\": \"IncidentSeverity\",
              \"operator\": \"Equals\",
              \"propertyValues\": [\"High\"]
            }
          }
        ]
      },
      \"actions\": [
        {
          \"actionType\": \"ModifyProperties\",
          \"order\": 1,
          \"actionConfiguration\": {
            \"status\": \"Active\"
          }
        },
        {
          \"actionType\": \"RunPlaybook\",
          \"order\": 2,
          \"actionConfiguration\": {
            \"logicAppResourceId\": \"$PLAYBOOK_ID\",
            \"tenantId\": \"$(az account show --query tenantId -o tsv)\"
          }
        }
      ]
    }
  }"
💡 Pro Tip: Create separate automation rules for different severity levels. High-severity incidents should trigger full containment (disable + revoke), while medium-severity incidents might only trigger enrichment + notification, leaving the containment decision to a human analyst. This tiered approach balances speed with safety.

Step 8 · Test with a Simulated Incident

Before relying on the playbook in production, you must validate it end-to-end with a controlled test incident. You can create a test incident programmatically or trigger one of your analytics rules deliberately.

Option A: Create a Test Incident via API

# Create a test incident with IP and account entities
az rest --method PUT \
  --url "https://management.azure.com/subscriptions/$SUB_ID/resourceGroups/$RG/providers/Microsoft.OperationalInsights/workspaces/$WORKSPACE/providers/Microsoft.SecurityInsights/incidents/test-incident-001?api-version=2024-03-01" \
  --body "{
    \"properties\": {
      \"title\": \"[TEST] Suspicious sign-in from malicious IP\",
      \"severity\": \"High\",
      \"status\": \"New\",
      \"description\": \"Test incident for playbook validation. Simulates a compromised account signing in from a known malicious IP address.\"
    }
  }"

Option B: Trigger an Analytics Rule

If you have a brute-force detection rule from Lab 02, generate failed sign-ins against your test account to trigger it:

# PowerShell: Generate failed sign-ins to trigger brute-force detection
# PURPOSE: Create test events that will fire the analytics rule and trigger the playbook
# WARNING: Use a dedicated TEST ACCOUNT only - never test against production admin accounts
$TestUPN = "testuser@yourtenant.onmicrosoft.com"  # Replace with your test account UPN
$WrongPassword = ConvertTo-SecureString "WrongPassword123!" -AsPlainText -Force
$Cred = New-Object PSCredential($TestUPN, $WrongPassword)

# Attempt 15 failed logins to exceed the brute-force threshold (10+)
# Each attempt generates a SigninLogs entry with ResultType = 50126 (bad password)
for ($i = 1; $i -le 15; $i++) {
    try {
        Connect-AzAccount -Credential $Cred -ErrorAction SilentlyContinue
    } catch {
        Write-Host "Attempt $i failed (expected)"  # Failures are intentional
    }
    Start-Sleep -Seconds 2  # 2-second delay between attempts (realistic pacing)
}
# Expected result: 15 failed sign-in events in SigninLogs within ~30 seconds
# Wait 5-15 min for ingestion, then check the Incidents blade for a new incident

Validate the Playbook Execution

  • Check Microsoft SentinelAutomationAutomation rule runs to confirm the rule fired
  • Open the incident and check the Activity log tab for playbook execution entries
  • Verify the Teams channel received the adaptive card notification
  • Confirm the test user account is disabled in Entra ID (Users → search user → check Account enabled status)
  • Check the incident comments for the enrichment summary
⚠️ Important: After testing, re-enable the test user account immediately: Update-MgUser -UserId "test-user-object-id" -AccountEnabled:$true. Leaving test accounts disabled can cause confusion and trigger additional alerts.

Step 9 · Monitor Playbook Runs and Debug Failures

Production playbooks will occasionally fail due to API throttling, expired connections, or unexpected data formats. Knowing how to monitor and debug Logic App runs is essential for maintaining reliable automation.

View Run History in the Azure Portal

  • Navigate to Logic AppsSentinel-IncidentEnrichOverview
  • The Runs history section shows all executions with status (Succeeded, Failed, Cancelled)
  • Click any run to open the visual debugger: each action shows inputs, outputs, status, and duration
  • Failed actions display the error message and HTTP status code in the outputs section

Query Playbook Health with KQL

Logic App diagnostic logs can be sent to Log Analytics for centralized monitoring. Use this KQL query to identify failing playbooks:

// Monitor Logic App (playbook) failures using diagnostic logs
// PURPOSE: Identify playbooks that are silently failing and missing incidents
// WHY: A failed playbook means no enrichment, no notification, no containment
// PREREQUISITE: Enable Diagnostic settings on the Logic App → send to Log Analytics
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.LOGIC"  // Logic Apps resource provider
| where Category == "WorkflowRuntime"           // Runtime execution logs (not design-time)
| where status_s == "Failed"                    // Only failed runs
| summarize
    FailedRuns   = count(),                     // Total failures per playbook
    LastFailure  = max(TimeGenerated),           // Most recent failure timestamp
    ErrorCodes   = make_set(code_s)              // Unique error codes (e.g., 403, 429, 404)
  by resource_workflowName_s                    // Group by Logic App name
| order by FailedRuns desc
// Expected output: Playbooks with failure counts and error codes
// ACTION: 403 = permission issue, 429 = throttling, 404 = invalid entity ID

Common Failure Patterns and Fixes

  • 403 Forbidden: Managed identity lacks required permissions. Re-run the role assignment from Step 2
  • 429 Too Many Requests: Graph API throttling. Add a retry policy with exponential backoff to your HTTP actions
  • 404 Not Found: Entity AadUserId is empty or invalid. Add a condition to check length(items('For_each_Account')?['AadUserId']) > 0 before calling Graph
  • Connection expired: API connection authorization expired. Re-authorize in the Logic App’s API connections blade

Add Retry Policies

// In Logic App Code View, add retry policy to HTTP actions:
// PURPOSE: Handle transient failures (API throttling, network blips) automatically
// WHY: Without retries, a single 429 response causes the entire playbook to fail
"retryPolicy": {
  "type": "exponential",     // Exponential backoff: wait longer between each retry
  "count": 4,                // Maximum 4 retry attempts before giving up
  "interval": "PT10S",       // Initial wait: 10 seconds before first retry
  "minimumInterval": "PT5S", // Never wait less than 5 seconds
  "maximumInterval": "PT1H"  // Never wait more than 1 hour (cap for exponential growth)
}
// Retry sequence: 10s → 20s → 40s → 80s (exponential backoff)
// This handles Graph API 429 (throttling) responses gracefully
💡 Pro Tip: Enable Diagnostic settings on your Logic App to send runtime logs to the same Log Analytics workspace as Sentinel. This lets you correlate playbook failures with incident data using KQL joins: essential for building an “automation health” workbook.

Step 10 · Build a Remediation Evidence Collector

The final step completes the automation loop by writing all actions taken back to the Sentinel incident as structured comments. This creates an auditable evidence trail for compliance, post-incident review, and SOC metrics reporting.

Add the “Add comment to incident” Action

  • At the end of your playbook (after all loops complete), add a new step
  • Search Microsoft Sentinel → select Add comment to incident
  • Set Incident ARM id to the dynamic content from the trigger
  • Build a structured comment using HTML formatting

Structured Evidence Comment Template

<h3>?? Automated Response Summary</h3>
<p><strong>Playbook:</strong> Sentinel-IncidentEnrich<br>
<strong>Execution Time:</strong> @{utcNow()}<br>
<strong>Run ID:</strong> @{workflow().run.name}</p>

<h4>?? IP Enrichment Results</h4>
@{variables('EnrichmentSummary')}

<h4>?? Containment Actions</h4>
<ul>
  <li>Account disabled: @{variables('DisabledAccounts')}</li>
  <li>Sessions revoked: @{variables('RevokedSessions')}</li>
</ul>

<h4>?? Notification</h4>
<ul>
  <li>Teams notification posted to SOC-Alerts channel</li>
</ul>

<p><em>All actions completed successfully. Incident ready for analyst review.</em></p>

Update Incident Severity and Tags

Add one final action to update the incident with a tag indicating automated response was applied:

  • Add step: Microsoft SentinelUpdate incident
  • Set Labels to: auto-enriched, auto-contained
  • Optionally set Owner to the SOC team’s shared account for assignment tracking

KQL Query: Measure Automation Effectiveness

After running the playbook for a few days, use this KQL query to measure the impact on MTTR:

// Measure SOAR automation effectiveness: MTTR comparison
// PURPOSE: Quantify the impact of automated playbooks on incident response times
// WHY: This data justifies SOAR investment to leadership and identifies automation gaps
// Run after the playbook has been active for at least 7-30 days for meaningful data
SecurityIncident
| where TimeGenerated > ago(30d)                  // Last 30 days of incidents
| where Labels has "auto-contained"               // Filter to auto-responded incidents
| extend MTTR = datetime_diff('minute', ClosedTime, CreatedTime)  // Minutes to resolution
| summarize
    TotalIncidents     = count(),                  // Total incidents in the period
    AvgMTTR_Minutes    = avg(MTTR),                // Average time to resolve (minutes)
    MedianMTTR_Minutes = percentile(MTTR, 50),     // Median - less skewed by outliers
    P95_MTTR_Minutes   = percentile(MTTR, 95),     // 95th percentile - worst-case response
    AutoContained      = countif(Labels has "auto-contained"),  // Auto-handled count
    ManualOnly         = countif(Labels !has "auto-contained")  // Manual-only count
| project
    TotalIncidents,
    AvgMTTR_Minutes    = round(AvgMTTR_Minutes, 1),
    MedianMTTR_Minutes = round(MedianMTTR_Minutes, 1),
    P95_MTTR_Minutes   = round(P95_MTTR_Minutes, 1),
    AutomationRate     = round(100.0 * AutoContained / TotalIncidents, 1)  // % automated
// Expected output: MTTR metrics and automation coverage percentage
// Target: MedianMTTR < 15 min, AutomationRate > 60%
💡 Pro Tip: Build a Sentinel Workbook that visualizes your automation metrics: playbook success rate, average MTTR before and after automation, top triggered playbooks, and containment action counts. This data is invaluable for justifying SOAR investment to leadership and identifying automation gaps.

Summary

What You Accomplished

  • Understood Sentinel automation architecture: automation rules, playbooks, and Logic Apps
  • Registered the Logic Apps resource provider and configured Sentinel playbook permissions
  • Created a Logic App playbook with the Microsoft Sentinel incident trigger
  • Added threat intelligence and geolocation enrichment for IP entities
  • Posted formatted adaptive card notifications to a Microsoft Teams SOC channel
  • Automated account isolation: disabled users and revoked sessions via Microsoft Graph
  • Created an automation rule to trigger the playbook on high-severity incidents
  • Tested end-to-end with a simulated incident and validated all actions
  • Monitored playbook run history and implemented retry policies for resilience
  • Built a remediation evidence collector that writes an audit trail back to the incident

💰 Cost Estimation

  • Logic App (Consumption): ~$0.000025 per action execution; a 10-action playbook running 100 times/day costs ~$0.75/month
  • Microsoft Sentinel: Automation rules and playbook triggers have no additional cost beyond standard Sentinel pricing
  • Microsoft Graph API: No additional cost for user management operations (included with Entra ID licensing)
  • Estimated lab cost: Under $5 for the duration of this lab if cleaned up promptly

🧹 Cleanup

# Cleanup: Re-enable the test user account after testing
# IMPORTANT: Do this immediately after testing - don’t leave test accounts disabled
Update-MgUser -UserId "test-user-object-id" -AccountEnabled:$true

# Delete the Logic App playbook (optional - keep if continuing to Lab 04)
# --yes : Skip confirmation prompt
az logic workflow delete --resource-group $RG --name "Sentinel-IncidentEnrich" --yes

# Delete the automation rule via REST API
# This removes the trigger that fires the playbook on new incidents
az rest --method DELETE \
  --url "https://management.azure.com/subscriptions/$SUB_ID/resourceGroups/$RG/providers/Microsoft.OperationalInsights/workspaces/$WORKSPACE/providers/Microsoft.SecurityInsights/automationRules/auto-enrich-contain?api-version=2024-03-01"

# Remove Graph API permission from the managed identity (optional cleanup)
# Replace ASSIGNMENT-ID with the value from: Get-MgServicePrincipalAppRoleAssignment
Remove-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $MIObjectId -AppRoleAssignmentId "ASSIGNMENT-ID"

🚀 Next Steps

  • Next Lab: Enterprise Threat Hunting with MITRE ATT&CK
  • Build additional playbooks for email containment (quarantine phishing messages via MDO)
  • Create approval-based playbooks with Teams adaptive card responses for human-in-the-loop workflows
  • Integrate with ITSM tools (ServiceNow, Jira) to auto-create tickets from Sentinel incidents
  • Deploy playbooks via ARM templates or Bicep for infrastructure-as-code management
  • Build a SOAR metrics workbook to track automation coverage and effectiveness
💡 Pro Tip: Microsoft provides a community repository of Sentinel playbooks on GitHub. Before building from scratch, check if a community playbook exists for your use case: you can deploy it directly and customize it to fit your environment.

📚 Documentation Resources

ResourceDescription
Automate threat response with playbooksOfficial guide to creating and managing playbooks in Microsoft Sentinel
Automation in Microsoft SentinelOverview of automation rules, playbooks, and SOAR capabilities
Azure Logic Apps overviewComprehensive documentation for the Logic Apps workflow engine
Tutorial: Respond to threats with playbooksStep-by-step tutorial for building your first Sentinel playbook
Automate incident handling with automation rulesCreate and manage automation rules for incident triage
Authenticate playbooks to SentinelManaged identity and API connection authentication options
Microsoft Graph: Update userAPI reference for disabling user accounts programmatically
Microsoft Graph: Revoke sign-in sessionsAPI reference for invalidating all user refresh tokens
← Previous Lab Next Lab →