If

The if step evaluates a boolean or Kibana Query Language (KQL) expression and runs different steps based on whether the condition is true or false.

Use the following parameters to configure an if step:

Parameter Required Description
name Yes Unique step identifier
type Yes Step type - must be if
condition Yes A boolean or KQL expression to evaluate
steps Yes An array of steps to run if the condition is true
else No An array of steps to run if the condition is false
steps:
  - name: conditionalStep
    type: if
    condition: <boolean or KQL expression>
    steps:
      # Steps to run if condition is true
    else:
      # Steps to run if condition is false (optional)
		

To run or skip one step on its own instead of branching between two sets of steps, set if on that step. Refer to Skip a single step.

The condition field supports the following expression types:

Use ${{ }} syntax when you want the expression to evaluate directly to a boolean value:

steps:
  - name: check-enabled
    type: if
    condition: "${{ inputs.isEnabled }}"
    steps:
      - name: process-enabled
        type: http
    else:
      - name: log-disabled
        type: console
		

If the expression evaluates to undefined, it defaults to false.

Use a string-based condition to evaluate the value as a KQL expression. You can use {{ }} templating to inject dynamic values:

steps:
  - name: check-status
    type: if
    condition: "{{ steps.fetchData.output.status }}: completed"
    steps:
      - name: process-data
        type: http
		

The if step supports the following KQL features:

condition: "status: active"
condition: "user.role: admin"
condition: "isActive: true"
condition: "count: 42"
condition: "users[0].name: Alice"
		
  1. Array index access
condition: "count >= 100"
condition: "count <= 1000"
condition: "count > 50"
condition: "count < 200"
condition: "count >= 100 and count <= 1000"
		
condition: "fieldName:*"
condition: "user.name: John*"
condition: "user.name: *Doe"
condition: "txt: *ipsum*"
condition: "user.name: J*n Doe"
		
  1. Field exists
  2. Starts with
  3. Ends with
  4. Contains
  5. Pattern
condition: "status: active and isEnabled: true"
condition: "status: active or status: pending"
condition: "not status: inactive"
condition: "status: active and (role: admin or role: moderator)"
		
  1. And
  2. Or
  3. Not
  4. Nested
condition: "user.info.name: John Doe"
condition: "steps.fetchData.output.status: completed"
condition: "users[0].name: Alice"
condition: "users.0.name: Alice"
		
  1. Nested property
  2. Deep nesting
  3. Array access
  4. Alternative syntax

This example runs different steps based on the event severity:

steps:
  - name: checkSeverity
    type: if
    condition: "event.severity: 'critical'"
    steps:
      - name: handleCritical
        type: console
        with:
          message: "Critical alert!"
    else:
      - name: handleNormal
        type: console
        with:
          message: "Normal severity"
		

You can write the same check as a boolean expression with ${{ }} syntax, for example condition: "${{ event.severity == 'critical' }}".

This example checks the number of search results and processes them differently based on the count:

name: National Parks Conditional Processing
steps:
  - name: searchParks
    type: elasticsearch.search
    with:
      index: national-parks-index
      size: 100

  - name: checkResultCount
    type: if
    condition: "steps.searchParks.output.hits.total.value > 5"
    steps:
      - name: processLargeDataset
        type: foreach
        foreach: "{{ steps.searchParks.output.hits.hits }}"
        steps:
          - name: processPark
            type: console
            with:
              message: "Processing park: {{ foreach.item._source.title }}"
    else:
      - name: handleSmallDataset
        type: console
        with:
          message: "Only {{ steps.searchParks.output.hits.total.value }} parks found - manual review needed"
		

This example uses multiple logical operators to check a combination of conditions:

steps:
  - name: check-complex
    type: if
    condition: "status: active and (count >= 100 or role: admin)"
    steps:
      - name: process-authorized
        type: http
		

When only one step in a workflow is optional, you don't need a separate if step to control it. Instead, set if directly on that step. This step-level if makes the step run only when its condition is true. When the condition is false, the workflow skips the step and continues with the next one.

For example, the following snippets both run a notify step only when an earlier search step, find_failed_logins, finds at least one match. The first snippet wraps notify in an if step:

- name: check_results
  type: if
  condition: "steps.find_failed_logins.output.hits.total.value > 0"
  steps:
    - name: notify
      type: console
      with:
        message: "Found {{ steps.find_failed_logins.output.hits.total.value }} failed logins."
		

The second snippet sets a step-level if on notify. The result is the same, with one fewer step and one fewer level of nesting:

- name: notify
  type: console
  if: "steps.find_failed_logins.output.hits.total.value > 0"
  with:
    message: "Found {{ steps.find_failed_logins.output.hits.total.value }} failed logins."
		

Use an if step instead when you need an else branch for the false case, or when one condition controls several steps.

A step-level if accepts the same boolean and KQL expressions as the if step's condition.

A skipped step doesn't produce output, so a later reference such as {{ steps.notify.output }} resolves to an empty value. If a later step depends on the skipped step's output, set the same condition on that step too.

A step-level if causes a validation error in the following cases. Each case has an alternative that you can use instead.

An if step already uses condition to decide what runs, so it can't also have a step-level if. For example, an if step can't have both condition: "status: active" and if: "inputs.enabled: true".

Instead, combine both checks in condition with and, for example condition: "status: active and inputs.enabled: true".

Steps inside a parallel branch can't have a step-level if, so a branch can't skip individual steps. For example, you can't skip a GeoIP lookup in one branch when an alert has no source IP.

To run or skip all branches at once, add if to the parallel step instead, for example if: "inputs.enrich: true". To skip steps in only one branch, move those steps into a child workflow, add if to them there, and call the child workflow from the branch with workflow.execute.

For more about what a branch can contain, refer to Branch body limitations.

A step-level if and an if step's condition can each be up to 2,000 characters long. You might reach this limit with a check that lists hundreds of host names joined with or.

Instead, move the long part out of the condition. For example, store the host names as a list in consts, then check the list with if: "${{ consts.watched_hosts contains inputs.host_name }}". You can also calculate a value in an earlier data.set step and check that value instead.