Migrating to Atmos

SkillFiles & storage

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.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Migrating to Atmos skill

What this skill tells your AI

The instructions your AI receives, as published by cloudposse/atmos in agent-skills/skills/atmos-migration/SKILL.md and read by ahel’s review.

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.

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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 atmos 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 dependencies.commands/dependencies.workflows 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:

User has...Use reference
One TF root module, env config via .tfvars or env varsfrom-native-terraform.md
Multiple TF root modules in scattered dirsfrom-native-terraform.md
terraform.workspace-driven environments with shared state backendfrom-terraform-workspaces.md
.tm.hcl files, stack.tm.hcl, generate_hcl blocks (Terramate project)from-terramate.md
Need to read outputs from un-migrated TF (legacy or another repo)remote-state-bridge.md
User has a Makefile driving builds/tests/deploysfrom-makefile.md
User has a Justfile (just command runner)from-justfile.md
User has a Taskfile.yml (go-task)from-taskfile.md
cloudposse/github-action-atmos-component-updaterfrom-component-updater.md
Terragrunt (terragrunt.hcl or terragrunt.stack.hcl)from-terragrunt.md
mise config (mise.toml, .mise.toml, .mise/config.toml, .tool-versions) for tool versionsfrom-mise.md
aqua.yaml (Aqua CLI) for tool versionsfrom-aqua.md

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 dependencies.commands instead. atmos workflow <name> fails with 'workflows.base_path' must be configured in 'atmos.yaml' until you add it (for example, workflows.base_path: "stacks/workflows"). None of this skill's atmos.yaml 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.

  1. Install Atmos. See atmos.tools/install.
  2. 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.
  3. Create one stack file for one environment. Use !include of an existing .tfvars file so nothing has to be rewritten:
    # stacks/dev.yaml
    import:
      - _defaults
    components:
      terraform:
        vpc:
          vars: !include ../path/to/existing/dev.tfvars
    
  4. 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_pathUse 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:

GoalReach for (NOT this)Use instead
Include a file's contentsgomplate.datasources with file URL!include path/to/file
Read an environment variablegomplate getenv "FOO"!env FOO
Run a shell commandTemplate + gomplate exec!exec "command"
Read a store valueCustom datasource URL!store store_name component stack key
Read Terraform outputTemplated remote-state datasource!terraform.state component output
Get current AWS account IDgomplate.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

Signals

GitHub stars
1k
Forks
175
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
atmos-migration
Source
github.com/cloudposse/atmos