Skip to main content
Use this skill
atmos ai skill install atmos-components
SKILL.md14.3 KB
View on GitHub

Atmos Component Architecture

Components are the building blocks of infrastructure in Atmos. Each component is an opinionated, reusable unit of infrastructure-as-code -- typically a Terraform root module -- that solves a specific problem. Atmos separates the component implementation (code) from its configuration (stack manifests), enabling one implementation to be deployed many times with different settings.

What Components Are

In Atmos, a component consists of two parts:

  1. Implementation -- The infrastructure code itself (a Terraform root module, Helmfile, or Packer template) stored in the components/ directory.
  2. Configuration -- The settings that customize how the component is deployed, defined in stack manifests under the components section.

This separation is fundamental: you write the Terraform module once, then configure it differently for each environment, region, and account through stack YAML files.

Component Types

Atmos natively supports three component types:

TypeImplementation LocationPurpose
Terraform / OpenTofucomponents/terraform/<name>/Provision cloud infrastructure resources
Helmfilecomponents/helmfile/<name>/Deploy Helm charts to Kubernetes clusters
Packercomponents/packer/<name>/Build machine images (AMIs, VM images)

Terraform is by far the most common type. Custom commands can extend Atmos to support any tooling.

Directory Structure

Components are stored in your project's components/ directory, organized by type:

components/
terraform/
vpc/
main.tf
variables.tf
outputs.tf
versions.tf
eks/
cluster/
main.tf
variables.tf
outputs.tf
s3-bucket/
main.tf
variables.tf
outputs.tf
iam-role/
main.tf
variables.tf
outputs.tf
helmfile/
nginx-ingress/
helmfile.yaml
cert-manager/
helmfile.yaml
packer/
ubuntu-base/
template.pkr.hcl

The base path for Terraform components is configured in atmos.yaml:

components:
terraform:
base_path: "components/terraform"

Nested directories are supported. A component at components/terraform/eks/cluster/ is referenced as eks/cluster in stack configurations.

Component Configuration in Stacks

Components are configured in the components section of stack manifests:

components:
terraform:
vpc:
metadata:
component: vpc # Points to components/terraform/vpc/
vars:
cidr_block: "10.0.0.0/16"
availability_zones:
- us-east-1a
- us-east-1b
settings:
validation:
check-cidr:
schema_type: jsonschema
schema_path: schemas/vpc.json

eks-cluster:
metadata:
component: eks/cluster # Points to components/terraform/eks/cluster/
vars:
cluster_name: prod-eks
kubernetes_version: "1.28"

Each component configuration can include these sections:

SectionPurpose
metadataComponent location, inheritance, type, and Atmos behavior
varsInput variables passed to Terraform/Helmfile/Packer
envEnvironment variables set during execution
settingsIntegration metadata such as Atlantis, validation, and custom settings
dependenciesComponent, file, folder, and tool dependencies
hooksLifecycle event handlers
backend / backend_typeTerraform state backend configuration
providersTerraform provider configuration
commandOverride the executable (e.g., tofu instead of terraform)
authAuthentication identity reference

Dependencies

Use dependencies.components for component ordering and affected/dependent analysis:

components:
terraform:
app:
dependencies:
components:
- component: vpc
- component: database
stack: plat-ue2-prod
- kind: file
path: configs/app.yaml
- kind: folder
path: src/lambda

Use dependencies.tools for runtime CLIs at global, component-type, or per-component scope; Atmos auto-installs and injects them, so do not add a separate install step.

Do not add new settings.depends_on examples. If a repository already uses that legacy field, recommend migrating it to dependencies.components.

Source Provisioning and Workdirs

Components can declare source in stack config for just-in-time provisioning from Git, OCI, S3, HTTP, or local paths. Treat source provisioning as part of component configuration: it controls how the component implementation is fetched, while vars, env, backend, and other sections control how that implementation is run.

Use component source provisioning when different stacks need different remote component versions, or when the repository should not commit the fetched implementation. When source provisioning is used, prefer provision.workdir.enabled: true so each component-stack instance gets an isolated execution directory.

components:
terraform:
vpc:
source: "github.com/cloudposse-terraform-components/aws-vpc.git?ref=1.450.0"
provision:
workdir:
enabled: true

Atmos provisions sources automatically before Terraform commands when needed. Use explicit source commands when an agent needs to inspect, refresh, or clean the fetched implementation:

atmos terraform source pull vpc --stack dev
atmos terraform source describe vpc --stack dev
atmos terraform source list --stack dev
atmos terraform source delete vpc --stack dev
atmos list sources

For protected sources, route identity and provider details to atmos-auth. For checked-in copies of remote components, route to atmos-vendoring; vendoring and source provisioning are alternatives for obtaining component implementation code.

Abstract vs Real Components

Abstract Components

Mark a component as metadata.type: abstract to create a blueprint that cannot be deployed directly:

components:
terraform:
vpc/defaults:
metadata:
type: abstract
component: vpc
vars:
enabled: true
nat_gateway_enabled: true
max_subnet_count: 3
vpc_flow_logs_enabled: true

Abstract components:

  • Cannot be provisioned with atmos terraform apply (Atmos returns an error).
  • Do not appear in atmos describe stacks output by default.
  • Serve as base configurations for real components to inherit from.

Real Components (Default)

If metadata.type is not specified, the component is real and can be deployed:

components:
terraform:
vpc:
metadata:
inherits:
- vpc/defaults
vars:
vpc_cidr: "10.0.0.0/16"

Component Inheritance

Single Inheritance

Use metadata.inherits to inherit configuration from a base component:

components:
terraform:
vpc/defaults:
metadata:
type: abstract
component: vpc
vars:
enabled: true
nat_gateway_enabled: true

vpc:
metadata:
inherits:
- vpc/defaults
vars:
nat_gateway_enabled: false # Override inherited value

The derived component receives all vars, env, settings, hooks, backend, providers, and command from the base, then its own values are deep-merged on top.

Multiple Inheritance

A component can inherit from multiple bases. Entries are processed in order with later entries having higher precedence:

components:
terraform:
rds:
metadata:
component: rds
inherits:
- base/defaults # Applied first
- base/logging # Applied second
- base/production # Applied last (highest base precedence)
vars:
name: my-database # Inline has highest precedence

This enables composing "traits" -- reusable abstract components that represent independent configuration concerns (logging, security, sizing, environment settings).

metadata.component

The metadata.component field maps an Atmos component name to its Terraform root module directory:

components:
terraform:
vpc-main:
metadata:
component: vpc # Uses components/terraform/vpc/
vars:
name: main-vpc

vpc-isolated:
metadata:
component: vpc # Same Terraform module
vars:
name: isolated-vpc
enable_internet_gateway: false

Both components use the same Terraform code but maintain separate state files and configurations. This is the multiple component instances pattern.

Multiple Component Instances

Deploy the same Terraform module multiple times in the same stack by giving each instance a unique Atmos component name:

components:
terraform:
vpc/1:
metadata:
component: vpc
inherits:
- vpc/defaults
vars:
name: vpc-1
ipv4_primary_cidr_block: 10.9.0.0/18

vpc/2:
metadata:
component: vpc
inherits:
- vpc/defaults
vars:
name: vpc-2
ipv4_primary_cidr_block: 10.10.0.0/18

Each instance has its own Terraform state and is independently deployable:

atmos terraform apply vpc/1 -s plat-ue2-prod
atmos terraform apply vpc/2 -s plat-ue2-prod

metadata Section Fields

All fields available in the metadata section:

FieldTypeDescription
componentstringPath to Terraform root module relative to components base path
inheritslistList of component names to inherit configuration from
typestringabstract (non-deployable) or real (default, deployable)
namestringStable logical identity for workspace key prefix
enabledbooleanEnable or disable the component (default: true)
lockedbooleanPrevent modifications to the component
terraform_workspacestringExplicit workspace name override
terraform_workspace_patternstringWorkspace name pattern with tokens
custommapUser-defined metadata (preserved, not interpreted by Atmos)

Catalog Patterns

The stacks/catalog/ directory is the conventional location for reusable component configurations:

stacks/
catalog/
vpc/
_defaults.yaml # Abstract base for all VPC instances
eks/
_defaults.yaml
cluster.yaml
s3-bucket/
_defaults.yaml
iam-role/
_defaults.yaml

Catalog files define abstract components with sensible defaults. Top-level stacks import from the catalog and override only what differs:

# stacks/catalog/vpc/_defaults.yaml
components:
terraform:
vpc/defaults:
metadata:
type: abstract
component: vpc
vars:
enabled: true
nat_gateway_enabled: true
max_subnet_count: 3
# stacks/orgs/acme/plat/prod/us-east-1.yaml
import:
- catalog/vpc/_defaults

components:
terraform:
vpc:
metadata:
inherits:
- vpc/defaults
vars:
vpc_cidr: "10.0.0.0/16"

Mixins for Reusable Configuration

Mixins are small, focused configuration snippets that alter component behavior. They are typically stored in stacks/mixins/ and imported into stacks:

stacks/
mixins/
region/
us-east-1.yaml
us-east-2.yaml
us-west-2.yaml
stage/
dev.yaml
staging.yaml
prod.yaml
tenant/
plat.yaml
# stacks/mixins/region/us-east-2.yaml
vars:
region: us-east-2
environment: ue2
# stacks/orgs/acme/plat/prod/us-east-2.yaml
import:
- mixins/region/us-east-2
- mixins/stage/prod
- catalog/vpc/_defaults

Remote State Access Between Components

Components can access outputs from other components using the remote-state module or YAML functions:

# Using YAML functions (state access is the fastest option)
components:
terraform:
eks-cluster:
vars:
vpc_id: !terraform.state vpc vpc_id
subnet_ids: !terraform.state vpc private_subnet_ids

For a cold terraform plan --all, upstream state may not exist yet even though component dependencies establish deployment order. Put a provider-valid fallback in the YQ expression; the real state value supersedes it after the upstream component deploys:

components:
terraform:
eks-cluster:
vars:
vpc_id: !terraform.state vpc '.vpc_id // "vpc-mock"'

For the Terraform-side approach, use the remote-state module:

# In components/terraform/eks/cluster/remote-state.tf
module "vpc" {
source = "cloudposse/stack-config/yaml//modules/remote-state"
version = "1.5.0"

component = "vpc"
}

This reads the VPC component's Terraform outputs from the same or a different stack.

Best Practices

  1. One concern per component: Each component should provision a single logical piece of infrastructure (VPC, EKS cluster, database). Do not combine resources with different lifecycles.
  2. Use abstract base components: Define catalog defaults as abstract components and inherit from them.
  3. Keep inheritance chains shallow: Limit to 2-3 levels for readability and debuggability.
  4. Use metadata.component for instances: When deploying the same module multiple times, use metadata.component to share the implementation.
  5. Use metadata.name for versioning: Set metadata.name to maintain stable Terraform workspace key prefixes across version upgrades.
  6. Design for reuse: Components should accept configuration through variables, not hard-coded values. Use the catalog pattern to define sensible defaults.
  7. Use atmos describe component: Always verify the resolved configuration before applying changes.
atmos describe component vpc -s plat-ue2-prod

References