| name | atmos-migration | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| description | Migrating to Atmos from existing IaC: techniques, tactics, and design patterns for native Terraform and Terraform Workspaces — minimum-disruption paths, file-layout options, workspace mapping, and the remote-state bridge for progressive migration. This skill also explains how to move from asdf, aqua, tfenv, tofuenv, tenv, or a Homebrew Brewfile to the Atmos toolchain. | |||||||||
| metadata |
|
|||||||||
| references |
|
This skill is the agent's decision guide for migrating an existing Terraform repository to Atmos.
Atmos is designed to adopt an existing repo without forcing a reorganization -- the canonical
components/terraform/ layout is a recommendation, not a requirement. Lead with the minimum
change that delivers value, then escalate only as the user's needs grow.
For full prose tutorials aimed at end users, link to:
- Migrating from Native Terraform
- Migrating from Terraform Workspaces
- Migrating from Terragrunt (not covered by this skill)
Everything in this skill applies identically to Terraform and OpenTofu. Atmos invokes
whichever binary is configured (components.terraform.command in atmos.yaml, defaulting to
terraform). Migration paths, file layouts, and the remote-state bridge are the same regardless
of which binary the user is running. Use the user's terminology -- if they say "OpenTofu," use
"OpenTofu" in your responses.
These principles override default agent instincts. Internalize them before proposing changes to a user's repo.
- Migration is opt-in, not all-or-nothing. Atmos does not require a filesystem
reorganization. Point
base_pathat the user's existing layout (e.g.,base_path: "terraform"orbase_path: ".") when preserving layout lowers adoption risk. Thecomponents/terraform/convention is still the best-practice layout for new or fully migrated repos because Atmos supports multiple toolchains (Terraform, Helmfile, Packer, Ansible); it is not a prerequisite for adopting Atmos in Terraform-only repos. - Existing
.tfvarsfiles may be kept during migration. Use!includeto pull them into stacks when the user wants minimal disruption. Converting values into native stack YAML remains the best-practice end state when the user wants deep-merge inheritance and richer stack composition, but it can happen progressively. - No Terraform code changes are required. Don't rewrite providers, backends, or modules
during migration. Atmos generates
backend.tf.jsonand*.auto.tfvars.jsonat runtime. - Workspaces are not the enemy. If the user has
terraform.workspace-driven environments, Atmos can map onto their existing state viametadata.terraform_workspaceandworkspace_key_prefix. They do not have to abandon their workspace state to adopt Atmos. - Prefer YAML functions over Gomplate datasources. When both can express the same thing
(
!includevsgomplate.datasourcesfor files,!execvs templated shell,!envvsgomplate getenv,!storevs custom datasource URLs), reach for the YAML function first. YAML functions are type-safe, can't break YAML parsing, produce clear errors, and don't require enabling Gomplate. See the atmos-yaml-functions and atmos-templates skills for the boundary. - Crawl → walk → run. Get the user to a working
atmos terraform planin 20 minutes; defer inheritance, catalogs, and multi-account hierarchies until they have a concrete need.
Before proposing any change, identify which source pattern the user has. Each routes to a different reference:
| User has... | Use reference |
|---|---|
One TF root module, env config via .tfvars or env vars |
from-native-terraform.md |
| Multiple TF root modules in scattered dirs | from-native-terraform.md |
terraform.workspace-driven environments with shared state backend |
from-terraform-workspaces.md |
| Need to read outputs from un-migrated TF (legacy or another repo) | remote-state-bridge.md |
The remote-state-bridge pattern is what makes progressive, component-by-component migration possible. Without it, a team is forced into a big-bang cutover. Cover it any time the user has existing Terraform state they need to read from new Atmos components.
This section covers a topic separate from the IaC-layout question above. A user can migrate the Terraform or OpenTofu layout, the tool-version manager, or both. Each choice is independent.
If the user currently pins CLI tool versions with asdf, aqua, tfenv, tofuenv, tenv, or a Homebrew Brewfile, use the matching reference below. Do not write a new config translation by hand.
| Current tool | Reference |
|---|---|
| asdf | from-asdf.md |
aqua CLI (aqua.yaml) |
from-aqua.md |
| tfenv | from-tfenv.md |
| tofuenv | from-tofuenv.md |
| tenv | from-tenv.md |
| Homebrew Brewfile | from-homebrew-brewfile.md |
Each reference includes a command-mapping table. This table shows the old tool's commands next to the equivalent Atmos toolchain command. Use it to translate familiar commands directly.
Each reference also includes a Shell Integration section. Most tools that a user migrates from add
themselves to every shell automatically, through a shim on PATH. The Atmos toolchain does not do
this by default. The Atmos toolchain resolves tools only while an atmos <subcommand> runs.
A user can still get the old shell experience back, where a plain terraform command works in any
shell. This is a supported feature, not a missing feature. To enable it, add atmos toolchain env
or atmos toolchain path to ~/.bashrc, ~/.zshrc, the fish config, or the PowerShell profile.
Always tell the user about this option when they are used to a shim-based tool.
When a user says "I want to try Atmos on my existing repo," this is the checklist. Do not deviate unless the user's setup requires it.
- Install Atmos. See
atmos.tools/install. - Create
atmos.yamlat the repo root, pointingbase_pathandcomponents.terraform.base_pathat the user's existing layout. Do not ask them to move files. - Create one stack file for one environment. Use
!includeof an existing.tfvarsfile so nothing has to be rewritten:# stacks/dev.yaml import: - _defaults components: terraform: vpc: vars: !include ../path/to/existing/dev.tfvars
- Run
atmos terraform plan vpc -s devand confirm output matches whatterraform plan -var-file=dev.tfvarsproduced before.
A working reference for this shape lives at examples/native-terraform/ in the Atmos repo.
Pick the layout that matches the user's migration goals. components/terraform/ is the recommended
Atmos convention, especially for new repos or multi-toolchain projects, but existing layouts can be
preserved when the user wants a lower-disruption migration.
base_path |
Use when |
|---|---|
base_path: "." |
TF root modules live at the repo root; user wants zero file moves |
base_path: "terraform" |
TF-only repo with code already in terraform/; preserve dir name |
base_path: "." + components.terraform.base_path: "components/terraform" |
Multi-toolchain or new repo; canonical Atmos layout |
For deeper organization patterns (multi-region, multi-account, org hierarchies), defer to the atmos-design-patterns skill.
This is a recurring footgun -- agents reach for Gomplate datasources when a YAML function would be safer and clearer. Prefer the right column:
| Goal | Reach for (NOT this) | Use instead |
|---|---|---|
| Include a file's contents | gomplate.datasources with file URL |
!include path/to/file |
| Read an environment variable | gomplate getenv "FOO" |
!env FOO |
| Run a shell command | Template + gomplate exec |
!exec "command" |
| Read a store value | Custom datasource URL | !store store_name component stack key |
| Read Terraform output | Templated remote-state datasource | !terraform.state component output |
| Get current AWS account ID | gomplate.datasources AWS plugin |
!aws.account_id |
YAML functions are type-safe, produce clear errors, work without enabling Gomplate, and don't require keeping templates valid YAML. Reserve Go templates for control flow (conditionals, loops, dynamic keys) that YAML functions cannot express. See atmos-templates for when Go templates are the right tool.
Lead with this when a user fears a big rewrite. None of the following must change to adopt Atmos:
- Terraform code -- providers, resources, data sources, modules all stay as-is.
- Module sources --
source = "../../modules/foo"or registry sources keep working. - Backend code -- delete the
backend "s3" {}block from.tffiles (Atmos generatesbackend.tf.json), or leave it and disable backend generation inatmos.yaml. Either works. .tfvarsfiles -- consumed via!include; convert to YAML later if/when the user wants deep-merge inheritance.- Custom provider configuration -- providers stay in
.tffiles; pass env vars via stackenv:or vars via stackvars:.
After the minimum migration is working, the user will often ask "how do I do X next?" Route those questions to the right skill:
- Organizing many stacks (orgs, tenants, accounts, regions) → atmos-design-patterns
- Abstract components, inheritance, catalog patterns → atmos-components
- Deep merging, imports, overrides → atmos-stacks
- Vendoring third-party components → atmos-vendoring
- Authentication / provider credentials → atmos-auth
- Validation policies (OPA, JSON Schema) → atmos-validation
- CI/CD with affected-detection → atmos-ci
- Cross-component data sharing via stores → atmos-stores
- Toolchain configuration (
dependencies.tools, registries, verification) → atmos-toolchain
Things to push back on if a user (or another agent) proposes them during migration:
- "You must move all Terraform into
components/terraform/before using Atmos." No -- that is the recommended layout, not a requirement. Let the user choose between adopting the best-practice layout now or pointingbase_pathat the existing layout and reorganizing later. - "You must rewrite all
.tfvarsas YAML before running Atmos." No -- native stack YAML is the best-practice destination for inheritance and composition, but!includelets users keep existing.tfvarsduring a progressive migration. - "Delete your workspace state and start over." No -- bridge it with
metadata.terraform_workspaceand the remote-state-bridge pattern. - "Add Gomplate datasources for everything." No -- reach for YAML functions first.
- "Adopt the full multi-account org hierarchy on day one." No -- start with one stack file.
- "Copy
aqua.yamlpackages into Atmos verbatim." Do not do this. Atmos supports only part of the Aqua registry schema. Check the Functional Gaps table in from-aqua.md first. - "Replace the whole Brewfile with Atmos toolchain." Do not do this. Casks,
masentries, and source-built formulae are out of scope. See from-homebrew-brewfile.md.
- References/from-native-terraform.md -- scenario-keyed recipes for vanilla TF migration
- References/from-terraform-workspaces.md -- mapping workspaces to stacks without losing state
- References/remote-state-bridge.md -- the dummy-component and abstract-component patterns for reading state from un-migrated or external Terraform
- References/from-asdf.md: steps to move
.tool-versionsand asdf plugins to the Atmos toolchain. Includes a command mapping and shell integration steps. - References/from-aqua.md: steps to move
aqua.yamlpackages to the Atmos toolchain. Includes the schema-gap table, a command mapping, and shell integration steps. - References/from-tfenv.md: steps to move
.terraform-versionandtfenv usepins to the Atmos toolchain. Includes a command mapping and shell integration steps. - References/from-tofuenv.md: steps to move
.opentofu-versionandtofuenv usepins to the Atmos toolchain. Includes a command mapping and shell integration steps. - References/from-tenv.md: steps to move tenv's version files for Terraform, OpenTofu, Terragrunt, and TFLint to the Atmos toolchain. Includes a command mapping and shell integration steps.
- References/from-homebrew-brewfile.md: steps to move the CLI-tool part of a Brewfile to the Atmos toolchain. Includes the partial-scope rules, a command mapping, and shell integration steps.