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"
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:
- Check if tflint 0.54.0 is installed in the toolchain directory
- Install it from the toolchain registry if missing
- 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
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-rootYAML 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.
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:
- Authenticate using the specified identity (prompting for MFA if required)
- Write temporary credentials to a file
- Set environment variables pointing to the credential files (
AWS_SHARED_CREDENTIALS_FILE,AWS_CONFIG_FILE,AWS_PROFILE, etc.) - 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:
- Authenticate as
superadmin(prompting for MFA if configured) - Set up environment variables pointing to temporary credential files
- Execute both
terraform planandterraform applywith 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 }}
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.
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
| Category | Step Types | Description |
|---|---|---|
| Command | atmos, shell | Execute Atmos or shell commands |
| Interactive | input, confirm, choose, filter, file, write | Collect user input |
| UI Messages | success, info, warn, error, markdown | Display formatted messages |
| Output | spin, table, pager, format, join, style | Format and display output |
| Terminal | alert, title, clear, env, exit | Terminal 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
trueto 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 (
trueorfalse)
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
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)
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.
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)
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)
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-runto 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
progressshow 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:
- Atmos reads
.tool-versionsto identify required tools - Missing tools are automatically installed
- PATH is updated to include toolchain binaries
- 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:
| 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 |
The combination of .tool-versions for project-wide defaults and per-command dependencies for overrides gives you flexibility while maintaining consistency across your team.
Related Documentation
- Toolchain Configuration - Configure tool version management
- Workflows - Workflows also support toolchain integration
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:
- Registers the custom component type in the component registry
- Looks up component configuration from
components.<type>.<component>in your stack manifests - 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: componentortype: stackin the argument definition - Flags: Set
semantic_type: componentorsemantic_type: stackin 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:
| Variable | Description |
|---|---|
{{ .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:
| Option | Description |
|---|---|
type | Required. The component type name (e.g., "script", "ansible", "manifest") |
base_path | Optional. 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:
| Feature | component: (new) | component_config: (legacy) |
|---|---|---|
| Component type | Custom types | Terraform only |
| Component/stack source | Inferred from typed args/flags | Explicit templates |
| Template variable | {{ .Component.* }} | {{ .ComponentConfig.* }} |
| Stack location | components.<type>.<component> | components.terraform.<component> |
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.