Foreach
The foreach step iterates over an array and runs its nested steps once for each item in the array.
Use the following parameters to configure a foreach step:
| Parameter | Required | Description |
|---|---|---|
name |
Yes | Unique step identifier |
type |
Yes | Step type - must be foreach |
foreach |
Yes | A template or JSON expression that evaluates to an array |
steps |
Yes | An array of steps to run for each iteration |
if |
No | KQL expression that skips the entire loop when it evaluates to false |
max-iterations |
No | Maximum number of iterations to run. Use a number or an object with limit and on-limit. |
timeout |
No | Timeout for the entire loop |
on-failure |
No | Loop-level failure handling policy. Supports the same continue, retry, and fallback options as other steps. |
iteration-timeout |
No | Timeout for each individual iteration |
iteration-on-failure |
No | Per-iteration failure handling policy. Supports continue, retry, and fallback without failing the whole loop. |
steps:
- name: loopStep
type: foreach
foreach: <array expression>
max-iterations:
limit: 100
on-limit: fail
steps:
# Steps to run for each item
# Current item is available as 'foreach.item'
Inside the loop, the current item is always available as foreach.item. You cannot customize this variable name.
The foreach field supports the following expression types:
Use {{ }} or ${{ }} syntax when the array comes from context variables such as step outputs, inputs, or constants. Both syntaxes behave identically for foreach:
foreach: "{{ steps.getData.output.items }}"
foreach: "${{ steps.getData.output.items }}"
Use a plain JSON array string for static arrays known at definition time:
foreach: '["item1", "item2", "item3"]'
Use a JSON string containing {{ }} template expressions for dynamically built arrays with a known structure:
foreach: '[{{ steps.getCount }}, {{ steps.getCount | plus: 1 }}]'
Avoid using plain property paths without template syntax (for example, foreach: 'consts.items'). Use foreach: "{{ consts.items }}" instead.
The workflow engine automatically provides the following variables during foreach iteration. To use these variables, reference them in your step parameters with {{ }} syntax:
| Variable | Description |
|---|---|
foreach.item |
Current item in the iteration |
foreach.index |
Zero-based index of the current iteration |
foreach.total |
Total number of items in the array |
foreach.items |
Complete array being iterated over |
Example:
message: "Processing {{ foreach.item.name }} ({{ foreach.index | plus: 1 }}/{{ foreach.total }})"
Nested foreach loops can access parent context using step references:
steps:
- name: outer-foreach
type: foreach
foreach: "{{ outerItems }}"
steps:
- name: inner-foreach
type: foreach
foreach: "{{ innerItems }}"
steps:
- name: log-both
type: console
with:
message: "Outer: {{ steps.outer-foreach.index }}, Inner: {{ foreach.index }}"
Template expressions support bracket notation for keys that contain dots or other special characters:
"{{ foreach.item['service.name'] }}"
Use loop-level guardrails to control the foreach step as a whole:
max-iterationscaps how many items the loop processes and defaults to 2000 withon-limit: continue, so a larger collection is silently truncated unless you raise the limit or seton-limit: fail. A bare number is shorthand for{ limit: N, on-limit: continue }; use the object form withon-limit: failto fail the workflow when the cap is reached.timeoutlimits the total time spent in the loop, across all iterations.on-failuredefines loop-level failure handling with the samecontinue,retry, andfallbackoptions used by other steps.ifskips the entire loop when the condition evaluates to false.
Use iteration-level guardrails to control each pass through the loop:
iteration-timeoutlimits how long one iteration can run.iteration-on-failurehandles failures for one iteration withcontinue,retry, orfallbackwithout failing the whole loop.
steps:
- name: processAlerts
type: foreach
foreach: "${{ event.alerts }}"
if: "inputs.process_alerts : true"
max-iterations:
limit: 100
on-limit: fail
timeout: "10m"
iteration-timeout: "30s"
iteration-on-failure:
continue: true
steps:
- name: logAlert
type: console
with:
message: "Processing alert {{ foreach.item._id }}"
This example searches for documents and enriches each result with metadata:
name: National Parks Enrichment
description: Enrich each park with additional data
steps:
- name: searchAllParks
type: elasticsearch.search
with:
index: national-parks-index
size: 100
query:
match_all: {}
- name: enrichEachPark
type: foreach
foreach: "{{ steps.searchAllParks.output.hits.hits }}"
steps:
- name: logProcessing
type: console
with:
message: "Processing park: {{ foreach.item._source.title }}"
- name: addMetadata
type: elasticsearch.update
with:
index: national-parks-index
id: "{{ foreach.item._id }}"
doc:
last_processed: "{{ execution.startedAt }}"
workflow_run: "{{ execution.id }}"
category_uppercase: "{{ foreach.item._source.category | upcase }}"