Skip to main content

Terraform/OpenTofu

Atmos manages Terraform root modules as components, handling workspace selection, backend configuration, provider overrides, and variable generation.

Overview​

In Atmos, a Terraform component is a Terraform root module stored in your components/terraform/ directory. Atmos orchestrates these components by:

  • Generating variables from stack configurations into .tfvars files
  • Managing workspaces automatically based on stack context
  • Configuring backends for state storage
  • Generating providers with dynamic configurations
  • Orchestrating execution across multiple components and stacks
Disambiguation
  • Terraform Component is a Terraform Root Module stored typically in components/terraform/$name that consists of the resources defined in the .tf files in a working directory (e.g. components/terraform/infra/vpc)

  • Stack provides configuration (variables and other settings) for a Terraform Component and is defined in one or more Atmos stack manifests (a.k.a. stack config files)

Provisioning Components​

To provision a Terraform component using the atmos CLI:

Terminal
atmos terraform plan eks --stack=ue2-dev
atmos terraform apply eks --stack=ue2-dev

where:

  • eks is the Terraform component to provision (from the components/terraform folder)
  • --stack=ue2-dev is the stack to provision the component into

Short versions of all command-line arguments can be used:

Terminal
atmos terraform plan eks -s ue2-dev
atmos terraform apply eks -s ue2-dev

The atmos terraform deploy command executes terraform apply -auto-approve to provision components in stacks without user interaction:

Terminal
atmos terraform deploy eks -s ue2-dev

Stack Configuration​

Configure Terraform components in your stack manifests:

components:
terraform:
vpc:
metadata:
type: real
component: vpc
inherits:
- vpc-defaults
vars:
enabled: true
name: primary
cidr_block: "10.0.0.0/16"
env:
TF_LOG: INFO
settings:
spacelift:
workspace_enabled: true

See Stack Configuration for the complete schema reference.

Using Submodules (Child Modules)​

If your components rely on local submodules, our convention is to use a modules/ subfolder of the component to store them.

Try It​

Explore a complete, working example that provisions a small AWS application stack — entirely offline, against a local AWS emulator.

atmos quick start advanced
 
00:00.0 / 00:00.0

Atmos Quick Start (Advanced)

Atmos is a universal tool for DevOps and cloud automation. This advanced example provisions a small AWS application stack against a local AWS emulator (Floci), so you can learn the patterns end to end without a real AWS account or any credentials.

Follow the Quick Start: Advanced guide for a step-by-step walkthrough of this repository.

It's just plain Terraform

The components in components/terraform/ are vanilla Terraform — raw aws_* resources using only the official hashicorp/aws provider. There are no Cloud Posse modules and no special wrappers. Atmos is a bring-your-own-Terraform orchestrator: it never requires you to rewrite your Terraform or adopt proprietary components. Each component carries only a stock providers.tf (provider "aws" { region = var.region }) with no endpoint or credentials configuration — the local-aws identity (kind: aws/emulator) generates a providers_override.tf.json that points the provider at the emulator at runtime, so the exact same code deploys unchanged against real AWS.

ComponentWhat it is (plain Terraform)
kms-keyaws_kms_key + alias
s3-bucketaws_s3_bucket (+ versioning, SSE)
dynamodb-tableaws_dynamodb_table
sns-topicaws_sns_topic
sqs-queueaws_sqs_queue (+ policy)
app-configpublishes resolved config + secrets to SSM Parameter Store

The components are wired together with Atmos features — not Terraform remote_state data sources: stack templates build predictable coordinates, dependency metadata controls graph order, store hooks publish applied outputs, and !secret resolves declared secrets from the SSM/Secrets Manager stores.

Run it end to end

Everything runs against the local emulator. You need Atmos and a container runtime (Docker or Podman). Atmos installs the pinned OpenTofu 1.10.10 and Checkov through its toolchain when a component needs them; no separate Terraform or OpenTofu installation is required. Initial tool and provider downloads require internet access.

The test custom command brings up the emulator, seeds secrets, plans and applies the full component DAG in dependency order, confirms the cross-component wiring resolves, then tears everything down:

atmos test -s plat-ue2-dev

Or run the same steps by hand:

# Start the shared local AWS emulator.
atmos emulator up aws -s local

# Lint every stack manifest.
atmos validate stacks

# One-time setup on macOS/Windows: trust the local Terraform registry-cache proxy.
atmos terraform cache trust

# Seed the required app-config secrets from the committed demo dotenv file.
# `secret init` also accepts the file through stdin: `atmos secret init < .env.local ...`.
atmos secret init --input .env.local -s plat-ue2-dev -c app-config

# Plan every component before state exists. `--use-mocks` fills Terraform lookup
# functions from literal, component-owned mocks wherever the remote state is still cold
# (real state wins once it exists; `--use-mocks=always` ignores state). Exit code 2
# means Terraform found changes and is expected for a first plan.
atmos terraform plan --all -s plat-ue2-dev --use-mocks

# Apply every component in dependency order (the S3 state backend is auto-provisioned
# inside the emulator via `provision.backend.enabled`).
atmos terraform deploy --all -s plat-ue2-dev

# Inspect a component and see where every value came from.
atmos describe component app-config -s plat-ue2-dev --provenance
atmos list instances --format tree --provenance

# Tear down.
atmos terraform destroy --all -s plat-ue2-dev -auto-approve
atmos emulator down aws -s local

Available stacks: local (the emulator), plat-ue2-{dev,staging,prod} (us-east-2), and plat-uw2-{dev,staging,prod} (us-west-2).

How the environments differ

The three environments deploy the same services with different settings - that's the whole point of the layered configuration. The catalog defines each component's defaults once; each region manifest sets the stage-specific values directly. Open stacks/orgs/acme/plat/prod/us-east-2.yaml and you can see exactly what makes that prod region different.

Settingdev (ephemeral)staging (middle)prod (hardened)
s3-bucket force_destroytruetruefalse
s3-bucket versioning_enabledfalsetruetrue
kms-key deletion_window_in_days71430
kms-key enable_key_rotationfalsetruetrue
sqs-queue message_retention_seconds86400 (1d)345600 (4d)1209600 (14d)

See it resolved per stage (no deploy required):

atmos describe component s3-bucket -s plat-ue2-dev
atmos describe component s3-bucket -s plat-ue2-prod

Tags and labels

Every component in the catalog carries metadata.tags (a list, matched with any) and metadata.labels (a map, matched with all) — see the Component Metadata reference. Use them to select components across the whole stack without listing names:

# Everything tagged "messaging" (sns-topic, sqs-queue) — regardless of stack.
atmos list components --tags messaging

# Everything labeled tier=foundational (kms-key, s3-bucket) — the components
# everything else in this stack depends on.
atmos list components --labels tier=foundational

# Plan (or deploy/destroy) only the messaging components for a stack.
atmos terraform plan --all --tags messaging -s plat-ue2-dev --use-mocks

# Plan only the foundational tier — composes with --all the same way --affected does.
atmos terraform plan --all --labels tier=foundational -s plat-ue2-dev --use-mocks

Operator commands

This example also registers operator-focused custom commands in atmos.yaml:

atmos operator status -s <stack> # show stack tree, instances, catalog, and next commands
atmos operator inspect <component> -s <stack> # describe component config with provenance

For the full CLI configuration and command reference, see Atmos CLI.