# Workflow Steps

Source: https://docs.ops0.com/docs/workflows/steps

Steps are the building blocks of workflows. Each step performs an action and can pass outputs to subsequent steps.

## Step Categories

Infrastructure

IaC Deploy, Configuration Deploy

Actions

Script, HTTP Request

Flow Control

Approval, Wait, Condition, Loop

Notifications

Email, Slack, Webhook

---

## IaC Deploy

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:**
```json
{
  "instance_type": "t3.large",
  "environment": "production"
}
```

**Outputs:** `outputs`, `plan_summary`, `status`

---

## Configuration Deploy

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`

---

## Script

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:**
```bash
#!/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`

---

## HTTP Request

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:**
```json
{
  "Content-Type": "application/json",
  "Authorization": "Bearer ${secrets.API_TOKEN}"
}
```

**Outputs:** `status_code`, `body`, `headers`

---

## Approval

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.com`
- `team:platform-team`
- `role:admin`

**Outputs:** `approved_by`, `approved_at`, `comment`

---

## Wait

Pause workflow for a specified duration.

| Field | Required | Description |
|-------|----------|-------------|
| **Duration** | Yes | Wait time value |
| **Unit** | Yes | seconds, minutes, hours |

**Use Cases:**
- Wait for resources to provision
- Rate limiting between API calls
- Scheduled delays

---

## Condition

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"
```

---

## Loop

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:**
```json
["us-east-1", "eu-west-1", "ap-southeast-1"]
```

```
${steps.get_regions.output.regions}
```

**Access Current Item:** `${loop.item}`

---

## Email

Send email notifications.

| Field | Required | Description |
|-------|----------|-------------|
| **To** | Yes | Recipients |
| **Subject** | Yes | Email subject |
| **Body** | Yes | Email body (HTML or text) |
| **CC** | No | CC recipients |

---

## Slack

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}
```

---

## Webhook

Send HTTP webhook notifications.

| Field | Required | Description |
|-------|----------|-------------|
| **URL** | Yes | Webhook URL |
| **Payload** | No | JSON payload |
| **Headers** | No | Custom headers |

---

## Script Step — Blocked Patterns

For security, the Script step validates code before execution and blocks patterns that could compromise the system:

| Blocked Pattern | Why |
|----------------|-----|
| Fork bombs (`:()&#123; :\|:& &#125;;:`) | 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.

**Validation runs before execution**

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.

---

## Variable Reference

All step fields support `${...}` interpolation. Available variables:

### Workflow 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 |

### Trigger Variables

| 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) |

### Step Output Variables

| 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).

### Loop Variables (inside Loop steps)

| Variable | Description |
|----------|-------------|
| `${loop.item}` | Current item in the iteration |
| `${loop.index}` | Zero-based index of the current iteration |

### Environment Variables

| Variable | Description |
|----------|-------------|
| `${env.VARIABLE_NAME}` | Environment variable set on the workflow or step |

---

## Common Settings

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 |

---

## Example: Multi-Step Deployment

### Step Configuration

| 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 |

### Condition Logic

```
                    ┌──────────────┐
                    │ Check Changes│
                    │ changes > 0  │
                    └──────┬───────┘
                           │
              ┌────────────┴────────────┐
              │ true                    │ false
        ┌─────▼─────┐             ┌─────▼─────┐
        │ Approval  │             │   Skip    │
        └─────┬─────┘             │  (end)    │
              │                   └───────────┘
        ┌─────▼─────┐
        │   Apply   │
        └───────────┘
```

### Variable Passing

```
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}
```
