Skip to main content

Custom Commands

Atmos can be easily extended to support any number of custom CLI commands. Custom commands are exposed through the atmos CLI when you run atmos help. It's a great way to centralize the way operational tools are run in order to improve DX.

For example, one great way to use custom commands is to tie all the miscellaneous scripts into one consistent CLI interface. Then we can kiss those ugly, inconsistent arguments to bash scripts goodbye! Just wire up the commands in atmos to call the script. Then developers can just run atmos help and discover all available commands.

Simple Example

Here is an example to play around with to get started.

Adding the following to atmos.yaml will introduce a new hello command.

# Custom CLI commands
commands:
- name: hello
description: This command says Hello world
steps:
- "echo Hello world!"

We can run this example like this:

atmos hello

Positional Arguments

Atmos also supports positional arguments. If a positional argument is required but not provided by the user, the command will fail—unless you define a default in your config.

For the example, adding the following to atmos.yaml will introduce a new greet command that accepts one name argument, but uses a default of "John Doe" if none is provided.

# subcommands
commands:
- name: greet
description: This command says hello to the provided name
arguments:
- name: name
description: Name to greet
required: true
default: John Doe
steps:
- "echo Hello {{ .Arguments.name }}!"

We can run this example like this:

atmos greet Alice

or defaulting to "John Doe"

atmos greet

Trailing Arguments

Atmos supports trailing arguments after -- (a standalone double-dash). The -- itself is a delimiter that signals the end of Atmos-specific options. Anything after -- is passed directly to the underlying command without being interpreted by Atmos. The value of these trailing arguments is accessible in {{ .TrailingArgs }}.

For the example, adding the following to atmos.yaml will introduce a new echo command that accepts one name argument and also uses trailingArgs

- name: ansible run
description: "Runs an Ansible playbook, allowing extra arguments after --."
arguments:
- name: playbook
description: "The Ansible playbook to run"
default: site.yml
required: true
steps:
- "ansible-playbook {{ .Arguments.playbook }} {{ .TrailingArgs }}"

Output:

$ atmos ansible run -- --limit web
Running: ansible-playbook site.yml --limit web

PLAY [web] *********************************************************************

Passing Flags

Passing flags works much like passing positional arguments, except for that they are passed using long or short flags. Flags can be optional (this is configured by setting the required attribute to false).

# subcommands
commands:
- name: hello
description: This command says hello to the provided name
flags:
- name: name
shorthand: n
description: Name to greet
required: true
steps:
- "echo Hello {{ .Flags.name }}!"

We can run this example like this, using the long flag:

atmos hello --name world

Or, using the shorthand, we can just write:

atmos hello -n world

Boolean Flags

Flags can be defined as boolean type using type: bool. Boolean flags don't require a value to be passed—when present, they are set to true.

commands:
- name: deploy
description: Deploy to environment
flags:
- name: dry-run
shorthand: d
description: Perform a dry run without making changes
type: bool
- name: verbose
shorthand: v
description: Enable verbose output
type: bool
default: false
- name: auto-approve
description: Auto-approve without prompting
type: bool
default: true
steps:
- |
{{ if .Flags.dry-run }}
echo "DRY RUN MODE"
{{ end }}
{{ if .Flags.verbose }}
echo "Verbose output enabled"
{{ end }}
{{ if .Flags.auto-approve }}
terraform apply -auto-approve
{{ else }}
terraform apply
{{ end }}

Usage:

# Enable dry-run (sets it to true)
atmos deploy --dry-run

# Use short flag
atmos deploy -d

# Boolean with explicit value
atmos deploy --auto-approve=false

Using Boolean Flags in Steps

Boolean flags are available as Go template variables with values true or false. Here are common patterns for using them in bash:

commands:
- name: build
description: Build the project
flags:
- name: verbose
shorthand: v
type: bool
description: Enable verbose output
- name: clean
type: bool
default: true
description: Clean before building
steps:
# Pattern 1: Conditional command execution with if/else
- |
{{ if .Flags.verbose }}
echo "Verbose mode enabled"
set -x
{{ end }}

# Pattern 2: Inline conditional flag
- echo "Building{{ if .Flags.verbose }} with verbose output{{ end }}..."

# Pattern 3: Pass as flag to another command
- make build {{ if .Flags.verbose }}VERBOSE=1{{ end }}

# Pattern 4: Conditional step execution
- |
{{ if .Flags.clean }}
echo "Cleaning build directory..."
rm -rf ./build
{{ end }}

# Pattern 5: Negation check
- |
{{ if not .Flags.clean }}
echo "Skipping clean step"
{{ end }}

# Pattern 6: Convert to shell variable
- |
VERBOSE={{ .Flags.verbose }}
if [ "$VERBOSE" = "true" ]; then
echo "Verbose is on"
fi

# Pattern 7: Using printf for explicit string conversion
- |
VERBOSE={{ printf "%t" .Flags.verbose }}
echo "Verbose flag is: $VERBOSE"
tip

Boolean values automatically render as true or false (lowercase strings) when used in templates. You can reference them directly with {{ .Flags.name }}—no conversion needed!

Flag Defaults

Both string and boolean flags support default values using the default attribute:

flags:
- name: environment
description: Target environment
default: "development"
- name: force
type: bool
description: Force the operation
default: false
- name: auto-approve
type: bool
description: Skip confirmation prompts
default: true

When a flag has a default value, users can omit it from the command line. The default value will be used unless explicitly overridden.

Advanced Examples

Define a New Terraform Command

# Custom CLI commands
commands:
- name: terraform
description: Execute 'terraform' commands
# subcommands
commands:
- name: provision
description: This command provisions terraform components
arguments:
- name: component
description: Name of the component
flags:
- name: stack
shorthand: s
description: Name of the stack
required: true
# ENV var values support Go templates
env:
- key: ATMOS_COMPONENT
value: "{{ .Arguments.component }}"
- key: ATMOS_STACK
value: "{{ .Flags.stack }}"
steps:
- atmos terraform plan $ATMOS_COMPONENT -s $ATMOS_STACK
- atmos terraform apply $ATMOS_COMPONENT -s $ATMOS_STACK

Override an Existing Terraform Command

# Custom CLI commands
commands:
- name: terraform
description: Execute 'terraform' commands
# subcommands
commands:
- name: apply
description: This command executes 'terraform apply -auto-approve' on terraform components
arguments:
- name: component
description: Name of the component
flags:
- name: stack
shorthand: s
description: Name of the stack
required: true
steps:
- atmos terraform apply {{ .Arguments.component }} -s {{ .Flags.stack }} -auto-approve

Show Component Info

# Custom CLI commands
commands:
- name: show
description: Execute 'show' commands
# subcommands
commands:
- name: component
description: Execute 'show component' command
arguments:
- name: component
description: Name of the component
flags:
- name: stack
shorthand: s
description: Name of the stack
required: true
# ENV var values support Go templates and have access to {{ .ComponentConfig.xxx.yyy.zzz }} Go template variables
env:
- key: ATMOS_COMPONENT
value: "{{ .Arguments.component }}"
- key: ATMOS_STACK
value: "{{ .Flags.stack }}"
- key: ATMOS_TENANT
value: "{{ .ComponentConfig.vars.tenant }}"
- key: ATMOS_STAGE
value: "{{ .ComponentConfig.vars.stage }}"
- key: ATMOS_ENVIRONMENT
value: "{{ .ComponentConfig.vars.environment }}"
# If a custom command defines 'component_config' section with 'component' and 'stack', 'atmos' generates the config for the component in the stack
# and makes it available in {{ .ComponentConfig.xxx.yyy.zzz }} Go template variables,
# exposing all the component sections (which are also shown by 'atmos describe component' command)
component_config:
component: "{{ .Arguments.component }}"
stack: "{{ .Flags.stack }}"
# Steps support using Go templates and can access all configuration settings (e.g. {{ .ComponentConfig.xxx.yyy.zzz }})
# Steps also have access to the ENV vars defined in the 'env' section of the 'command'
steps:
- 'echo Atmos component from argument: "{{ .Arguments.component }}"'
- 'echo ATMOS_COMPONENT: "$ATMOS_COMPONENT"'
- 'echo Atmos stack: "{{ .Flags.stack }}"'
- 'echo Terraform component: "{{ .ComponentConfig.component }}"'
- 'echo Backend S3 bucket: "{{ .ComponentConfig.backend.bucket }}"'
- 'echo Terraform workspace: "{{ .ComponentConfig.workspace }}"'
- 'echo Namespace: "{{ .ComponentConfig.vars.namespace }}"'
- 'echo Tenant: "{{ .ComponentConfig.vars.tenant }}"'
- 'echo Environment: "{{ .ComponentConfig.vars.environment }}"'
- 'echo Stage: "{{ .ComponentConfig.vars.stage }}"'
- 'echo Dependencies: "{{ .ComponentConfig.deps }}"'

Set EKS Cluster

# Custom CLI commands
commands:
- name: set-eks-cluster
description: |
Download 'kubeconfig' and set EKS cluster.

Example usage:
atmos set-eks-cluster eks/cluster -s plat-ue1-dev -r admin
atmos set-eks-cluster eks/cluster -s plat-uw2-prod --role reader
verbose: false # Set to `true` to see verbose outputs
arguments:
- name: component
description: Name of the component
flags:
- name: stack
shorthand: s
description: Name of the stack
required: true
- name: role
shorthand: r
description: IAM role to use
required: true
# If a custom command defines 'component_config' section with 'component' and 'stack',
# Atmos generates the config for the component in the stack
# and makes it available in {{ .ComponentConfig.xxx.yyy.zzz }} Go template variables,
# exposing all the component sections (which are also shown by 'atmos describe component' command)
component_config:
component: "{{ .Arguments.component }}"
stack: "{{ .Flags.stack }}"
env:
- key: KUBECONFIG
value: /dev/shm/kubecfg.{{ .Flags.stack }}-{{ .Flags.role }}
steps:
- >
aws
--profile {{ .ComponentConfig.vars.namespace }}-{{ .ComponentConfig.vars.tenant }}-gbl-{{ .ComponentConfig.vars.stage }}-{{ .Flags.role }}
--region {{ .ComponentConfig.vars.region }}
eks update-kubeconfig
--name={{ .ComponentConfig.vars.namespace }}-{{ .Flags.stack }}-eks-cluster
--kubeconfig="${KUBECONFIG}"
> /dev/null
- chmod 600 ${KUBECONFIG}
- echo ${KUBECONFIG}

List Stacks and Components

# Custom CLI commands
commands:
- name: list
description: Execute 'atmos list' commands
# subcommands
commands:
- name: stacks
description: |
List all Atmos stacks.
steps:
- >
atmos describe stacks --process-templates=false --sections none | grep -e "^\S" | sed s/://g
- name: components
description: |
List all Atmos components in all stacks or in a single stack.

Example usage:
atmos list components
atmos list components -s tenant1-ue1-dev
atmos list components --stack tenant2-uw2-prod
flags:
- name: stack
shorthand: s
description: Name of the stack
required: false
steps:
- >
{{ if .Flags.stack }}
atmos describe stacks --stack {{ .Flags.stack }} --format json --sections none | jq ".[].components.terraform" | jq -s add | jq -r "keys[]"
{{ else }}
atmos describe stacks --format json --sections none | jq ".[].components.terraform" | jq -s add | jq -r "keys[]"
{{ end }}

Tool Dependencies

Custom commands can declare tool dependencies that are automatically installed before execution. This ensures that any CLI tools your command requires are available at the correct version, leveraging Atmos's toolchain management capabilities.

Basic Usage

Specify tool dependencies using the dependencies.tools section within your command:

commands:
- name: lint
description: Run tflint on terraform components
dependencies:
tools:
tflint: "0.54.0"
arguments:
- name: component
description: Component to lint
required: true
flags:
- name: stack
shorthand: s
description: Stack name
required: true
steps:
- atmos terraform generate varfile {{ .Arguments.component }} -s {{ .Flags.stack }}
- tflint --chdir=components/terraform/{{ .Arguments.component }}

When you run atmos lint vpc -s plat-ue2-dev, Atmos will:

  1. Check if tflint 0.54.0 is installed in the toolchain directory
  2. Install it from the toolchain registry if missing
  3. Execute the command steps with the tool available in PATH

Multiple Tool Dependencies

Commands can declare multiple tool dependencies:

commands:
- name: security-scan
description: Run security scans on infrastructure code
dependencies:
tools:
tflint: "0.54.0"
checkov: "3.0.0"
tfsec: "1.28.0"
steps:
- tflint --chdir=components/terraform
- checkov -d components/terraform
- tfsec components/terraform

Version Specification

Tool versions can be specified in several formats:

dependencies:
tools:
# Exact version
terraform: "1.9.8"

# Latest available version
kubectl: "latest"

Integration with Toolchain Configuration

Tool dependencies in custom commands work with your toolchain configuration in atmos.yaml. Tools are installed to the configured install_path and resolved using your configured registries.

# atmos.yaml
toolchain:
install_path: ".tools"
registries:
- name: aqua
type: aqua
source: https://github.com/aquaproj/aqua-registry/tree/main/pkgs
priority: 10

commands:
- name: lint
dependencies:
tools:
tflint: "0.54.0" # Installed to .tools/, resolved via aqua registry
steps:
- tflint --version
tip

Use tool dependencies in custom commands to ensure all team members and CI/CD pipelines use consistent tool versions without manual installation steps.

Working Directory

Custom commands can specify a working_directory field to control where the command steps execute. This is useful when commands need to run from a specific location, regardless of where atmos was invoked.

Path Resolution

  • Absolute paths are used as-is (e.g., /tmp, /home/user/scripts)
  • Relative paths are resolved against the Atmos base_path
  • The !repo-root YAML function can be used to reference the git repository root

Example: Run from Repository Root

commands:
- name: build
description: Build the project from repository root
working_directory: !repo-root .
steps:
- make build
- make test

This ensures the build commands run from the repository root, even if you invoke atmos build from a subdirectory.

Example: Run in Temp Directory

commands:
- name: download-tools
description: Download and extract tools in /tmp
working_directory: /tmp
steps:
- wget https://example.com/tools.tar.gz
- tar -xzf tools.tar.gz

Example: Run in Component Directory

commands:
- name: component-init
description: Initialize a component
working_directory: components/terraform/vpc
steps:
- terraform init
- terraform validate

Since components/terraform/vpc is a relative path, it will be resolved against base_path.

tip

Use working_directory: !repo-root . when defining commands in .atmos.d/ at the repository root. This ensures commands work correctly when invoked from any subdirectory in your project.

Using Authentication with Custom Commands

Custom commands can specify an identity field to authenticate before execution. This is useful when commands need to interact with cloud resources that require specific credentials or elevated permissions.

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 all command steps with these environment variables

Example: Custom Command with Authentication

commands:
- name: deploy-infra
description: Deploy infrastructure with superadmin privileges
identity: superadmin # Authenticate as superadmin before running
arguments:
- name: component
description: Component to deploy
required: true
flags:
- name: stack
shorthand: s
description: Stack to deploy to
required: true
steps:
- atmos terraform plan {{ .Arguments.component }} -s {{ .Flags.stack }}
- atmos terraform apply {{ .Arguments.component }} -s {{ .Flags.stack }} -auto-approve

To execute this command:

atmos deploy-infra vpc -s plat-ue2-prod

Atmos will:

  1. Authenticate as superadmin (prompting for MFA if configured)
  2. Set up environment variables pointing to temporary credential files
  3. Execute both terraform plan and terraform apply with those credentials

Example: Multi-Step Command with Authentication

commands:
- name: audit-resources
description: Audit cloud resources with auditor credentials
identity: auditor
flags:
- name: region
shorthand: r
description: AWS region
required: true
steps:
- |
echo "Running audit in {{ .Flags.region }}..."
aws sts get-caller-identity
- |
aws ec2 describe-instances --region {{ .Flags.region }} \
--query 'Reservations[].Instances[].[InstanceId,State.Name,Tags[?Key==`Name`].Value|[0]]' \
--output table
- |
aws s3 ls --region {{ .Flags.region }}

All steps in this command will execute with the auditor identity credentials.

Authentication with Component Config

You can combine identity authentication with component configuration:

commands:
- name: provision-with-auth
description: Provision component with specific identity
identity: infrastructure-admin
arguments:
- name: component
description: Component to provision
flags:
- name: stack
shorthand: s
description: Stack name
required: true
component_config:
component: "{{ .Arguments.component }}"
stack: "{{ .Flags.stack }}"
env:
- key: COMPONENT_REGION
value: "{{ .ComponentConfig.vars.region }}"
steps:
- |
echo "Provisioning {{ .Arguments.component }} in {{ .ComponentConfig.vars.region }}"
echo "Using identity: infrastructure-admin"
aws sts get-caller-identity
- atmos terraform apply {{ .Arguments.component }} -s {{ .Flags.stack }}
tip

Configure identities in your atmos.yaml under the auth section. See the Authentication documentation for configuration details.

Overriding Identity at Runtime

You can override the identity specified in the command configuration using the --identity flag:

# Use the identity from command config
atmos deploy-infra vpc -s plat-ue2-prod

# Override with a different identity
atmos deploy-infra vpc -s plat-ue2-prod --identity developer

# Use no identity (skip authentication even if configured)
atmos deploy-infra vpc -s plat-ue2-prod --identity ""

The --identity flag is automatically added to all custom commands and takes precedence over the identity field in the command configuration.

note

The identity field applies to all steps in the custom command. If you need different identities for different operations, consider using separate custom commands or workflows with per-step identity configuration.

Extended Step Types

Custom commands support the same extended step types as workflows, enabling interactive CLI wizards directly in your custom commands. Use these step types to collect user input, display formatted output, and control execution flow.

Example: Interactive Deployment Command

commands:
- name: deploy-wizard
description: Interactive deployment wizard
steps:
# Collect environment selection
- name: env
type: choose
prompt: "Select target environment"
options:
- dev
- staging
- prod
default: dev

# Show warning for production
- name: prod_warning
type: warn
content: "You are about to deploy to PRODUCTION!"

# Confirm deployment
- name: confirm
type: confirm
prompt: "Deploy to {{ .steps.env.value }}?"
default: false

# Run the deployment
- name: deploy
type: atmos
command: terraform apply vpc -s {{ .steps.env.value }}

# Show success message
- name: done
type: success
content: "Deployment to {{ .steps.env.value }} completed!"

Example: Multi-Component Deploy with Selection

commands:
- name: deploy-components
description: Select and deploy multiple components
steps:
# Multi-select components
- name: components
type: filter
prompt: "Select components to deploy"
multiple: true
options:
- vpc
- eks
- rds
- s3
- lambda

# Show selected components
- name: summary
type: markdown
content: |
## Deployment Summary

You selected **{{ len .steps.components.values }}** components:
{{ range .steps.components.values }}
- {{ . }}
{{ end }}

# Confirm and deploy
- name: confirm
type: confirm
prompt: "Proceed with deployment?"

# Deploy each component
- type: shell
command: |
{{ range .steps.components.values }}
echo "Deploying {{ . }}..."
{{ end }}

Example: Input Collection

commands:
- name: create-ticket
description: Create a deployment ticket
steps:
- name: title
type: input
prompt: "Ticket title"
placeholder: "Brief description of the change"

- name: description
type: write
prompt: "Detailed description"

- name: priority
type: choose
prompt: "Priority level"
options: [low, medium, high, critical]
default: medium

- type: success
content: |
Ticket created:
Title: {{ .steps.title.value }}
Priority: {{ .steps.priority.value }}

Available Step Types Reference

Atmos supports 25+ step types across five categories: Command, Interactive, UI Messages, Output, and Terminal. These step types enable building sophisticated CLI wizards and interactive deployment pipelines.

Step Types Overview

CategoryStep TypesDescription
Commandatmos, shellExecute Atmos or shell commands
Interactiveinput, confirm, choose, filter, file, writeCollect user input
UI Messagessuccess, info, warn, error, markdownDisplay formatted messages
Outputspin, table, pager, format, join, styleFormat and display output
Terminalalert, title, clear, env, exitTerminal control and workflow management

Interactive Step Types

Interactive steps require a TTY (terminal) to run. In CI environments, use --dry-run to preview workflows or set default values.

input

Prompts for single-line text input.

steps:
- name: message
type: input
prompt: "Enter deployment message"
placeholder: "e.g., Fix for ticket JIRA-123"
default: "Routine deployment"
prompt
The prompt text to display
default
Default value if user presses enter
placeholder
Placeholder text shown in the input field
password
Set to true to mask input for sensitive values

confirm

Prompts for yes/no confirmation.

steps:
- name: confirm_prod
type: confirm
prompt: "Deploy to production?"
default: false
prompt
The confirmation question
default
Default value (true or false)

choose

Single selection from a list of options.

steps:
- name: environment
type: choose
prompt: "Select target environment"
options:
- dev
- staging
- prod
default: dev
prompt
The selection prompt
options
List of options to choose from
default
Default selected option

filter

Filterable selection with optional multi-select support.

steps:
- name: components
type: filter
prompt: "Select components to deploy"
multiple: true
limit: 5
options:
- vpc
- eks
- rds
- s3
- lambda
prompt
The selection prompt
options
List of options to filter/select from
multiple
Allow multiple selections (default: false)
limit
Maximum number of visible options
tip

Use {{ .steps.NAME.values }} to access the array of selected values when multiple: true.

file

File picker for selecting files from the filesystem.

steps:
- name: config_file
type: file
prompt: "Select a configuration file"
path: "./configs"
extensions:
- yaml
- yml
- json
prompt
The file selection prompt
path
Starting directory for file browsing
extensions
List of allowed file extensions

write

Multi-line text editor for longer input.

steps:
- name: notes
type: write
prompt: "Enter deployment notes"
placeholder: |
Describe the changes being deployed...

- Change 1
- Change 2
prompt
The editor prompt
placeholder
Placeholder text shown in the editor

UI Message Step Types

These steps display formatted messages to the user. They use the Atmos UI theme for consistent styling.

success

Displays a success message with a green checkmark icon.

steps:
- name: done
type: success
content: "Deployment completed successfully!"

info

Displays an informational message with a blue info icon.

steps:
- name: status
type: info
content: "Processing {{ .steps.count.value }} items..."

warn

Displays a warning message with a yellow warning icon.

steps:
- name: caution
type: warn
content: "This action cannot be undone!"

error

Displays an error message with a red error icon.

steps:
- name: failed
type: error
content: "Configuration validation failed"

markdown

Renders markdown content with full formatting support.

steps:
- name: intro
type: markdown
content: |
# Deployment Wizard

Welcome to the interactive deployment workflow!

This wizard will guide you through:
- **Environment selection**
- **Component configuration**
- **Deployment verification**
content
Markdown content to render. Supports Go templates for dynamic content.

Output Step Types

spin

Displays a spinner while executing a command.

steps:
- name: building
type: spin
title: "Building application..."
command: "npm run build"
timeout: 60s
title
Text displayed next to the spinner
command
Shell command to execute
timeout
Maximum time for command execution (e.g., 30s, 5m)

table

Displays data in a formatted table.

steps:
- name: environments
type: table
title: "Available Environments"
columns:
- name
- region
- status
data:
- name: dev
region: us-east-1
status: active
- name: staging
region: us-west-2
status: active
title
Table title
columns
List of column names
data
Array of row objects with values for each column

join

Joins multiple strings together with a separator.

steps:
- name: combined
type: join
separator: ", "
options:
- "{{ .steps.header.value }}"
- "{{ .steps.body.value }}"
- "{{ .steps.footer.value }}"
options
Array of strings to join (supports templates)
separator
String to join values with (default: newline)
content
Alternative: single template string to resolve and return as-is

pager

Displays content in a scrollable pager.

steps:
- name: show_logs
type: pager
title: "Build Logs"
content: "{{ .steps.build.metadata.stdout }}"

Or load from a file:

steps:
- name: show_readme
type: pager
title: "README"
path: "./README.md"
markdown: true
title
Title displayed at the top of the pager
content
Content to display (supports templates)
path
Path to file to display (alternative to content)
markdown
Render content as markdown (default: auto-detect from .md/.markdown extension)

format

Formats and outputs text using Go templates.

steps:
- name: summary
type: format
content: |
Environment: {{ .steps.env.value }}
Components: {{ .steps.components.values | join ", " }}
content
Go template string to render and output

style

Applies terminal styling to content using lipgloss.

steps:
- name: banner
type: style
content: "Deployment Complete"
foreground: "#00ff00"
border: rounded
padding: "1 2"
bold: true
content
Text to style (supports templates)
foreground
Text color (hex or named)
background
Background color (hex or named)
border
Border style: normal, rounded, thick, double, hidden
border_foreground
Border color
padding
Padding (CSS-like: "1", "1 2", "1 2 1 2")
margin
Margin (CSS-like format)
width
Fixed width in characters
height
Fixed height in lines
align
Text alignment: left, center, right
bold
Bold text
italic
Italic text
underline
Underlined text
strikethrough
Strikethrough text
faint
Dimmed text
markdown
Render content as markdown before styling

linebreak

Outputs blank lines for visual spacing.

steps:
- name: spacer
type: linebreak
count: 2
count
Number of blank lines to output (default: 1)

log

Outputs structured log messages at various levels.

steps:
- name: debug_info
type: log
level: debug
content: "Processing component"
fields:
component: "{{ .steps.select.value }}"
stack: "{{ .env.ATMOS_STACK }}"
level
Log level: trace, debug, info, warn, error
content
Log message (supports templates)
fields
Structured key-value pairs to include

Terminal Step Types

Terminal step types control the terminal environment and workflow execution flow.

alert

Plays a terminal bell sound to notify the user. Useful at the end of long-running workflows.

steps:
- name: notify
type: alert
content: "Workflow complete!" # Optional message
content
Optional message to display after the bell (supports templates)
tip

Use alert at the end of long-running workflows to notify users when the workflow completes, even if they've switched to another window.

title

Sets the terminal window title. Useful for showing workflow progress in the title bar.

steps:
- name: set_title
type: title
content: "Deploying to {{ .steps.env.value }}..."

- name: deploy
type: atmos
command: terraform apply vpc

- name: restore_title
type: title # Empty content restores original title
content
Title text (supports templates). If empty, restores the original title.
note

Respects settings.terminal.title configuration. Some terminals may not support title changes.

clear

Clears the current terminal line. Useful for cleaning up dynamic output.

steps:
- name: clear_line
type: clear

No parameters required.

env

Sets environment variables for subsequent steps in the workflow.

steps:
- name: get_env
type: choose
prompt: "Select environment"
options: [dev, staging, prod]

- name: set_env
type: env
vars:
DEPLOY_ENV: "{{ .steps.get_env.value }}"
AWS_REGION: us-east-1
TF_VAR_environment: "{{ .steps.get_env.value }}"

- name: deploy
type: atmos
command: terraform apply vpc
vars
Map of environment variable names to values (supports templates)
tip

Use env to pass user input or computed values to subsequent shell/atmos commands as environment variables.

exit

Exits the workflow immediately with a specific exit code.

steps:
- name: confirm
type: confirm
prompt: "Continue with deployment?"

- name: abort
type: exit
code: 1
content: "Deployment cancelled by user"
code
Exit code (default: 0). Use non-zero for error conditions.
content
Optional message to display before exiting (supports templates)
warning

The exit step immediately terminates the workflow. No subsequent steps will run.

Variable Passing Between Steps

Steps can access previous step results using Go templates. Each step result is stored in .steps.<name>.

{{ .steps.<name>.value }}
Primary value from the step (string)
{{ .steps.<name>.values }}
Array of values (for multi-select steps)
{{ .steps.<name>.metadata.<key> }}
Metadata like exit_code, stdout, stderr
{{ .steps.<name>.skipped }}
Whether the step was skipped
{{ .steps.<name>.error }}
Error message if the step failed
{{ .env.<VAR> }}
Environment variables

Output Modes

Control how command output is displayed using the output field at the workflow or step level.

workflows:
verbose-deploy:
output: log # Workflow-level default
steps:
- name: plan
type: atmos
command: terraform plan vpc
output: viewport # Step-level override
viewport
Display output in a scrollable pager
raw
Direct passthrough to terminal (default)
log
Grouped output with step headers
none
Suppress output (check exit code only)

Show Configuration

Control automatic formatting and progress display using the show field at the workflow or step level. All options default to false (opt-in).

workflows:
verbose-deploy:
description: Deploy with full visibility
show:
header: true # Display workflow description as styled header
flags: true # Show command-line flags (stack, identity, etc.)
count: true # Show step count prefix [1/5]
command: true # Show step command before execution
progress: true # Show progress bar (TTY only)
steps:
- name: plan
type: atmos
command: terraform plan vpc
show:
command: false # Override: don't show command for this step
header
Display the workflow description as a styled header before the first step executes
flags
Show command-line flag values under the header (e.g., stack: dev, identity: prod-admin)
command
Show each step's command before execution with a $ prefix for shell-like appearance
count
Show step count prefix (e.g., [1/5]) in step headers
progress
Show a right-aligned progress bar during execution (Docker-build style, TTY only)

Step-level show settings override workflow-level defaults via deep merge. Only set fields are overridden; unset fields inherit from the workflow.

TTY Requirements

Interactive steps require a terminal (TTY) to function. In non-interactive environments like CI/CD:

  • Use --dry-run to preview workflow steps without execution
  • Set default values in your workflow configuration
  • Use environment variables instead of interactive prompts
  • Consider using conditional logic to skip interactive steps in CI
  • The progress show option only renders in TTY mode

Using Toolchain Tools

Custom commands 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 command executes.

Automatic Tool Access

If your project has a .tool-versions file:

terraform 1.10.0
kubectl 1.32.0
helm 3.16.0

Custom commands can use these tools directly:

commands:
- name: deploy
description: Deploy infrastructure
steps:
- terraform init
- terraform plan
- kubectl apply -f manifests/

When you run atmos deploy:

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

Declaring Additional Dependencies

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

commands:
- name: validate
description: Validate with specific tool versions
dependencies:
tools:
tflint: "^0.54.0" # Additional tool not in .tool-versions
terraform: "1.9.8" # Override .tool-versions version
steps:
- terraform validate
- tflint --recursive

Dependencies declared in the command take precedence over .tool-versions.

Version Constraints

Dependencies support SemVer constraints:

ConstraintMeaningExample
1.10.3Exact versionOnly version 1.10.3
~> 1.10.0Pessimistic (patch)Includes 1.10.x but not 1.11.0
^1.10.0Compatible (minor)Includes 1.x.x but not 2.0.0
latestLatest availableMost recent version
tip

The combination of .tool-versions for project-wide defaults and per-command dependencies for overrides gives you flexibility while maintaining consistency across your team.

Custom Component Types

Custom commands can define their own component types beyond the built-in terraform, helmfile, and packer types. This enables you to use Atmos's stack configuration system for any tool—Ansible playbooks, Kubernetes manifests, shell scripts, CDK apps, and more.

How It Works

When you define a component: section in a custom command with a type, Atmos:

  1. Registers the custom component type in the component registry
  2. Looks up component configuration from components.<type>.<component> in your stack manifests
  3. Makes the component configuration available via {{ .Component.* }} template variables

Typed Arguments and Flags

To tell Atmos which argument or flag provides the component name and stack name, use semantic types:

  • Arguments: Set type: component or type: stack in the argument definition
  • Flags: Set semantic_type: component or semantic_type: stack in the flag definition

Note: For flags, we use semantic_type because type is already used to specify the data type (string, bool).

Example: Script Runner

Here's a complete example of a custom command that runs script components:

atmos.yaml:

commands:
- name: script
description: "Run script components"
arguments:
- name: component
description: "Component name"
type: component # This argument provides the component name
required: true
flags:
- name: stack
shorthand: s
description: "Stack name"
semantic_type: stack # This flag provides the stack name
required: true
component:
type: script # Define the custom component type
base_path: components/script # Optional: defaults to components/<type>
steps:
- 'echo "Running {{ .Component.component }} in {{ .Component.atmos_stack }}"'
- 'echo "App: {{ .Component.vars.app_name }}"'
- 'echo "Version: {{ .Component.vars.version }}"'
- 'cd {{ .Component.vars.script_dir }} && ./deploy.sh'

stacks/catalog/script/deploy-app.yaml:

components:
script: # Matches component.type in the command
deploy-app: # Component name
vars:
app_name: "myapp"
version: "1.0.0"
script_dir: "${atmos.base_path}/components/script/deploy-app"

stacks/deploy/dev.yaml:

import:
- catalog/script/deploy-app

vars:
stage: dev

components:
script:
deploy-app:
vars:
version: "1.0.0-dev" # Override for dev environment

Usage:

atmos script deploy-app -s dev
# Output:
# Running deploy-app in dev
# App: myapp
# Version: 1.0.0-dev
# (runs deploy.sh)

Available Template Variables

When using custom component types, the {{ .Component }} object contains:

VariableDescription
{{ .Component.component }}Component name
{{ .Component.component_type }}Component type (e.g., "script")
{{ .Component.atmos_stack }}Stack name
{{ .Component.vars.* }}Variables defined in the component
{{ .Component.settings.* }}Settings defined in the component
{{ .Component.env.* }}Environment variables defined in the component

Configuration Options

The component: section supports:

OptionDescription
typeRequired. The component type name (e.g., "script", "ansible", "manifest")
base_pathOptional. Base directory for components of this type. Defaults to components/<type>

Comparison with component_config

The component: section differs from the legacy component_config: in several ways:

Featurecomponent: (new)component_config: (legacy)
Component typeCustom typesTerraform only
Component/stack sourceInferred from typed args/flagsExplicit templates
Template variable{{ .Component.* }}{{ .ComponentConfig.* }}
Stack locationcomponents.<type>.<component>components.terraform.<component>
tip

Use component: for new custom commands. The component_config: approach continues to work for backward compatibility but is limited to Terraform components.

Example: Ansible Playbook Runner

commands:
- name: ansible
description: "Run Ansible playbooks"
arguments:
- name: component
type: component
required: true
flags:
- name: stack
shorthand: s
semantic_type: stack
required: true
- name: check
description: "Run in check mode (dry-run)"
type: bool
component:
type: ansible
base_path: components/ansible
steps:
- |
cd {{ .Component.vars.playbook_dir }}
ansible-playbook {{ .Component.vars.playbook }} \
-i {{ .Component.vars.inventory }} \
--extra-vars "env={{ .Component.vars.environment }}" \
{{ if .Flags.check }}--check{{ end }}

Example: Kubernetes Manifest Deployer

commands:
- name: manifest
description: "Apply Kubernetes manifests"
arguments:
- name: component
type: component
required: true
flags:
- name: stack
shorthand: s
semantic_type: stack
required: true
- name: dry-run
type: bool
component:
type: manifest
steps:
- |
kubectl apply \
--context {{ .Component.vars.cluster_context }} \
--namespace {{ .Component.vars.namespace }} \
{{ if index .Flags "dry-run" }}--dry-run=client{{ end }} \
-f {{ .Component.vars.manifest_path }}

Try It

Explore working examples that demonstrate custom commands in action.

Custom Commands

This example demonstrates how to extend Atmos with custom CLI commands, making it easier for teams to use your toolchain through a single, consistent interface.

Quick Start

# View available custom commands
atmos --help

# Run basic commands
atmos hello
atmos ip
atmos github status
atmos greet Alice
atmos weather --location LAX

Example Structure

custom-commands/
├── atmos.yaml              # Basic custom commands
├── .atmos.d/
│   ├── interactive.yaml    # Interactive step types (choose, confirm, input, etc.)
│   └── advanced.yaml       # Boolean flags, component config, working directory
└── README.md

Basic Commands (atmos.yaml)

Hello World

The simplest custom command:

atmos hello

Get Your IP

Returns your current public IP address:

atmos ip

Nested Commands (GitHub)

Commands can be nested under parent commands:

# Check GitHub status
atmos github status

# Get stargazer count for a repository
atmos github stargazers cloudposse/atmos

Positional Arguments

Pass arguments to commands:

# Uses default "John Doe"
atmos greet

# Pass a custom name
atmos greet Alice

Flags

Use long or short flags:

atmos weather --location LAX
atmos weather -l NYC

Interactive Commands (.atmos.d/interactive.yaml)

Interactive commands create CLI wizards using various step types.

Note: Interactive commands require a TTY (terminal) and won't work in CI/CD pipelines.

Deploy Wizard

Interactive deployment with environment selection and confirmation:

atmos deploy-wizard

Component Selection

Multi-select components with filtering:

atmos select-components

Create Ticket

Collect information with various input types:

atmos create-ticket

Collect Credentials

Secure credential collection with password masking:

atmos collect-credentials

File Selection

Interactive file picker:

atmos select-config

Show Environments

Display data in a formatted table:

atmos show-environments

Build with Progress

Show progress spinner during command execution:

atmos build-with-progress

Advanced Commands (.atmos.d/advanced.yaml)

Boolean Flags

Commands with boolean flags and conditional execution:

# Default behavior (clean=true, verbose=false)
atmos build

# Enable verbose output
atmos build --verbose
# or
atmos build -v

# Disable clean step
atmos build --clean=false

# Dry run mode
atmos build --dry-run

Environment Variables

Commands that set environment variables:

atmos deploy-component vpc -s dev
atmos deploy-component eks -s prod --auto-approve

Component Configuration

Access component configuration in command steps:

# Requires stacks to be configured
atmos show-component vpc -s dev

Working Directory

Run commands from specific directories:

# Run from repository root
atmos run-from-root

Nested Subcommands

Multiple levels of command nesting:

atmos ops status
atmos ops health --verbose
atmos ops restart my-service --force

Trailing Arguments

Pass additional arguments after --:

atmos run myscript.sh -- --arg1 value1 --arg2 value2

Configuration Structure

Custom commands are defined in the commands section of atmos.yaml or any file in .atmos.d/:

commands:
- name: my-command
description: Description shown in help
arguments:
- name: arg-name
description: Argument description
required: true
default: default-value
flags:
- name: flag-name
shorthand: f
description: Flag description
type: bool # or string (default)
default: false
env:
- key: MY_VAR
value: "{{ .Arguments.arg-name }}"
steps:
- echo "Running command"
- "{{ if .Flags.flag-name }}echo 'Flag enabled'{{ end }}"

Available Step Types

Custom commands support various step types for building interactive CLIs:

TypeDescription
Shell command (string)Execute a shell command
markdownDisplay formatted markdown
chooseSingle-select dropdown
filterFilterable multi-select
inputSingle-line text input
writeMulti-line text editor
confirmYes/No confirmation
fileFile picker
toastStyled status message
spinProgress spinner
tableTabular data display
styleStyled bordered content

Template Variables

Steps can access various template variables:

  • {{ .Arguments.name }} - Positional argument value
  • {{ .Flags.name }} - Flag value
  • {{ .TrailingArgs }} - Arguments after --
  • {{ .steps.step_name.value }} - Value from a previous step
  • {{ .steps.step_name.values }} - Array of values (multi-select)
  • {{ .ComponentConfig.vars.xxx }} - Component configuration (requires component_config)
  • {{ .env.VAR }} - Environment variable

Learn More