Steps are the building blocks of workflows. Each step performs an action and can pass outputs to subsequent steps.
Run Terraform plan, apply, or destroy operations.
| Field | Required | Description |
|---|---|---|
| Project | Yes | Select from IaC projects |
| Action | Yes | Plan, Apply, or Destroy |
| Auto Approve | No | Skip approval for Apply |
| Variables | No | Override Terraform variables |
| Workspace | No | Terraform workspace |
Variables Format:
{
"instance_type": "t3.large",
"environment": "production"
}
Outputs: outputs, plan_summary, status
Run Ansible playbooks or Kubernetes manifests.
| Field | Required | Description |
|---|---|---|
| Project | Yes | Select config project |
| Target | Yes | Server, group, or cluster |
| Limit | No | Ansible host pattern |
| Tags | No | Run only tagged tasks |
| Check Mode | No | Dry run |
Outputs: changed, failed, summary
Execute custom bash, Python, or Node scripts.
| Field | Required | Description |
|---|---|---|
| Script | Yes | Inline script or file path |
| Shell | No | bash, sh, python, node |
| Environment | No | Environment variables |
| Working Directory | No | Execution path |
| Timeout | No | Max execution time |
Example Script:
#!/bin/bash
echo "Deploying version ${VERSION}"
curl -X POST https://api.example.com/deploy \
-H "Authorization: Bearer ${API_TOKEN}" \
-d '{"version": "'${VERSION}'"}'
Outputs: stdout, stderr, exit_code
Call external APIs or webhooks.
| Field | Required | Description |
|---|---|---|
| URL | Yes | Full URL |
| Method | Yes | GET, POST, PUT, DELETE |
| Headers | No | HTTP headers |
| Body | No | Request body (JSON) |
| Auth | No | None, Basic, Bearer, API Key |
| Success Codes | No | Expected status codes (default: 2xx) |
Headers Example:
{
"Content-Type": "application/json",
"Authorization": "Bearer ${secrets.API_TOKEN}"
}
Outputs: status_code, body, headers
Pause workflow for human approval.
| Field | Required | Description |
|---|---|---|
| Approvers | Yes | Users, teams, or roles |
| Message | No | Approval request message |
| Timeout | No | Auto-reject after duration |
| Min Approvals | No | Required approval count |
Approver Formats:
user:john@example.comteam:platform-teamrole:adminOutputs: approved_by, approved_at, comment
Pause workflow for a specified duration.
| Field | Required | Description |
|---|---|---|
| Duration | Yes | Wait time value |
| Unit | Yes | seconds, minutes, hours |
Use Cases:
Branch workflow based on expressions.
| Field | Required | Description |
|---|---|---|
| Condition | Yes | Expression to evaluate |
| True Path | Yes | Steps if condition is true |
| False Path | No | Steps if condition is false |
Operators:
| Operator | Description |
|---|---|
== | Equals |
!= | Not equals |
>, <, >=, <= | Comparisons |
&&, || | And, Or |
contains() | String contains |
Example Conditions:
${steps.plan.output.changes} > 0
${trigger.branch} == "main"
${env.ENVIRONMENT} == "production"
Iterate over a list of items.
| Field | Required | Description |
|---|---|---|
| Items | Yes | Array to iterate |
| Parallel | No | Run iterations in parallel |
| Max Parallel | No | Concurrent iteration limit |
Items Sources:
["us-east-1", "eu-west-1", "ap-southeast-1"]
${steps.get_regions.output.regions}
Access Current Item: ${loop.item}
Send email notifications.
| Field | Required | Description |
|---|---|---|
| To | Yes | Recipients |
| Subject | Yes | Email subject |
| Body | Yes | Email body (HTML or text) |
| CC | No | CC recipients |
Send Slack messages.
| Field | Required | Description |
|---|---|---|
| Channel | Yes | Slack channel |
| Message | Yes | Message text |
| Blocks | No | Slack blocks JSON |
Message Template:
π *Deployment Complete*
Workflow: ${workflow.name}
Status: ${workflow.status}
Duration: ${workflow.duration}
Send HTTP webhook notifications.
| Field | Required | Description |
|---|---|---|
| URL | Yes | Webhook URL |
| Payload | No | JSON payload |
| Headers | No | Custom headers |
For security, the Script step validates code before execution and blocks patterns that could compromise the system:
| Blocked Pattern | Why |
|---|---|
Fork bombs (:(){ :|:& };:) | Exhausts system processes |
dd if=/dev/zero to disks | Overwrites disk data |
mkfs commands | Formats partitions |
Reverse shells (/dev/tcp, nc -e) | Opens backdoor connections |
/dev/sda writes | Direct disk access |
rm -rf / or rm -rf /* | Deletes root filesystem |
PTY spawn patterns (pty.spawn) | Spawns privileged shells |
| Python subprocess reverse shells | Remote code execution |
Scripts are also limited to 100 KB maximum size.
Blocked patterns are checked when you save the workflow, not just at runtime. The editor shows a validation error if a blocked pattern is found.
All step fields support ${...} interpolation. Available variables:
| Variable | Description |
|---|---|
${workflow.name} | Name of the workflow |
${workflow.run_id} | Unique ID for this execution run |
${workflow.status} | Current status of the workflow |
${workflow.duration} | Elapsed execution time |
| Variable | Description |
|---|---|
${trigger.type} | Trigger type: manual, scheduled, webhook, git_push, incident, workflow |
${trigger.branch} | Git branch (git_push triggers only) |
${trigger.payload.*} | Webhook payload fields (webhook triggers only) |
${trigger.incident.severity} | Incident severity (incident triggers only) |
| Variable | Description |
|---|---|
${steps.<stepName>.output.<field>} | Output value from a named step |
${steps.<stepName>.status} | Status of a named step (success, failed, skipped) |
Step names are the display names you give each step in the editor (lowercased, spaces replaced with underscores).
| Variable | Description |
|---|---|
${loop.item} | Current item in the iteration |
${loop.index} | Zero-based index of the current iteration |
| Variable | Description |
|---|---|
${env.VARIABLE_NAME} | Environment variable set on the workflow or step |
All steps share these settings:
| Setting | Description |
|---|---|
| Timeout | Max execution time |
| On Error | What to do if step fails: stop (default), continue, or retry |
| Retry Attempts | Number of retry attempts (when On Error is retry) |
| Retry Delay | Wait duration between retries |
| Run If | Condition expression β step only runs when true |
| Skip If | Condition expression β step is skipped when true |
| Step | Type | Key Settings |
|---|---|---|
| Build | Script | npm run build |
| Plan | IaC Deploy | Action: Plan |
| Check Changes | Condition | ${steps.plan.output.changes} > 0 |
| Approval | Approval | Approvers: team:devops |
| Apply | IaC Deploy | Action: Apply |
| Health Check | HTTP Request | GET /health, expect 200 |
| Notify | Slack | Channel: #deploys |
ββββββββββββββββ
β Check Changesβ
β changes > 0 β
ββββββββ¬ββββββββ
β
ββββββββββββββ΄βββββββββββββ
β true β false
βββββββΌββββββ βββββββΌββββββ
β Approval β β Skip β
βββββββ¬ββββββ β (end) β
β βββββββββββββ
βββββββΌββββββ
β Apply β
βββββββββββββ
Step 1 (Build):
Output: version = "1.2.3"
Step 2 (Deploy):
Input: ${steps.build.output.version}
Step 3 (Notify):
Message: Deployed v${steps.build.output.version}