| name | atmos-migration |
| description | This skill helps you migrate a repository to Atmos. It covers native Terraform, Terraform Workspaces, Terramate, Terragrunt, Makefiles, Justfiles, and Taskfiles. It gives minimum-disruption paths, file-layout options, workspace mapping, task-to-command mapping, generate_hcl/script decomposition, and the remote-state bridge for a step-by-step migration; also covers migrating CLI tool-version management from mise or Aqua CLI to the Atmos toolchain. |
| metadata | {"copyright":"Copyright Cloud Posse, LLC 2026","version":"1.0.0","category":"state-versioning"} |
| references | ["references/from-native-terraform.md","references/from-terraform-workspaces.md","references/remote-state-bridge.md","references/from-terramate.md","references/from-makefile.md","references/from-justfile.md","references/from-taskfile.md","references/from-component-updater.md","references/from-terragrunt.md","references/from-mise.md","references/from-aqua.md"] |
Migrating to Atmos
Overview
This skill is a decision guide. Use it to migrate an existing Terraform repository to Atmos.
Atmos can adopt an existing repository without a reorganization. The components/terraform/
layout is a recommendation. It is not a requirement. Start with the smallest change that gives
value. Add more only when the user has a real need for it.
This skill also covers migrating CLI tool-version management from mise or Aqua CLI to the Atmos
toolchain -- see from-mise.md and
from-aqua.md in the routing table below.
For full tutorials for end users, see:
Terraform or OpenTofu
This skill applies the same way to Terraform and to OpenTofu. Atmos runs the binary set in
components.terraform.command in atmos.yaml. The default binary is terraform. The migration
steps, file layouts, and the remote-state bridge do not change based on the binary. Use the same
word the user uses. If the user says "OpenTofu," write "OpenTofu" in your response.
Core Principles
These principles come before your normal instincts. Read them before you propose a change to the
user's repository.
- Migration is opt-in, not all-or-nothing. Atmos does not require a filesystem
reorganization. Point
base_path at the user's existing layout (e.g., base_path: "terraform"
or base_path: ".") when preserving layout lowers adoption risk. The components/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
.tfvars files may be kept during migration. Use !include to 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.json and *.auto.tfvars.json at runtime.
- Workspaces are not the enemy. If the user has
terraform.workspace-driven environments,
Atmos can map onto their existing state via metadata.terraform_workspace and
workspace_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
(
!include vs gomplate.datasources for files, !exec vs templated shell, !env vs
gomplate getenv, !store vs 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 plan in 20 minutes; defer
inheritance, catalogs, and multi-account hierarchies until they have a concrete need.
- Task runners are not a blocker. Atmos custom commands and workflows can replace the
targets, recipes, and tasks that Make, Just, and Task provide. This doesn't have to happen all
at once — a Makefile, Justfile, or Taskfile can stay as a thin wrapper around commands
during migration, the same incremental approach described in Principle 6. The end state turns
each leaf target into a custom command; a target chain usually stays a custom command too,
using / for its prerequisites. Reserve
workflows for fixed, multi-step orchestration across more than one component — not every
dependency chain needs one.
Decide the Migration Shape First
Find the user's source pattern before you propose any change. Each pattern points to a different
reference file:
The remote-state-bridge pattern makes progressive migration possible. It lets a team migrate one
component at a time. Without it, the team must migrate everything at once. Use this pattern when
the user has existing Terraform state that a new Atmos component must read.
Common Problems in Task-Runner Migration
These behaviors apply to every task runner. Check them before you open a reference file:
- The default order can change, and it differs by source tool. Task runs
deps: at the same
time by default, so command-level dependencies.commands/dependencies.workflows -- also
concurrent by default -- is its direct match. Make and Just run dependencies one after another
by default; make -j is required for concurrency. Do not describe dependencies.commands as
matching Make's/Just's default -- it changes the order, and can introduce a race between
prerequisites that were only ever sequential by accident, not by a declared dependency. For an
ordinary Make/Just chain, ordered steps preserve the default; reach for dependencies.commands
there only when the source used -j, the prerequisites are genuinely independent, or a
prerequisite is shared by more than one caller (it dedups a shared dependency to a single run
regardless of concurrency -- true for every one of these tools). Check the source tool's real
default before you move it.
- Freshness checks map to
inputs/artifacts, not to plain steps -- and the scope is per
step. Task's sources:/generates: fields and non-.PHONY Make targets both skip the
entire recipe/task when a file has not changed. Atmos's step-level inputs.sources/
artifacts.paths fields are the direct match: with no explicit when:, declaring them
implicitly means when: checksum.changed, and that one step is skipped when nothing has
changed since its last successful run -- later steps in the same command still run regardless.
If the source recipe/task runs more than one command and the freshness decision must gate all
of them together, combine them into a single shell/script step rather than spreading
inputs/artifacts across several steps. This does not carry over on its own -- add
inputs/artifacts to the migrated step yourself. The require/assert step type does not
replace this. It only checks that a file exists, not whether it is fresh.
workflows.base_path needs to be set explicitly once the user has their own atmos.yaml.
Only fixed, multi-step orchestration across more than one component becomes an Atmos workflow
(Principle 7) -- most target chains stay a custom command with instead.
fails with
until you add it (for example,
). None of this skill's snippets show it
by default -- add it the moment the user's migration reaches its first workflow.
Each reference file has its own "Common Problems" section with the exact field names and steps
for that tool. This section is only a short summary.
The Minimum-Viable Migration
Use this checklist when the user wants to try Atmos on an existing repository. Do not change the
order unless the user's setup requires it.
- Install Atmos. See
atmos.tools/install.
- Create
atmos.yaml at the repo root, pointing base_path and components.terraform.base_path
at the user's existing layout. Do not ask them to move files.
- Create one stack file for one environment. Use
!include of an existing .tfvars file so
nothing has to be rewritten:
import:
- _defaults
components:
terraform:
vpc:
vars: !include ../path/to/existing/dev.tfvars
- Run
atmos terraform plan vpc -s dev and confirm output matches what terraform plan -var-file=dev.tfvars produced before.
A working example of this shape is at examples/native-terraform/ in the Atmos repository.
File-Layout Options
Pick the layout that matches the user's goals. Atmos recommends the components/terraform/
layout, especially for a new repository or a multi-tool project. You can keep an existing layout
when the user wants less disruption.
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 more organization patterns, such as multi-region, multi-account, and organization
hierarchies, see the skill atmos-design-patterns.
YAML Functions vs Gomplate Datasources
This is a common mistake: an agent chooses a Gomplate datasource when a YAML function is safer
and clearer. Use the option in 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 |
A YAML function checks its own types. It gives a clear error message. It works without Gomplate
turned on. It does not require the template text to stay valid YAML. Use a Go template only for
control flow, such as a conditional, a loop, or a dynamic key, that a YAML function cannot
express. See atmos-templates for when to use a Go template.
What Does NOT Need to Change
Tell the user this list first, if they are afraid of a large rewrite. None of these items must
change to adopt Atmos:
- Terraform code. Providers, resources, data sources, and modules stay the same.
- Module sources. A local path, such as
source = "../../modules/foo", or a registry
source, keeps working.
- Backend code. You can delete the
backend "s3" {} block from the .tf files, because
Atmos creates backend.tf.json. Or you can keep the block and turn off backend generation in
atmos.yaml. Both methods work.
.tfvars files. Atmos reads them through !include. Convert them to YAML later, only if
the user wants deep-merge inheritance.
- Custom provider configuration. Providers stay in the
.tf files. Pass environment
variables through stack env:. Pass Terraform variables through stack vars:.
When to Escalate to Other Skills
After the minimum migration works, the user will often ask what to do next. Send each question
to the correct skill:
- Organize many stacks, such as by organization, tenant, account, or region. Use
atmos-design-patterns.
- Build abstract components, inheritance, or catalog patterns. Use
atmos-components.
- Use deep merging, imports, or overrides. Use atmos-stacks.
- Vendor third-party components. Use atmos-vendoring.
- Set up authentication or provider credentials. Use atmos-auth.
- Add validation policies, such as OPA or JSON Schema. Use
atmos-validation.
- Set up CI/CD with affected-component detection. Use atmos-ci.
- Share data between components through a store. Use
atmos-stores.
Anti-Patterns
Push back if a user or another agent proposes one of these methods during migration:
- "You must move all Terraform into
components/terraform/ before you use Atmos." This is
false. That layout is a recommendation, not a requirement. Let the user pick: adopt the
recommended layout now, or point base_path at the current layout and reorganize later.
- "You must rewrite all
.tfvars files as YAML before you run Atmos." This is false. Native
stack YAML is the best final format for inheritance and composition. But !include lets the
user keep existing .tfvars files during a step-by-step migration.
- "Delete your workspace state and start over." This is false. Connect the existing state
with
metadata.terraform_workspace and the remote-state-bridge pattern.
- "Add a Gomplate datasource for everything." This is false. Use a YAML function first.
- "Adopt the full multi-account organization hierarchy on day one." This is false. Start
with one stack file.
- "Wrap atmos commands in a Makefile, Justfile, or Taskfile forever." This is false. A
wrapper is a good bridge while the user builds trust in Atmos. But it is not the final state.
Change each leaf target to a custom command. An ordinary Make or Just dependency chain (for
example,
deploy: build test) stays a custom command with ordered steps or
dependencies.commands -- it does not need a workflow. A Taskfile's deps: is different: Task
runs deps: concurrently by default, so it maps directly onto dependencies.commands (also
concurrent by default) on the custom command -- reach for ordered steps instead only when the
user's dependency chain actually requires serial execution. Reserve workflows for fixed,
multi-step orchestration across more than one component, not for an ordinary target chain.
Additional Resources
- References/from-native-terraform.md: steps for a plain
Terraform migration, matched to each shape.
- References/from-terraform-workspaces.md: how to map
workspaces to stacks without losing state.
- References/remote-state-bridge.md: the dummy-component and
abstract-component patterns. Use them to read state from Terraform that is not yet migrated, or
from an external repository.
- References/from-terramate.md: construct-by-construct mapping
from Terramate (
stack.tm.hcl, globals, generate_hcl, script{}, tags/labels) to Atmos,
including the one remaining known gap (.tmtriggers).
- references/from-terragrunt.md -- concept mapping and migration
workflow for classic Terragrunt and Terragrunt Stacks.
- References/from-makefile.md: steps for a Makefile, matched to
each shape.
- References/from-justfile.md: steps for a Justfile, matched to
each shape.
- References/from-taskfile.md: steps for a Taskfile.yml (go-task)
file, matched to each shape.
- References/from-mise.md -- migrating tool versions, tasks, and env
vars from mise to the Atmos toolchain.
- References/from-aqua.md -- migrating tool versions from Aqua CLI's
aqua.yaml to the Atmos toolchain.