# Workflows

Workflows are a way of combining multiple commands into one executable unit of work.

You can use [Atmos Custom Commands](/cli/configuration/commands) in Atmos Workflows, and Atmos Workflows in [Atmos Custom Commands](/cli/configuration/commands)

## Simple Example

Here's an example workflow called `eks-up` which runs a few commands that will bring up the EKS cluster:

```yaml title=stacks/workflows/workflow1.yaml
workflows:
  eks-up:
    description: |
      Bring up the EKS cluster.
    steps:
      - command: terraform apply vpc -auto-approve
      - command: terraform apply eks/cluster -auto-approve
      - command: terraform apply eks/alb-controller -auto-approve
```

## Retry Configuration

The `retry` section accepts the following parameters. **All parameters are optional** - omit any
parameter to disable that feature or use unlimited behavior.

- **`max_attempts`**
  Maximum number of retry attempts. Omit for unlimited retries. Must be greater than zero if specified.
- **`max_elapsed_time`**
  Total time budget for all attempts combined (e.g., 
  `10m`
  , 
  `1h`
  ). This is the cumulative time across all retry attempts plus backoff delays, not a per-attempt timeout. Omit for no time limit. Must be greater than zero if specified.
- **`backoff_strategy`**
  The backoff strategy between retries: 
  `constant`
  , 
  `linear`
  , or 
  `exponential`
  . Default: 
  `constant`
  .
- **`initial_delay`**
  Delay before the first retry (e.g., 
  `5s`
  , 
  `100ms`
  ). Omit for no delay. Cannot be negative.
- **`max_delay`**
  Maximum delay between retries, caps exponential/linear growth (e.g., 
  `30s`
  ). Omit for no cap. Must be greater than zero if specified.
- **`multiplier`**
  Multiplier for exponential or linear backoff (e.g., 
  `2.0`
  ). Omit to use the default multiplier (2.0). Must be greater than zero if specified.
- **`random_jitter`**
  Jitter factor between 0.0 and 1.0 to add randomness to delays. Omit for no jitter.

:::note Configuration Philosophy

- **Omitting a parameter** disables that feature or makes it unlimited
- **Explicitly setting a parameter to zero** is an error (invalid configuration)
- This allows minimal configs like `retry: { max_attempts: 3 }` to work without surprises

:::

### Example: Retry Indefinitely

To retry indefinitely until success, omit both `max_attempts` and `max_elapsed_time`:

```yaml
workflows:
  deploy:
    steps:
      - command: terraform apply vpc -auto-approve
        retry:
          initial_delay: 5s
          backoff_strategy: exponential
          max_delay: 60s
          # max_attempts: omitted = unlimited retries
          # max_elapsed_time: omitted = no time limit
```

This will keep retrying with exponential backoff (capped at 60s between attempts) until either:

- The command succeeds
- The workflow is cancelled (e.g., Ctrl+C)

### Example: Limited Retries, No Time Limit

```yaml
workflows:
  eks-up:
    description: Bring up the eks cluster
    steps:
      - command: terraform apply vpc -auto-approve
        retry:
          max_attempts: 3
          backoff_strategy: exponential
          initial_delay: 3s
          multiplier: 2
          # max_elapsed_time: omitted = no time limit
      - command: terraform apply eks/cluster -auto-approve
      - command: terraform apply eks/alb-controller -auto-approve
```

### Example: Time-Limited Retries

```yaml
retry:
  max_elapsed_time: 10m  # Stop after 10 minutes total
  initial_delay: 5s
  backoff_strategy: exponential
  # max_attempts: omitted = unlimited attempts within time budget
```

:::note

The workflow name can be anything you want, and the workflow can also accept command-line parameters (e.g. stack name)

:::

If you define this workflow in the file `workflow1.yaml`, it can be executed like this to provision
the `vpc`, `eks/cluster` and `eks/alb-controller` [Atmos Components](/components) into
the `tenant1-ue2-dev` [Atmos Stack](/learn/stacks):

```shell
atmos workflow eks-up -f workflow1 --stack tenant1-ue2-dev
```

:::tip

Refer to [`atmos workflow`](/cli/commands/workflow) for the complete description of the CLI command

:::

## Workflow File Auto-Discovery

Atmos can automatically discover workflow files, eliminating the need to specify `--file` for uniquely named workflows.

### Running Workflows Without `--file`

If your workflow name is unique across all workflow files, Atmos automatically finds and runs it:

```shell
# Just specify the workflow name - Atmos finds the file automatically
atmos workflow eks-up --stack tenant1-ue2-dev
```

This is equivalent to:

```shell
# Explicitly specifying the file (still supported)
atmos workflow eks-up -f workflow1 --stack tenant1-ue2-dev
```

### How Auto-Discovery Works

When you run `atmos workflow <name>` without `--file`:

1. **Scans workflow directory** - Atmos searches all YAML files in your configured `workflows.base_path`
2. **Finds matching workflows** - Looks for workflows with the specified name
3. **Auto-selects if unique** - If only one file contains that workflow name, it runs automatically
4. **Prompts if multiple** - If multiple files have the same workflow name, shows an interactive selector (TTY) or an error with hints (CI/non-TTY)

### Interactive Selection for Duplicate Workflow Names

If multiple workflow files contain the same workflow name, Atmos presents an interactive selector:

```console
$ atmos workflow deploy

Multiple workflows found with name 'deploy'. Please choose:
> production.yaml - Deploy to production environment
  staging.yaml - Deploy to staging environment
  development.yaml - Deploy to development environment
```

You can still use `--file` to skip the prompt and specify exactly which file to use:

```shell
atmos workflow deploy --file production.yaml
```

:::note
The `--file` value is relative to your configured `workflows.base_path`. You can omit the `.yaml` extension - Atmos adds it automatically if not provided.
:::

### When to Use `--file`

The `--file` flag is optional for most workflows. You only need it when:

- Multiple workflow files contain a workflow with the same name
- You want to explicitly specify which file to use (e.g., in CI/CD scripts)
- You're running in a non-interactive environment where prompts aren't available

### Backward Compatibility

All existing workflows with explicit `--file` flags continue to work exactly as before:

```shell
# Explicit --file flag still works
atmos workflow deploy --file deploy.yaml

# Auto-discovery is purely additive
atmos workflow deploy
```

## Configuration

To configure and execute Atmos workflows, follow these steps:

- Configure workflows in [`atmos.yaml` CLI config file](/cli/configuration)
- Create workflow files and define workflows using the workflow schema

### Configure Workflows in `atmos.yaml`

In `atmos.yaml` CLI config file, add the following sections related to Atmos workflows:

```yaml
# Base path for components, stacks and workflows configurations.
# Can also be set using 'ATMOS_BASE_PATH' ENV var, or '--base-path' command-line argument.
# Supports both absolute and relative paths.
# If not provided or is an empty string, 'components.terraform.base_path', 'components.helmfile.base_path', 'stacks.base_path'
# and 'workflows.base_path' are independent settings (supporting both absolute and relative paths).
# If 'base_path' is provided, 'components.terraform.base_path', 'components.helmfile.base_path', 'stacks.base_path'
# and 'workflows.base_path' are considered paths relative to 'base_path'.
base_path: ""

workflows:
  # Can also be set using 'ATMOS_WORKFLOWS_BASE_PATH' ENV var, or '--workflows-dir' command-line arguments
  # Supports both absolute and relative paths
  base_path: "stacks/workflows"
```

where:

- **`base_path`**
  The base path for components, stacks and workflows configurations
- **`workflows.base_path`**
  The base path to Atmos workflow files

### Create Workflow Files

In `atmos.yaml`, we set `workflows.base_path` to `stacks/workflows`. The folder is relative to the root of the repository.

Refer to [networking.yaml](https://github.com/cloudposse/atmos/tree/main/examples/quick-start-advanced/stacks/workflows/networking.yaml) for an example.

We put the workflow files into the folder. The workflow file names can be anything you want, but we recommend naming them according to the functions
they perform, e.g. create separate workflow files per environment, account, team, or service.

For example, you can have a workflow file `stacks/workflows/workflows-eks.yaml` to define all EKS-related workflows.

Or, you can have a workflow file `stacks/workflows/workflows-dev.yaml` to define all workflows to provision resources into the `dev` account.
Similarly, you can create a workflow file `stacks/workflows/workflows-prod.yaml` to define all workflows to provision resources into the `prod`
account.

You can segregate the workflow files even further, e.g. per account and service. For example, in the workflow
file `stacks/workflows/workflows-dev-eks.yaml` you can define all EKS-related workflows for the `dev` account.

### Use Workflow Schema

Workflow files must confirm to the following schema:

```yaml
workflows:

  workflow-1:
    description: "Description of Workflow #1"
    steps: []

  workflow-2:
    description: "Description of Workflow #2"
    steps: []
```

Each workflow file must have the `workflows:` top-level section with a map of workflow definitions.

Each workflow definition must confirm to the following schema:

```yaml
  workflow-1:
    description: "Description of Workflow #1"
    stack: <Atmos stack> # optional
    steps:
      - command: <Atmos command to execute>
        name: <step name>  # optional
        type: atmos  # optional
        stack: <Atmos stack> # optional
        identity: <identity name> # optional
      - command: <Atmos command to execute>
        name: <step name>  # optional
        stack: <Atmos stack> # optional
        identity: <identity name> # optional
      - command: <shell script>
        name: <step name>  # optional
        type: shell  # required for the steps of type `shell`
        identity: <identity name> # optional
```

### Schema Definitions

- **`description`**
  The workflow description
- **`stack`**
  Workflow-level Atmos stack (optional). If specified, all workflow steps of type 
  `atmos`
   will be executed for this Atmos stack. It can be overridden in each step or on the command line by using the 
  `--stack`
   flag (
  `-s`
   for shorthand)
- **`steps`**
  A list of workflow steps which are executed sequentially in the order they are specified
- **`command`**
  The command to execute. Can be either an Atmos 
  [CLI command](/cli/commands)
   (without the 
  `atmos`
   binary name in front of it, for example 
  `command: terraform apply vpc`
  ), or a shell script. The type of the command is specified by the 
  `type`
   attribute
- **`name`**
  Step name (optional). It's used to find the first step from which to start executing the workflow when the command-line flag 
  `--from-step`
   is specified. If the 
  `name`
   is omitted, a friendly name will be generated for you consisting of a prefix of 
  `step`
   and followed by the index of the step (the index starts with 1, so the first generated step name would be 
  `step1`
  ).
- **`type`**
  The type of the step. Can be 
  `atmos`
  , 
  `shell`
  , or any of the 
  [interactive step types](#step-types)
   like 
  `input`
  , 
  `confirm`
  , 
  `choose`
  , 
  `filter`
  , 
  `markdown`
  , 
  `spin`
  , and more. Type 
  `atmos`
   is implicit for command steps. Type 
  `shell`
   is required for shell scripts. See the 
  [Step Types](#step-types)
   section for all available types
- **`stack`**
  Step-level Atmos stack (optional). If specified, the 
  `command`
   will be executed for this Atmos stack. It overrides the workflow-level 
  `stack`
   attribute, and can itself be overridden on the command line by using the 
  `--stack`
   flag (
  `-s`
   for shorthand)
- **`identity`**
  Step-level identity for authentication (optional). If specified, Atmos will authenticate using this identity before executing the step. The subprocess will receive environment variables pointing to temporary credential files (e.g., 
  `AWS_SHARED_CREDENTIALS_FILE`
  , 
  `AWS_CONFIG_FILE`
  , 
  `AWS_PROFILE`
  ). See 
  [Authentication](/cli/commands/auth/usage)
   for more details

:::note

A workflow command of type `shell` can be any simple or complex shell command or script.
You can use [YAML Multiline Strings](https://yaml-multiline.info/) to create complex multi-line shell scripts.

:::

:::tip Interactive Workflows

Atmos supports 25+ step types for building interactive CLI wizards. Use `input`, `confirm`, `choose`, and `filter` to collect user input, `markdown` and `success`/`info`/`warn`/`error` for rich output, `spin` for progress spinners, and `alert`/`title`/`env`/`exit` for terminal control. See the [Step Types](#step-types) section below for complete documentation.

:::

## Step Types

## Executing Workflow from a Named Step

Each workflow step can be given an arbitrary name (step's identifier) using the `name` attribute. For example:

```yaml title=stacks/workflows/workflow1.yaml
workflows:
  test-1:
    description: "Test workflow"
    steps:
      - command: echo Command 1
        name: step1
        type: shell
      - command: echo Command 2
        name: step2
        type: shell
      - command: echo Command 3
        name: step3
        type: shell
      - command: echo Command 4
        type: shell
```

The step's name can be used in the `--from-step` command-line flag to start the workflow execution from the step.

For example, the following command will skip the first two steps and will start executing the workflow from `step3`:

```console
atmos workflow test-1 -f workflow1 --from-step step3
```

This is useful when you want to restart the workflow from a particular step.

For example:

- You run the workflow first time with the command `atmos workflow test-1 -f workflow1`
- `step1` and `step2` succeed, but `step3` fails
- You fix the issue with the `step3` command
- You don't want to execute `step1` and `step2` again (to not spend time on it, or if they are
  not [idempotent](https://en.wikipedia.org/wiki/Idempotence))
- You run the command `atmos workflow test-1 -f workflow1 --from-step step3` to restart the workflow from `step3`

If the `name` attribute in a workflow step is omitted, a friendly name will be generated for you consisting of a prefix of `step` and followed by the
index of the step. The index starts with 1, so the first generated step name would be `step1`.

The `test-1` workflow defined above does not have the `name` attribute for the last workflow step.
When we execute the `atmos workflow` commands, Atmos automatically generates the names for the steps where the `name` attribute is omitted.
In this case, the generated name for the last step will be `step4`.

For example:

```shell
Executing the workflow 'test-1' from 'stacks/workflows/workflow1.yaml'

description: Test workflow
steps:
- name: step1
  command: echo Command 1
  type: shell
- name: step2
  command: echo Command 2
  type: shell
- name: step3
  command: echo Command 3
  type: shell
- name: step4
  command: echo Command 4
  type: shell

Executing workflow step: echo Command 4

Executing command: echo Command 4
Command 4

Executing workflow step: echo Command 5

Executing command: echo Command 5
Command 5
```

## Workflow Examples

The following workflow defines four steps of type `atmos` (implicit type) without specifying the workflow-level or step-level `stack` attribute.
Since the workflow does not specify the stack, it's generic and can be executed for any Atmos stack.
In this case, the stack needs to be provided on the command line.

```yaml title=stacks/workflows/workflow1.yaml
workflows:
  terraform-plan-all-test-components:
    description: |
      Run 'terraform plan' on 'test/test-component' and all its derived components.
      The stack must be provided on the command line:
      `atmos workflow terraform-plan-all-test-components -f workflow1 -s <stack>`
    steps:
      # Inline scripts are also supported
      # Refer to https://yaml-multiline.info for more details
      - type: shell
        command: |
          echo "Starting the workflow execution..."
          read -p "Press any key to continue... " -n1 -s
      - command: terraform plan test/test-component
      - command: terraform plan test/test-component-override
      - command: terraform plan test/test-component-override-2
      - command: terraform plan test/test-component-override-3
      - type: shell
        command: >-
          echo "All done!"
```

To run this workflow for the `tenant1-ue2-dev` stack, execute the following command:

```console
atmos workflow terraform-plan-all-test-components -f workflow1 -s tenant1-ue2-dev
```

The following workflow executes `terraform plan` on `test/test-component-override-2` component in all stacks.
In this case, the stack is specified inline for each workflow command.

```yaml title=stacks/workflows/workflow1.yaml
workflows:
  terraform-plan-test-component-override-2-all-stacks:
    description: Run 'terraform plan' on 'test/test-component-override-2' component in all stacks
    steps:
      - command: terraform plan test/test-component-override-2 -s tenant1-ue2-dev
      - command: terraform plan test/test-component-override-2 -s tenant1-ue2-staging
      - command: terraform plan test/test-component-override-2 -s tenant1-ue2-prod
      - command: terraform plan test/test-component-override-2 -s tenant2-ue2-dev
      - command: terraform plan test/test-component-override-2 -s tenant2-ue2-staging
      - command: terraform plan test/test-component-override-2 -s tenant2-ue2-prod
      - type: shell
        command: echo "All done!"
```

To run this workflow, execute the following command:

```console
atmos workflow terraform-plan-test-component-override-2-all-stacks -f workflow1
```

The following workflow is similar to the above, but the stack for each command is specified in the step-level `stack` attribute.

```yaml title=stacks/workflows/workflow1.yaml
workflows:
  terraform-plan-test-component-override-3-all-stacks:
    description: Run 'terraform plan' on 'test/test-component-override-3' component in all stacks
    steps:
      - command: terraform plan test/test-component-override-3
        stack: tenant1-ue2-dev
      - command: terraform plan test/test-component-override-3
        stack: tenant1-ue2-staging
      - command: terraform plan test/test-component-override-3
        stack: tenant1-ue2-prod
      - command: terraform plan test/test-component-override-3
        stack: tenant2-ue2-dev
      - command: terraform plan test/test-component-override-3
        stack: tenant2-ue2-staging
      - command: terraform plan test/test-component-override-3
        stack: tenant2-ue2-prod
```

To run this workflow, execute the following command:

```console
atmos workflow terraform-plan-test-component-override-3-all-stacks -f workflow1
```

## Working with Atmos Stacks in Workflows

The Atmos stack used by the workflow commands of type `atmos` can be specified in four different ways:

### Inline in the command itself

```yaml
steps:
  - command: terraform plan test/test-component-override-2 -s tenant1-ue2-dev
```

### In the workflow-level `stack` attribute

```yaml
workflows:
  my-workflow:
    stack: tenant1-ue2-dev
    steps:
      - command: terraform plan test/test-component
```

### In the step-level `stack` attribute

```yaml
steps:
  - command: terraform plan test/test-component
    stack: tenant1-ue2-dev
```

### On the command line

```console
atmos workflow my-workflow -f workflow1 -s tenant1-ue2-dev
```

## Workflow Failure Handling and Resuming

When a workflow step fails, Atmos will:

1. Display which step failed
2. Show the exact command that failed
3. Provide a ready-to-use command to resume the workflow from the failed step

Given this workflow:

```yaml title="stacks/workflows/networking.yaml"
workflows:
  provision-vpcs:
    description: "Deploy vpc components"
    steps:
      - command: terraform plan vpc -s plat-ue2-dev
        name: step-1
      - command: terraform plan vpc -s plat-ue2-staging
        name: step-2
      - command: terraform plan vpc -s plat-ue2-prod
        name: step-3
```

If step-2 fails, you'll see:

```console
Step 'step-2' failed!

Command failed:
terraform plan vpc -s plat-ue2-staging

To resume the workflow from this step, run:
atmos workflow provision-vpcs -f networking --from-step step-2
```

### Stack Precedence

The stack defined inline in the command itself has the lowest priority, it can and will be overridden by any other stack definition.
The step-level stack will override the workflow-level stack. The command line `--stack` option will override all other stacks defined in the workflow
itself. You can also use any combinations of the above (e.g. specify the stack at the workflow level, then override it at the step level for some
commands, etc.).

While this provides a great flexibility in defining the stack for workflow commands, we recommend creating generic workflows without defining
stacks in the workflow itself (the stack should be provided on the command line). This way, the workflow can be executed for any stack without any
modifications and without dealing with multiple workflows that are similar but differ only by the environment where the resources are provisioned.

## Using Authentication with Workflows

Workflow steps can specify an `identity` to authenticate before execution. This is useful when different steps require different levels of access or need to operate under different cloud accounts.

When an identity is specified, Atmos will:

1. Authenticate using the specified identity (prompting for MFA if required)
2. Write temporary credentials to a file
3. Set environment variables pointing to the credential files (`AWS_SHARED_CREDENTIALS_FILE`, `AWS_CONFIG_FILE`, `AWS_PROFILE`, etc.)
4. Execute the command with these environment variables

### Example: Workflow with Authentication

```yaml title="stacks/workflows/deploy.yaml"
workflows:
  deploy-with-auth:
    description: Deploy infrastructure with different identities
    steps:
      # Deploy foundation resources as superadmin
      - command: terraform apply vpc -s plat-ue2-prod
        identity: superadmin
        name: deploy-vpc

      # Deploy application resources as developer
      - command: terraform apply app -s plat-ue2-prod
        identity: developer
        name: deploy-app

      # Run a shell script with authentication
      - command: |
          echo "Current AWS identity:"
          aws sts get-caller-identity
        type: shell
        identity: superadmin
        name: verify-identity
```

To execute this workflow:

```console
atmos workflow deploy-with-auth -f deploy
```

### Mixed Authentication

Different steps can use different identities, or no identity at all:

```yaml title="stacks/workflows/mixed-auth.yaml"
workflows:
  mixed-auth-workflow:
    description: Workflow with mixed authentication
    steps:
      # Step without authentication (uses ambient credentials)
      - command: terraform plan vpc -s plat-ue2-dev
        name: plan-without-auth

      # Step with superadmin identity
      - command: terraform apply vpc -s plat-ue2-prod
        identity: superadmin
        name: apply-with-superadmin

      # Step with different identity
      - command: terraform apply monitoring -s plat-ue2-prod
        identity: monitoring-admin
        name: apply-with-monitoring-admin
```

:::tip
Configure identities in your [`atmos.yaml`](/cli/configuration) under the `auth` section. See the [Authentication documentation](/cli/commands/auth/usage) for configuration details.
:::

### Setting a Default Identity for All Steps

You can use the `--identity` flag to set a default identity for all workflow steps that don't specify their own:

```shell
# Apply superadmin identity to all steps without their own identity
atmos workflow deploy-all -f workflows --identity superadmin
```

**Precedence**: Step-level `identity` field > `--identity` flag > no authentication

**Example**:

```yaml
workflows:
  deploy-mixed:
    steps:
      # This step uses the --identity flag value (if provided)
      - command: terraform plan vpc
        name: plan-vpc

      # This step ALWAYS uses developer identity (overrides --identity flag)
      - command: terraform apply app
        identity: developer
        name: apply-app

      # This step uses the --identity flag value (if provided)
      - command: terraform apply monitoring
        name: apply-monitoring
```

Running `atmos workflow deploy-mixed -f workflows --identity superadmin` will:

- Execute `plan-vpc` step as `superadmin`
- Execute `apply-app` step as `developer` (step-level identity overrides)
- Execute `apply-monitoring` step as `superadmin`

## Using Toolchain Tools

Workflows automatically have access to tools defined in your project's `.tool-versions` file. The toolchain ensures the correct versions are installed and available in PATH when your workflow executes.

### Automatic Tool Access

If your project has a `.tool-versions` file:

```
terraform 1.10.0
kubectl 1.32.0
helm 3.16.0
```

Workflow steps can use these tools directly:

```yaml title="stacks/workflows/deploy.yaml"
workflows:
  deploy:
    description: Deploy infrastructure
    steps:
      # Atmos commands (type: atmos is implicit)
      - name: plan-vpc
        command: terraform plan vpc
      - name: apply-vpc
        command: terraform apply vpc -auto-approve

      # Shell commands for non-Atmos tools
      - name: apply-k8s
        command: kubectl apply -f manifests/
        type: shell
```

When the workflow runs:

1. Atmos reads `.tool-versions` to identify required tools
2. Missing tools are automatically installed
3. PATH is updated to include toolchain binaries
4. Workflow steps execute with the correct tool versions

### Declaring Workflow Dependencies

Use the `dependencies` field at the workflow level to require additional tools or override versions from `.tool-versions`:

```yaml title="stacks/workflows/validate.yaml"
workflows:
  validate:
    description: Validate infrastructure
    dependencies:
      tools:
        tflint: "^0.54.0"
        checkov: "latest"
    steps:
      # Atmos command (type: atmos is implicit)
      - name: validate
        command: terraform validate vpc

      # Shell commands for toolchain tools
      - name: lint
        command: tflint --recursive
        type: shell
      - name: security
        command: checkov -d .
        type: shell
```

Dependencies declared in the workflow take precedence over `.tool-versions`.

### Version Constraints

Dependencies support SemVer constraints:

| Constraint | Meaning | Example |
|------------|---------|---------|
| `1.10.3` | Exact version | Only version 1.10.3 |
| `~> 1.10.0` | Pessimistic (patch) | Includes 1.10.x but not 1.11.0 |
| `^1.10.0` | Compatible (minor) | Includes 1.x.x but not 2.0.0 |
| `latest` | Latest available | Most recent version |

:::tip
Use `.tool-versions` for project-wide tool defaults and workflow `dependencies` for workflow-specific version requirements. This gives you flexibility while maintaining consistency across your team.
:::

### Related Documentation

- [Toolchain Configuration](/cli/configuration/toolchain) - Configure tool version management
- [Custom Commands](/cli/configuration/commands) - Custom commands also support toolchain integration

## Try It

Explore working examples that demonstrate workflows in action.
