<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[Tinsae Erkailo - Elastic Security Labs]]></title>
    <description><![CDATA[Trusted security news & research from the team at Elastic.]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[Tinsae Erkailo - Elastic Security Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte2c6b841aff36df4/6a88d9784acc96e3f324863d/security-labs-thumbnail.png</url>
      <link>https://www.elastic.co/security-labs/author/tinsae-erkailo</link>
    </image>
    <link>https://www.elastic.co/security-labs/author/tinsae-erkailo</link>
    <atom:link href="https://www.elastic.co/security-labs/rss/author/tinsae-erkailo.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[en]]></language>
    <lastBuildDate>Tue, 22 Sep 2026 13:12:47 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Elastic Workflows GA: automation where your security data already lives]]></title>
    <description><![CDATA[Elastic Workflows is generally available in 9.4, bringing production-ready security automation with deeper case management integration, human-in-the-loop support, natural language authoring, and more.]]></description>
    <content:encoded><![CDATA[<p>Elastic Workflows is generally available in 9.4. It is the automation layer built directly into Elastic, running where your data lives across Security, Observability, and Search. While this post focuses on a security deep dive, the same workflow capabilities apply across solutions, with no separate platform to deploy and no data to move. When an alert fires or a schedule triggers, a Workflow executes: querying Elasticsearch, enriching with threat intel, creating cases, calling external APIs, and notifying your team. Define it in YAML or describe it in natural language and let AI generate the workflow.</p>
<p>The <a href="https://www.elastic.co/security-labs/security-automation-with-elastic-workflows">9.3 Tech Preview</a> introduced the foundation for native automation in Elastic. 9.4 brings it to general availability with production stability and significantly expanded capabilities. Case management gets 25 dedicated automation steps covering the full lifecycle. Human-in-the-loop becomes a first-class Workflow primitive. Natural language authoring moves to Tech Preview. The platform gains more flow-control primitives (<code>while</code>, <code>switch</code>, iteration control), data-transformation steps for working with collections, deeper AI integration with <a href="https://www.elastic.co/elasticsearch/agent-builder">Agent Builder</a>, and broader event-driven triggers. Production-ready automation across Elastic.</p>
<h2 id="caseautomationatscale">Case automation at scale</h2>
<p>The biggest addition for security teams is case management. In the Tech Preview, working with cases involved four generic steps: creating, retrieving, updating, and commenting. Anything beyond that required raw API calls.</p>
<p>Now there are 25 dedicated <code>cases.*</code> steps covering the full lifecycle: create, find, find similar, update, close, assign, unassign, add alerts, add observables, add comments, add tags, set severity, set status, and more. Each step is typed, validated, and appears in the YAML editor's autocomplete, which means natural-language authoring can generate them accurately, too.</p>
<p>Here's what a realistic triage workflow looks like. An alert fires. The Workflow checks whether a case already exists for this alert. If not, it creates one, attaches the alert and observables, assigns the on-call analyst, and routes severity based on risk score:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3ce7eb6b0b37ec66/6a7d7fa9e3a219300199c6d7/image3.png" alt="" /></p>
<p>The code below shows an example of a workflow described using YAML:</p>
<pre><code>name: Triage Workflow
enabled: true
triggers:
  - type: alert

steps:
  - name: check_existing
    type: cases.getCasesByAlertId
    with:
      alert_id: "{{ event.alerts[0]._id }}"

  - name: route
    type: if
    condition: "steps.check_existing.output : ''"
    steps:
      - name: update_existing
        type: cases.addComment
        with:
          case_id: "{{ steps.check_existing.output[0].id }}"
          comment: |
            Correlated alert: {{ event.rule.name }}
            Source: {{ event.alerts[0].source.ip | default: "unknown" }}
            Risk score: {{ event.alerts[0].kibana.alert.risk_score }}
    else:
      - name: create_case
        type: cases.createCase
        with:
          owner: securitySolution
          title: "{{ event.rule.name }} - {{ event.alerts[0].host.name }}"
          description: |
            Auto-created from detection rule: {{ event.rule.name }}
            Host: {{ event.alerts[0].host.name }}
            Source IP: {{ event.alerts[0].source.ip | default: "N/A" }}
          severity: high
          tags:
            - auto-triage
            - "{{ event.rule.name }}"

      - name: attach_evidence
        type: cases.addAlerts
        with:
          case_id: "{{steps.create_case.output.case.id}}"
          alerts:
            - alertId: "{{ event.alerts[0]._id }}"
              index: "{{ event.alerts[0]._index }}"

      - name: add_observables
        type: cases.addObservables
        with:
          case_id: "{{steps.create_case.output.case.id}}"
          observables:
            - typeKey: observable-type-ipv4
              value: "{{ event.alerts[0].source.ip }}"
            - typeKey: observable-type-file-hash
              value: "{{ event.alerts[0].file.hash.sha256 }}"
        on-failure:
          continue: true
</code></pre>
<p>The Workflow automatically handles deduplication, evidence attachment, observable enrichment, graceful handling of missing fields, and analyst assignment. The analyst opens Kibana and sees a case with all the context already there.</p>
<p>The 25 new <code>cases.*</code> steps include create, find, find similar, update, close, delete, assign, unassign, add/remove alerts, add/remove observables, add/update/delete comments, add/remove tags, set severity, set status, get by ID, get by alert ID, and more for custom fields, user actions, and metrics. Each step is typed and validated. If you're using natural language authoring, the AI can generate them accurately because they're part of the schema.</p>
<p>As Workflows mature, you'll see more domain-specific steps for detection rule management, endpoint response actions, and threat intelligence operations.</p>
<h2 id="humancheckpointsinautomatedworkflows">Human checkpoints in automated Workflows</h2>
<p>Case automation handles the mechanical work, but not every decision should be fully automated. AI can classify an alert and gather context, but the analyst should decide whether to escalate. The question is how much mechanical work happens before they make that call.</p>
<p><code>waitForInput</code> pauses a Workflow for human judgment. The Workflow runs the investigation, gathers evidence, classifies the alert with AI, and stops. It presents structured findings and waits. The analyst reviews, approves or redirects, and adds notes, and then the Workflow resumes based on their input.</p>
<p>As the following image shows, the automation handles the investigation, but the analyst makes the decision before the automation executes it.<br />
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc9bc4c8a61dab88f/6a7d7facea068d114ff071f0/image1.png" alt="" /><br />
<em>Classifies incoming alerts with AI, pauses for analyst review and approval, and only escalates approved cases by creating a high-severity incident with context from both the model and the analyst.</em></p>
<pre><code>name: HITL Example
enabled: true
triggers:
  - type: alert

steps:
  - name: classify
    type: ai.classify
    connector-id: Anthropic-Claude-Sonnet-4-6
    with:
      includeRationale: true
      input: ${{ event.alerts[0] }}
      categories:
        - true_positive
        - false_positive
        - needs_investigation

  - name: approval_gate
    type: waitForInput
    with:
      message: |
        Alert: {{ event.rule.name }}
        Classification: {{ steps.classify.output.category }}
        Rationale: {{ steps.classify.output.rationale }}
        Review the classification and approve to escalate.
      schema:
        type: object
        properties:
          approved:
            type: boolean
            title: Approve escalation
          notes:
            type: string
            title: Analyst notes
        required:
          - approved

  - name: escalate
    type: if
    condition: "steps.approval_gate.output.approved : true"
    steps:
      - name: create_escalated_case
        type: cases.createCase
        with:
          owner: securitySolution
          title: "[Escalated] {{ event.rule.name }}"
          description: |
            Escalated by analyst after AI classification.
            Classification: {{ steps.classify.output.category }}
            Notes: {{ steps.approval_gate.output.notes }}
          severity: high
          tags:
            - escalated
            - analyst-reviewed
</code></pre>
<p>Today, <code>waitForInput</code> works through the Kibana execution view and the REST API. Slack, Teams, and email delivery channels are coming so analysts can review and approve without switching context. This is especially important for workflows defined as tools in Agent Builder, where agent-driven investigations benefit from human checkpoints before taking action.</p>
<h2 id="buildingworkflowsfromnaturallanguage">Building Workflows from natural language</h2>
<p>With the automation patterns in place, the next challenge is making them accessible. We chose YAML as the Workflow language because it's declarative, reviewable, and portable. But it was also a strategic choice: YAML is structured text, and large language models are very good at generating structured text. A well-typed workflow schema is an ideal target for AI-powered authoring. In 9.4, that bet pays off. Natural language authoring is available in Tech Preview.</p>
<p>Inside the Workflow editor, describe what should happen: "When a malware alert fires, check the file hash against VirusTotal, create a high-severity case, attach the alert and observables, and notify the SOC channel." The AI assistant generates the YAML. You review, refine, and deploy.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d94f6a7b248e052/6a7d7fb05588ad4296ee42bc/image2.png" alt="" /><br />
The YAML is fully inspectable and editable. If you know what should happen but aren't a YAML expert, this gets you from intent to a working workflow. If you are a YAML expert, you can start in natural language and refine in the editor.</p>
<p>The authoring experience will keep evolving. Visual editing is a complementary mode. Authoring that extends into Slack and other tools your team uses. The goal is to meet security teams wherever they work.</p>
<h2 id="composableworkflows">Composable Workflows</h2>
<p>As your automation library grows, organization becomes critical. Security automation gets complex fast. You start with a single triage workflow, then realize different alert types need different investigation steps. You add conditionals, then more conditionals, and suddenly your workflow is hundreds of lines with nested logic that's hard to follow and harder to change.</p>
<p><code>workflow.execute</code> lets you build reusable workflows with typed inputs and outputs. Instead of embedding all your investigation logic in one place, you create focused workflows for specific scenarios and compose them together. Update your malware investigation workflow once, and every workflow that calls it benefits. The logic stays organized, changes stay contained, and your automation scales without becoming brittle.</p>
<p>Here's what this looks like in practice. Classify the alert, then route to the specialized workflow based on the threat type:</p>
<pre><code>steps:
  - name: dispatch
    type: switch
    expression: "{{ steps.classify.output.category }}"
    cases:
      - match: malware
        steps:
          - name: run_malware
            type: workflow.execute
            with:
              workflow-id: ${{ consts.malware_workflow_id }}
              inputs:
                alert: ${{ event.alerts[0] }}
      - match: phishing
        steps:
          - name: run_phishing
            type: workflow.execute
            with:
              workflow-id: ${{ consts.phishing_workflow_id }}
              inputs:
                alert: ${{ event.alerts[0] }}
    default:
      - name: run_generic
        type: workflow.execute
        with:
          workflow-id: ${{ consts.generic_triage_id }}
          inputs:
            alert: ${{ event.alerts[0] }}
</code></pre>
<p>Each workflow is independently testable. Most teams start with a single workflow and extract sub-workflows as patterns emerge. Composition is something you grow into.</p>
<p>The template library will eventually move into the product so you can discover and install pre-built workflows directly in Kibana.</p>
<h2 id="whereanalystsalreadywork">Where analysts already work</h2>
<p>Workflows are most useful when they're available where decisions get made. The primitives and patterns are in place, but automation only delivers value if analysts can reach it without switching tools. We're investing in making Workflows accessible throughout the security analyst experience, starting with the alerts table and Attack Discovery. Analysts can send alerts or entire attacks directly to a Workflow, triggering the investigation logic they've built without leaving their current context. This integration will continue expanding so that Workflow automation becomes a seamless part of the analyst's daily work, not a separate destination.</p>
<p>"Run workflow" in the alerts table lets you right-click an alert (or select multiple) and trigger a Workflow directly. The alert context passes automatically.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd56758ded66462cf/6a7d7fb333fa8a893f1ff955/image5.png" alt="" /></p>
<p>This is the beginning. Workflows will continue expanding into more parts of the security experience, making automation accessible where analysts need it.</p>
<h2 id="morecontrolmoreflexibility">More control, more flexibility</h2>
<p>Beyond the major features, the Workflow language itself has become more capable. New flow control primitives give you cleaner ways to handle complex logic. <code>while</code> loops let you poll for conditions or wait for external state changes. <code>switch</code> provides multi-way branching so you can route alerts by category without nested conditionals. <code>loop.break</code> and <code>loop.continue</code> give you standard iteration control.</p>
<p>Step-level data transformation steps handle filtering, finding, aggregating, and JSON operations on collections, so you can process alert lists, IOC lookups, and enrichment responses without custom scripting.</p>
<p>The AI steps (<code>ai.prompt</code>, <code>ai.classify</code>, <code>ai.summarize</code>, <code>ai.agent</code>) are more stable and now support structured outputs, making it easier to use AI-generated classifications and summaries in downstream workflow logic.</p>
<p>LiquidJS templating expanded too. You can now set variables and access them throughout the workflow, making it easier to reference computed values across multiple steps.</p>
<h2 id="reliabilityandproductioncontrols">Reliability and production controls</h2>
<p>All of this is useful only if it's reliable. Every step supports <code>on-failure</code> configuration: retry for transient failures, continue when a step isn't critical, and abort when downstream steps depend on the result. Error details are available at <code>steps.&lt;step-id&gt;.error</code> to help route around failures.</p>
<p>Concurrency controls prevent alert storms from spawning hundreds of parallel executions. Loop guardrails prevent runaway execution. Composition depth limits prevent infinite recursion.</p>
<p>Elastic 9.4 introduces event-driven triggers, starting with <code>workflows.failed</code>. When a workflow execution fails, it can trigger a separate handler workflow. Build a notification workflow that alerts your team when automation goes down. More trigger types are coming: case status changes, alert state transitions, and detection rule updates, making Workflows reactive to what's happening across your security environment.</p>
<p>Every execution is recorded in execution history with step-by-step results, including analyst decisions from <code>waitForInput</code> steps. This is your debugging tool and your audit trail.</p>
<p>Workflows is enabled by default in 9.4 with an Enterprise license. Granular RBAC controls who creates, edits, executes, and views workflows. Every management operation writes to the security audit log. Import/export moves Workflows between environments with connector references intact.</p>
<h2 id="licensingandpricing">Licensing and pricing</h2>
<p>Workflows is available with an Enterprise license on Elastic Cloud Hosted and self-managed deployments, and with the Complete tier on Elastic Cloud Serverless for Security projects.</p>
<p>Version 9.4 introduces a unified execution-based pricing model across all deployment types.</p>
<p>Across Serverless, Elastic Cloud Hosted, and self-managed, pricing is based on workflow executions, with volume-based discounts at scale. <strong>Each month includes a baseline allocation of workflow executions that are not billed</strong>. Usage beyond this allocation is billed per execution, with volume-based discounts applied as usage increases.</p>
<p><strong>Elastic Cloud Serverless</strong> begins applying this model on May 1, 2026. Usage beyond the monthly non-billed executions is charged per execution, with discounts at higher volumes. <a href="https://www.elastic.co/pricing/serverless-security">See full pricing details</a>.</p>
<p><strong>Elastic Cloud Hosted and self-managed</strong> deployments follow the same pricing model, <strong>but are currently in a promotional period</strong> with execution charges not yet applied. This allows teams to build and run Workflows, establish usage patterns, and prepare for the transition to execution-based billing in a future release.</p>
<p>Start building now. The Workflows you create today will continue to run as pricing transitions to the unified model.</p>
<h2 id="gettingstarted">Getting started</h2>
<p>Workflows are enabled by default in 9.4. Open Kibana, navigate to Workflows, and start building.</p>
<p>If you're new to Workflows, the <a href="https://www.elastic.co/security-labs/security-automation-with-elastic-workflows">Tech Preview post</a> walks through building your first triage Workflow. The <a href="https://www.elastic.co/security-labs/speeding-apt-attack-discovery-confirmation-with-attack-discovery-workflows-and-agent-builder">Chrysalis APT workflow</a> shows Agent Builder integration in action. The <a href="https://www.elastic.co/docs/explore-analyze/workflows">documentation</a> has the complete step type reference. The <a href="https://github.com/elastic/workflows">Elastic Workflow Library</a> has ready-to-use templates.</p>
<p>Start with the Workflow that would save your team the most time this week. If you can describe it, you can build it.</p>
<hr />
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/security-labs/blog/elastic-workflows-ga-9-4</link>
    <guid isPermaLink="false">elastic-workflows-ga-9-4</guid>
    <category><![CDATA[AI & Automation]]></category>
    <dc:creator><![CDATA[Tinsae Erkailo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt849a685ddd18f030/6a7d7fb7b437704d8b4d3f25/elastic-workflows-ga-9-4.webp" length="0" type="image/webp"/>
    <pubDate>Tue, 05 May 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Security Automation with Elastic Workflows: From Alert to Response]]></title>
    <description><![CDATA[A practical guide to building intelligent, automated security playbooks with Elastic Workflows.]]></description>
    <content:encoded><![CDATA[<h2 id="thedailyloop">The daily loop</h2>
<p>An alert fires. You open it. You read through the details. You gather context from the surrounding activity. You check for related signals across your environment. You decide what it means and what to do next. Sometimes you escalate. Sometimes you close it and move on.</p>
<p>You do this dozens of times a day. The steps are almost always the same. The data you need is already in your SIEM. The actions you take are predictable. But the work is still manual.</p>
<p>This is the kind of work that automation should handle. Not because it's hard, but because it's repetitive, and every minute spent on repetitive manual triage is a minute not spent on the alerts that actually need a human.</p>
<p>Elastic Workflows brings that automation into the SIEM itself. No separate tool. No integration to build. Your detection rule fires, and a workflow runs, with direct access to your alerts, cases, and security data.</p>
<p>This blog post walks through building a security playbook with Workflows, step by step. We'll start simple and build up to a workflow that runs when an alert fires, checks threat intel, gathers context, creates cases, notifies the team, and brings in AI when the investigation calls for it.</p>
<p>If you're new to Workflows, the <a href="https://www.elastic.co/search-labs/blog/elastic-workflows-automation">introductory technical deep dive</a> blog and <a href="https://www.youtube.com/watch?v=Tu505Zn1wUc">video</a> cover the core concepts of Workflows. This post focuses on applying these concepts in a security context.</p>
<h2 id="quickorientation">Quick orientation</h2>
<p>Workflows are YAML definitions that run inside Kibana. You define what should happen, and the platform handles execution. At a high level, a workflow is composed of three main parts: triggers (when it runs), steps (what it does), and data flow (how information moves between steps).</p>
<p><a href="https://www.elastic.co/docs/explore-analyze/workflows/triggers"><strong>Triggers</strong></a> decide when the workflow runs. An alert trigger runs on a detection. A scheduled trigger runs on a cadence. A manual trigger runs on demand. A workflow can have more than one.</p>
<p><a href="https://www.elastic.co/docs/explore-analyze/workflows/steps"><strong>Steps</strong></a> define what the workflow does. They run in order and can use outputs from earlier steps. They can query data in <a href="https://www.elastic.co/docs/explore-analyze/workflows/steps/elasticsearch">Elasticsearch</a>, update alerts and cases in <a href="https://www.elastic.co/docs/explore-analyze/workflows/steps/kibana">Kibana</a>, and <a href="https://www.elastic.co/docs/explore-analyze/workflows/steps/external-systems-apps">call external systems</a> like sending a Slack message or scanning a hash on VirusTotal. They can also apply logic such as conditionals or loops, and use <a href="https://www.elastic.co/docs/explore-analyze/workflows/steps/ai-steps">AI</a> for tasks like summarizing text, prompting an LLM, or invoking agents when deeper reasoning is needed.</p>
<p>This is the toolkit. With these primitives, you can build workflows that take a signal, gather context, and drive a response.</p>
<h2 id="buildingasecurityplaybook">Building a security playbook</h2>
<p>We'll build an alert triage workflow incrementally. Each section adds a capability, and by the end, you'll have a working playbook that handles the full triage loop.</p>
<h3 id="startwiththetrigger">Start with the trigger</h3>
<p>Security workflows start with an event. It could be an alert, a case update, a user action, or a scheduled check. The workflow takes that signal, gathers context, and decides what to do next.</p>
<p>We’ll start with alert triage. It’s the most common path, and it shows the full loop end to end. Each section adds a capability, and by the end, you’ll have a working playbook.</p>
<p>Here’s a minimal workflow with an alert trigger:</p>
<pre><code>name: Alert Triage Playbook
description: Enriches alerts, checks threat intel, creates a case, and notifies the team.
enabled: true
tags:
  - security
  - triage

triggers:
  - type: alert

steps:
  # we'll build these out
</code></pre>
<p>The <code>alert</code> trigger connects this workflow to detection rules. You link a specific rule to this workflow from the rule's <strong>Actions</strong> settings in Kibana. When the rule fires, the workflow runs and receives the full alert context through the <code>event</code> variable. That includes <code>event.alerts</code> (the alert documents), <code>event.rule</code> (the rule metadata), and every field on the alert.</p>
<p>From here, you start adding steps.</p>
<h3 id="checkthreatintel">Check threat intel</h3>
<p>The first real step: take the file hash from the alert and check it against VirusTotal. Workflows have a built-in VirusTotal connector, so you don't need to construct HTTP requests or manage API keys in your YAML (connector credentials like VirusTotal API keys or Slack tokens are configured once in the connector under <strong>Stack Management &gt; Connectors</strong>):</p>
<pre><code>  - name: check_virustotal
    type: virustotal.scanFileHash
    connector-id: "my-virustotal"
    with:
      hash: "{{ event.alerts[0].file.hash.sha256 }}"
    on-failure:
      retry:
        max-attempts: 2
        delay: 3s
      continue: true
</code></pre>
<p>Every step in a workflow follows a simple, consistent structure. It starts with a <code>name</code>, which gives the step a clear identity, and a <code>type</code>, which defines the action being performed. In this case, the step calls the VirusTotal file hash scan capability. Because this is a connector-backed action, it also includes a <code>connector-id</code>, which tells the workflow which configured integration to use, including its credentials.</p>
<p>The <code>with</code> block is where you pass inputs into the step. Each step type defines the parameters it accepts. Here, you provide the file hash to scan. Rather than hardcoding values, workflows use a built-in templating engine powered by LiquidJS. The <code>{{ }}</code> syntax lets you <a href="https://www.elastic.co/docs/explore-analyze/workflows/data#workflows-dynamic-values">reference data from the execution context</a>, so the hash is pulled directly from the alert that triggered the workflow.</p>
<p>Finally, the <code>on-failure</code> block defines how the step behaves if something goes wrong. In this case, it retries twice with a short delay and continues execution even if the lookup fails. This is important in production workflows, where a transient external API issue should not block the entire triage process.</p>
<h3 id="gathercontextwithesql">Gather context with ES|QL</h3>
<p>Next, query for related alerts on the same host. ES|QL runs directly against your security indices, so there's no API bridging or credential management:</p>
<pre><code>  - name: related_alerts
    type: elasticsearch.esql.query
    with:
      query: |
        FROM .alerts-security*
        | WHERE host.name == "{{ event.alerts[0].host.name }}"
        | WHERE @timestamp &gt; NOW() - 24 hours
        | STATS
            alert_count = COUNT(*),
            rules_triggered = VALUES(kibana.alert.rule.name),
            users_involved = VALUES(user.name)
      format: json
</code></pre>
<p>This tells you whether the host has been generating other alerts, which rules triggered, and which users were involved. That context is included in the case description and informs the severity assessment later.</p>
<p>The same approach works for any enrichment that touches data in Elasticsearch: looking up a user's first-seen date, checking how many times a hash has appeared in your logs, or pulling the process tree from endpoint data. If the data is in your cluster, ES|QL can get it.</p>
<h3 id="branchonfindings">Branch on findings</h3>
<p>Now the workflow needs to decide what to do. If VirusTotal flagged the file as malicious, create a case and respond. If not, close the alert as a false positive:</p>
<pre><code>  - name: check_malicious
    type: if
    condition: steps.check_virustotal.output.stats.malicious &gt; 5
    steps:
      # true positive path: steps below
    else:
      - name: close_false_positive
        type: kibana.SetAlertsStatus
        with:
          status: closed
          reason: false_positive
          signal_ids:
            - "{{ event.alerts[0]._id }}"
</code></pre>
<p>The <code>if</code> step evaluates a condition and runs different steps depending on the result. The false positive path closes the alert in a single step. The true positive path continues below.</p>
<h3 id="createacase">Create a case</h3>
<p>When the alert is confirmed malicious, open a case with context from previous steps:</p>
<pre><code>      - name: create_case
        type: kibana.createCase
        with:
          title: "Malware Detected: {{ event.alerts[0].file.hash.sha256 }}"
          description: |
            Confirmed malicious file detected on {{ event.alerts[0].host.name }}.

            **Detection:** {{ event.rule.name }}
            **User:** {{ event.alerts[0].user.name }}
            **VirusTotal:** {{ steps.check_virustotal.output.stats.malicious }} engines flagged this file
            **Related alerts (24h):** {{ steps.related_alerts.output.values[0][0] }} 
              alerts from {{ steps.related_alerts.output.values[0][1] | size }} rules
          owner: securitySolution
          severity: high
          tags:
            - automation
            - malware
          settings:
            syncAlerts: false
          connector:
            id: none
            name: none
            type: ".none"
            fields: null
</code></pre>
<p><a href="https://www.elastic.co/docs/explore-analyze/workflows/data#workflows-dynamic-values">Liquid templating</a> pulls data from the alert (<code>event</code>), from the VirusTotal results (<code>steps.check_virustotal.output</code>), and from the ES|QL query (<code>steps.related_alerts.output</code>). Every field from every previous step is available to every subsequent step.</p>
<h3 id="notifytheteam">Notify the team</h3>
<p>Send a Slack message so the team knows a confirmed case is open:</p>
<pre><code>      - name: notify_team
        type: slack
        connector-id: "security-alerts"
        with:
          message: |
            Malware confirmed on {{ event.alerts[0].host.name }}.
            VirusTotal: {{ steps.check_virustotal.output.stats.malicious }} detections.
            Case created: {{ steps.create_case.output.id }}
</code></pre>
<p>Slack is one option. Jira, ServiceNow, PagerDuty, Microsoft Teams, email, and Opsgenie are all supported as connector steps.</p>
<h3 id="thecompleteworkflow">The complete workflow</h3>
<p>Here's the full workflow assembled:</p>
<pre><code>name: Alert Triage Playbook
description: Enriches alerts, checks threat intel, creates a case, and notifies the team.
enabled: true
tags:
  - security
  - triage

triggers:
  - type: alert

steps:
  - name: check_virustotal
    type: virustotal.scanFileHash
    connector-id: "my-virustotal"
    with:
      hash: "{{ event.alerts[0].file.hash.sha256 }}"
    on-failure:
      retry:
        max-attempts: 2
        delay: 3s
      continue: true

  - name: related_alerts
    type: elasticsearch.esql.query
    with:
      query: |
        FROM .alerts-security*
        | WHERE host.name == "{{ event.alerts[0].host.name }}"
        | WHERE @timestamp &gt; NOW() - 24 hours
        | STATS
            alert_count = COUNT(*),
            rules_triggered = VALUES(kibana.alert.rule.name),
            users_involved = VALUES(user.name)
      format: json

  - name: check_malicious
    type: if
    condition: steps.check_virustotal.output.stats.malicious &gt; 5
    steps:
      - name: create_case
        type: kibana.createCase
        with:
          title: "Malware Detected: {{ event.alerts[0].file.hash.sha256 }}"
          description: |
            Confirmed malicious file detected on {{ event.alerts[0].host.name }}.

            **Detection:** {{ event.rule.name }}
            **User:** {{ event.alerts[0].user.name }}
            **VirusTotal:** {{ steps.check_virustotal.output.stats.malicious }} engines flagged this file
            **Related alerts (24h):** {{ steps.related_alerts.output.values[0][0] }} 
              alerts from {{ steps.related_alerts.output.values[0][1] | size }} rules
          owner: securitySolution
          severity: high
          tags:
            - automation
            - malware
          settings:
            syncAlerts: false
          connector:
            id: none
            name: none
            type: ".none"
            fields: null

      - name: notify_team
        type: slack
        connector-id: "security-alerts"
        with:
          message: |
            Malware confirmed on {{ event.alerts[0].host.name }}.
            VirusTotal: {{ steps.check_virustotal.output.stats.malicious }} detections.
            Case created: {{ steps.create_case.output.id }}

    else:
      - name: close_false_positive
        type: kibana.SetAlertsStatus
        with:
          status: closed
          reason: false_positive
          signal_ids:
            - "{{ event.alerts[0]._id }}"
</code></pre>
<p>That's the triage loop, automated. Alert fires, threat intel checked, context gathered, decision made, case created, team notified. Every execution is logged and auditable.</p>
<p>This is a starting point. The <a href="https://github.com/elastic/workflows/blob/main/workflows/security/response/traditional-triage.yaml">traditional-triage.yaml</a> in the Elastic Workflows library on GitHub goes further: it isolates the host, looks up the on-call analyst, creates a dedicated Slack channel, assigns the case, and posts a rich incident summary. Same patterns, more steps.</p>
<h2 id="addingaitotheplaybook">Adding AI to the playbook</h2>
<p>The workflow above handles a defined path. If the hash is malicious, do X; otherwise, do Y. That covers a lot of triage work. But not every alert fits a clean branching condition, and not every case description should be a list of raw fields.</p>
<p>Workflows include AI steps that handle the parts where structured logic runs out. There are three, and they work together.</p>
<h3 id="classifyletaidrivethebranching">Classify: let AI drive the branching</h3>
<p>Instead of branching on a VirusTotal score threshold, use <code>ai.classify</code> to categorize the alert. It considers the full alert context, not just a single number:</p>
<pre><code>  - name: classify_alert
    type: ai.classify
    with:
      input: "${{ event }}"
      categories:
        - malware
        - phishing
        - lateral_movement
        - data_exfiltration
        - false_positive
      instructions: |
        Classify this security alert based on the alert details,
        rule name, and affected entities.
      includeRationale: true
</code></pre>
<p>The output is structured: <code>steps.classify_alert.output.category</code> returns a single string like <code>"malware"</code> or <code>"false_positive"</code>. That drives the <code>if</code> condition directly. The rationale explains why, and you can include it in the case for audit purposes.</p>
<h3 id="summarizewritecasedescriptionsthatadapt">Summarize: write case descriptions that adapt</h3>
<p>Rather than templating raw field values into a case description, use <code>ai.summarize</code> to generate a readable overview. Run it once before case creation for the initial description, and once after the agent investigation to update the description with the full picture:</p>
<pre><code>  - name: initial_summary
    type: ai.summarize
    with:
      input: "${{ event }}"
      instructions: |
        Write a one-paragraph overview of this security alert.
        State what was detected, on which host, by which user, and the severity.
        Do not include recommendations. Just the facts.
      maxLength: 300
</code></pre>
<p>The summary adapts to whatever fields are present on the alert, so you don't need to account for every possible field combination in your Liquid templates. Use <code>steps.initial_summary.output.content</code> in the case description and the Slack notification.</p>
<h3 id="agentinvestigatewhattheplaybookcant">Agent: investigate what the playbook can't</h3>
<p>The <code>ai.agent</code> step invokes an Agent Builder agent. Unlike classify and summarize, an agent has access to tools. It can query your indices, check threat intel, correlate signals across data sources, and reason about what it finds:</p>
<pre><code>  - name: escalate_to_agent
    type: ai.agent
    agent-id: "security-agent"
    create-conversation: true
    with:
      message: |
        Investigate this alert. Search for related activity on this host,
        check for persistence mechanisms and lateral movement,
        and determine the full scope of the incident.
        Alert: {{ event | json }}
        Classification: {{ steps.classify_alert.output.category }}
        VirusTotal: {{ steps.check_virustotal.output | json }}
        Related alerts: {{ steps.related_alerts.output | json }}
    timeout: 10m
</code></pre>
<p>The agent processes the input, calls whatever tools it needs, and returns its findings. The workflow waits, then continues with the next steps: adding the investigation to the case, notifying the team, and updating the case description with a concise summary of what the agent found.</p>
<p>Setting <code>create-conversation: true</code> persists the conversation, so the workflow can fetch the agent's reasoning trail and add it to the case as a structured comment with clickable links to each query it ran. And the analyst gets a direct link to pick up the conversation with the agent if they want to dig deeper.</p>
<h3 id="puttingittogether">Putting it together</h3>
<p>In the full version of this workflow, the three AI steps work in sequence:</p>
<ol>
<li><strong>Classify</strong> the alert to drive the triage decision  </li>
<li><strong>Summarize</strong> the alert for the initial case description and Slack notification  </li>
<li><strong>Agent</strong> investigates the full scope: persistence, lateral movement, IOCs, affected systems  </li>
<li><strong>Summarize</strong> again, this time distilling the agent's findings into a concise, updated case description</li>
</ol>
<p>The case starts with a clean factual overview and evolves into a comprehensive summary as the investigation completes. The agent's full analysis and reasoning trail live as case comments for analysts who want the details.</p>
<p>The complete workflow, including the AI investigation pipeline with reasoning trails, clickable Discover links, and follow-up Slack notifications, is available in the <a href="https://github.com/elastic/workflows">Elastic Workflows library on GitHub</a>.</p>
<h2 id="workflowsasagenttools">Workflows as agent tools</h2>
<p>The integration between Workflows and Agent Builder works in both directions. Workflows can call agents (as shown above). And agents can call workflows.</p>
<p>When you expose a workflow as a tool in Agent Builder, an agent can invoke it during a conversation. The agent decides what needs to happen, and the workflow handles the execution reliably and repeatably.</p>
<p>This is the pattern demonstrated in the <a href="https://www.elastic.co/security-labs/speeding-apt-attack-discovery-confirmation-with-attack-discovery-workflows-and-agent-builder">Chrysalis APT blog post</a>: a two-step workflow hands the entire Attack Discovery to an agent, and the agent calls workflow-backed tools to verify malware hashes, search logs, check the on-call schedule, create a case, and spin up a Slack channel. The workflow is the trigger and the safety net. The agent is the brain.</p>
<p>Agents reason. Workflows execute. Together they cover the full range from judgment to action.</p>
<h2 id="openbydesign">Open by design</h2>
<p>Not every team starts from zero. Some already have automation running in Tines, Splunk SOAR, Palo Alto XSOAR, or another platform. Workflows don't ask you to replace any of your existing tools.</p>
<p>The idea is straightforward: use Workflows for the parts of your automation that are native to Elastic. Alert triage, enrichment from your own indices, case management, and alert status updates. These touch your Elastic data directly, and a native workflow will always be simpler and faster than an external tool making API calls back into Elastic.</p>
<p>For everything else, connectors bridge the gap. We have native connectors for Tines, Resilient, Swimlane, TheHive, D3 Security, Torq, and XSOAR. A workflow can kick off a Tines story, push an incident to Resilient, or trigger any external system via HTTP. Your existing tools handle cross-platform orchestration. Workflows handle what's native. As the capability grows, you can consolidate at your own pace. Nobody's forcing a migration.</p>
<h2 id="whatshereandwhatsnext">What's here and what's next</h2>
<p>Workflows is available today. Here's what you can build with it today:</p>
<ul>
<li><strong>Alert triggers</strong> connect workflows to detection and alerting rules  </li>
<li><strong>Case and alert management</strong> through named Kibana steps (<code>kibana.createCase</code>, <code>kibana.SetAlertsStatus</code>, <code>kibana.addCaseComment</code>, and more)  </li>
<li><strong>Direct data access</strong> via Elasticsearch search and ES|QL  </li>
<li><strong>39 workflow-compatible connectors</strong> covering threat intel (VirusTotal, AbuseIPDB, GreyNoise, Shodan, URLVoid, AlienVault OTX), ticketing (Jira, ServiceNow), communication (Slack, Teams, PagerDuty, email), SOAR platforms (Tines, Resilient, Swimlane, TheHive, and others), and AI providers  </li>
<li><strong>AI steps</strong> for classification, summarization, prompts, and Agent Builder invoking Elastic Agents/Skils  </li>
<li><strong>YAML authoring</strong> with autocomplete, validation, and step testing in Kibana  </li>
<li><strong>50+ example workflows</strong> on <a href="https://github.com/elastic/workflows">GitHub</a>, including security-specific templates for detection, enrichment, and response</li>
</ul>
<p>What's coming:</p>
<ul>
<li><strong>Visual workflow builder</strong> for drag-and-drop authoring  </li>
<li><strong>In-product template library</strong> to browse and install workflows directly in Kibana  </li>
<li><strong>Human-in-the-loop</strong> approvals that pause workflows for human input via Slack, email, or the Kibana UI  </li>
<li><strong>Natural language authoring</strong> where AI helps translate intent into working workflows</li>
</ul>
<p>Today, authoring is YAML-based. If you've written detection rules or configured CI/CD pipelines, the learning curve is gentle. The editor has built-in autocomplete, validation, and step testing, and the example library gives you templates to start from. A visual builder is coming to make this accessible to a wider audience.</p>
<h2 id="getstarted">Get started</h2>
<p>Elastic Workflows is available now. To start building:</p>
<ol>
<li><a href="https://cloud.elastic.co/registration">Start an Elastic Cloud trial</a> or enable Workflows in your existing deployment under <strong>Stack Management &gt; Advanced Settings</strong>  </li>
<li>Explore the <a href="https://www.elastic.co/docs/explore-analyze/workflows">Workflows documentation</a>  </li>
<li>Browse the <a href="https://github.com/elastic/workflows">Elastic Workflow Library on GitHub</a> for security templates you can adapt  </li>
<li>Read the <a href="https://www.elastic.co/search-labs/blog/elastic-workflows-automation">introductory technical deep dive</a> for core concepts  </li>
<li>See the <a href="https://www.elastic.co/security-labs/speeding-apt-attack-discovery-confirmation-with-attack-discovery-workflows-and-agent-builder">Chrysalis APT blog</a> for a complete Attack Discovery + Workflows + Agent Builder walkthrough</li>
</ol>
<p>Start with the workflow that would save you the most time tomorrow.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/security-labs/blog/security-automation-with-elastic-workflows</link>
    <guid isPermaLink="false">security-automation-with-elastic-workflows</guid>
    <category><![CDATA[AI & Automation]]></category>
    <dc:creator><![CDATA[Tinsae Erkailo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb99b1122b307cb42/6a7d84106c6eacbfe1f113a9/security-automation-with-elastic-workflows.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 24 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>