Skip to main content

component

The component field lets a custom command define its own component type beyond the built-in terraform, helmfile, packer, and ansible types. This brings Atmos's stack configuration system to any tool — AWS CDK, CloudFormation, Pulumi, Bicep, database migrations, and more.

Custom Component Types

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

Passing Secrets to Custom Components

A custom component's env section is exported as real environment variables to every step, just like the built-in terraform/helmfile/packer/ansible component types. This makes secrets easy to pass securely: declare them under secrets.vars, reference them with !secret in the component env section, and read them in your steps (or the scripts they invoke) as ordinary environment variables.

stacks/deploy/prod.yaml
components:
script:
deploy-app:
secrets:
vars:
DB_PASSWORD:
description: "Application database password"
store: ssm-secrets # a `secret: true` store; or use `sops: <provider>`
required: true
env:
DB_PASSWORD: !secret DB_PASSWORD
vars:
app_name: "myapp"
atmos.yaml
commands:
- name: deploy
arguments:
- name: component
type: component
required: true
flags:
- name: stack
shorthand: s
semantic_type: stack
required: true
component:
type: script
steps:
# $DB_PASSWORD is exported from the component env section — no need to inline it.
- './deploy.sh'
Don't inline secrets into the command string

Reference secrets as environment variables ($DB_PASSWORD), not by inlining the template value (echo {{ .Component.env.DB_PASSWORD }}). An inlined value lands in shell history and process arguments. Step output is masked, but that is defense‑in‑depth, not the primary mitigation.

See Passing Secrets for the full list of supported secret backends (AWS SSM/Secrets Manager, Vault, Azure Key Vault, GCP Secret Manager, 1Password, SOPS, and more).

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