# steps

The `steps` field is an ordered array of step objects. Atmos executes the steps in the order they appear.

```yaml
workflows:
  deploy:
    steps:
      - name: plan
        type: atmos
        command: terraform plan vpc
      - name: apply
        type: atmos
        command: terraform apply vpc -auto-approve
```

Each array item is a step — the unit of work Atmos runs, whether it executes a command or renders interactive UI. The `type` field selects step behavior. Fields such as `title`, `columns`, `prompt`, `retry`, and `timeout` are parameters on that same step object; they are not separate steps.

```yaml
steps:
  - name: building
    type: spin
    title: Building application
    command: npm run build
    timeout: 60s
```

In this example, `spin` is the value of `steps[].type`. The fields `title`, `command`, and `timeout` configure that `spin` step.

## Common Fields

- **`name`**
  Optional step identifier. Atmos generates names like 
  `step1`
   when omitted. Names are used by 
  `--from-step`
   and by templates such as 
  `{{ .steps.plan.value }}`
  .
- **`type`**
  Step behavior selector. Type 
  `atmos`
   is the default for command steps. See 
  [type](/workflows/steps/type)
  .
- **`command`**
  Command text for command-running step types such as 
  `atmos`
  , 
  `shell`
  , 
  `spin`
  , and 
  `container`
   run actions.
- **`stack`**
  Step-level stack override for 
  `type: atmos`
   steps.
- **`identity`**
  Step-level auth identity. See 
  [identity](/workflows/steps/identity)
  .
- **`when`**
  Optional condition that decides whether the step runs. Built-in predicate keywords are evaluated first; other non-empty scalar strings are evaluated as CEL.
- **`env`**
  Step-level environment variables. See 
  [env](/workflows/steps/env)
  .
- **`working_directory`**
  Step-level working directory override. See 
  [working_directory](/workflows/steps/working-directory)
  .
- **`retry`**
  Retry policy for the step. See 
  [retry](/workflows/steps/retry)
  .
- **`output`**
  Step-level output mode override. See 
  [output](/workflows/steps/output)
  .
- **`show`**
  Step-level display settings override. See 
  [show](/workflows/steps/show)
  .
- **`outputs`**
  Declared outputs derived from the step result. See 
  [outputs](/workflows/steps/outputs)
  .
- **`when`**
  Condition controlling whether the step runs. Omitted means success-only: the step runs while the workflow status is still 
  `success`
  . Use 
  `when: always`
   for cleanup steps that must run after a previous step fails, such as stopping an emulator, restoring a backup file, or removing Terraform state.

Type-specific fields are documented with [type](/workflows/steps/type).

## Referencing values from other steps

A named step's result is available to later steps as `{{ .steps.<name>.value }}`
(and `{{ .steps.<name>.values }}` for multi-value steps). These templates — along
with `{{ .env.* }}` and `{{ .flags.* }}` and Sprig/Gomplate functions — resolve
in `shell`, `atmos`, and `exec` step **commands** and in a step's **`env:`**
values, the same way they do for [custom command steps](/cli/configuration/commands/steps):

```yaml
workflows:
  deploy:
    steps:
      - name: component
        type: choose
        prompt: "Component"
        options: [vpc, eks]
      - type: atmos
        command: terraform apply {{ .steps.component.value }} -auto-approve
        env:
          COMPONENT: "{{ .steps.component.value }}"
```

## Interactive Steps in CI (non-TTY)

Interactive step types (`choose`, `input`, `confirm`, `filter`, `file`, `write`) prompt for input and need a terminal. When Atmos runs without a TTY — most commonly in CI — a step behaves based on whether it has a `default`:

- **With a `default`**, the step uses the default value without prompting.
- **Without a `default`**, the step fails with `interactive terminal required for step`. This preserves the previous behavior, so an unattended run never proceeds with an unintended value.

This lets the _same_ workflow prompt locally and run unattended in CI. Source the default from the environment with the `!env` function (`!env VAR fallback`):

```yaml
steps:
  - name: account
    type: choose
    prompt: "Account"
    options: [dev, prod]
    default: !env STACK_ACCOUNT dev
  - type: shell
    command: 'echo "Deploying {{ .steps.account.value }}"'
```

Workflow step `default`, `prompt`, `options`, and `placeholder` evaluate the `!env` and `!exec` YAML functions. (Custom commands defined in `atmos.yaml` resolve these during config load.) The captured value — entered interactively or taken from the default — is available to later steps as `{{ .steps.<name>.value }}`.

## Conditional Execution

Use `when` to skip a workflow step unless a condition is true. Bare predicate keywords keep their existing behavior:

```yaml
steps:
  - name: plan
    type: atmos
    command: terraform plan vpc
    when: ci
```

Any other non-empty scalar string is evaluated as a CEL expression:

```yaml
steps:
  - name: prod-ci-check
    type: shell
    command: ./scripts/check-prod.sh
    when: stack == "prod" && ci
```

Use the explicit `!cel` tag when the value is intended to be CEL or could look ambiguous:

```yaml
steps:
  - name: prod-ci-check
    type: shell
    command: ./scripts/check-prod.sh
    when: !cel 'stack == "prod" && ci'
```

Workflow step CEL expressions can use `ci`, `status`, `stack`, `component`, `workflow`, `step`, and `env`. Predicate keywords win over CEL for bare strings, so `when: ci` uses the built-in predicate while `when: !cel 'ci'` evaluates CEL. `status` starts as `success` and becomes `failure` after a previous step fails.

## Cleanup Steps

Atmos continues evaluating structured steps after a failure so failure-aware
cleanup can run:

```yaml
steps:
  - type: shell
    name: start-emulator
    command: atmos emulator up aws -s dev --ephemeral

  - type: shell
    name: record-or-test
    command: atmos terraform apply demo -s dev -auto-approve

  - type: shell
    name: stop-emulator
    when: always
    command: atmos emulator down aws -s dev

  - type: shell
    name: validate
    command: atmos terraform output demo -s dev
```

If `record-or-test` fails, `stop-emulator` still runs because it declares
`when: always`. The `validate` step is skipped because omitted `when` means
success-only. The workflow still exits with the original failure; cleanup does
not make the failed workflow look successful.
