atmos ai skill install atmos-migrationMigrating to Atmos
Overview
Use this skill to adopt Atmos for infrastructure orchestration, general-purpose task running, or tool-version management. Select the migration path from the user's goal and existing tools.
For Make, Just, and Task, assume the user is adopting Atmos as a task runner for application
builds, tests, scripts, releases, and other automation. Custom commands need only atmos.yaml;
do not introduce Terraform components, stacks, .tfvars conversion, or cloud credentials unless
the user separately requests infrastructure orchestration. Preserve the commands behind the tasks,
and let Atmos call the existing task runner while individual tasks are migrated.
For Terraform repositories, Atmos can adopt the existing file layout. Start with the smallest change that gives value and add structure as needed.
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:
- Migrating from Native Terraform
- Migrating from Terraform Workspaces
- Migrating from Terragrunt -- see from-terragrunt.md for the agent-actionable recipes
- Migrating from Terramate -- covered by this skill via references/from-terramate.md (no atmos.tools tutorial yet)
- Migrating from Makefiles
- Migrating from Justfiles
- Migrating from Taskfile.yml
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.
- Terraform migration can preserve the existing layout. 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. - Start with one working command. For task-runner adoption, start with
atmos buildor another existing task. For Terraform adoption, start with a plan. Add configuration structure only when it serves the selected migration. - Task-runner adoption is a complete use case. Map targets, recipes, and tasks to custom
commands. Preserve ordering and shared prerequisites; use workflows where they help organize
reusable multi-step automation. No Terraform migration is implied. Atmos can call
make,just, ortaskduring incremental adoption, with no requirement to remove the original files.
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 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 |
.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/deploys | from-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-updater | from-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 versions | from-mise.md |
aqua.yaml (Aqua CLI) for tool versions | from-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
Check these before you open a reference file; each reference file's own "Common Problems" section has the exact field names and steps.
- Default order differs by source tool. Task's
deps:runs concurrently by default, matchingdependencies.commands/dependencies.workflowsdirectly. Make and Just run dependencies sequentially by default (make -jis required for concurrency) -- moving an ordinary Make/Just chain todependencies.commandschanges the order and can introduce a race. Preserve ordered steps for a sequential source chain; reach fordependencies.commandsonly when the source used-j, the prerequisites are genuinely independent, or a prerequisite is shared by more than one caller (deduped to a single run regardless of concurrency, true for every one of these tools). - Freshness checks map to
inputs/artifactsat the step level, not to a whole recipe/task. Task'ssources:/generates:and non-.PHONYMake targets skip the entire recipe when nothing changed. Atmos's step-levelinputs.sources/artifacts.paths(implicitlywhen: checksum.changed) only skip that step -- later steps in the same command still run. Combine multi-command recipes into oneshell/scriptstep if the freshness gate must cover all of them together; this doesn't carry over automatically.require/assertonly checks existence, not freshness. workflows.base_pathmust be set explicitly once the user has their ownatmos.yaml(atmos workflow <name>fails without it) -- add it the moment migration reaches its first workflow. Workflows can organize general-purpose tasks as well as infrastructure operations; many target chains can stay custom commands. For task-runner adoption, a path such asworkflows.base_path: "workflows"keeps workflows separate from Terraform stacks.
Migrating Authentication
Authentication is an orthogonal migration axis from IaC -- a user may migrate their Terraform code, their auth setup, both, or neither in a given session. Don't conflate the two. Identify which credential tooling the user has today and route to the matching reference:
| User has... | Use reference |
|---|---|
~/.aws/config/~/.aws/credentials profiles | from-aws-config.md |
gcloud CLI config, ADC, or service-account keys | from-gcp-config.md |
az CLI config, service principals, or Managed Identity | from-azure-config.md |
| Leapp (desktop credential manager) | from-leapp.md |
Granted (the assume CLI) | from-granted.md |
| saml2aws | from-aws2saml.md |
| okta-aws-cli | from-okta-cli.md -- partial support only, read the gap callouts |
All are pure config-translation guides -- there is no atmos auth import/migrate command.
None of them require touching the user's IaC migration path; they can run before, after, or
independently of one. from-okta-cli.md is the one exception to "full mapping exists": whether it
works depends on the org's Okta auth policy, not on a different AWS app type -- read it fully
before promising a user anything.
The Minimum-Viable Migration
Choose the checklist for the user's goal.
Task Runner
- Install Atmos and create
atmos.yamlin the existing project. - Add one custom command that calls an existing task, such as
make build,just build, ortask build. - Run
atmos buildand confirm it produces the same result as the original command. - Move task bodies into native steps as needed, preserving parameters, environments, dependency order, and freshness behavior. No stack files or Terraform changes are required.
Terraform Orchestration
Use this checklist when the user explicitly wants Atmos to orchestrate existing Terraform code.
- 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.yamlimport:- _defaultscomponents: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 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.tffiles, because Atmos createsbackend.tf.json. Or you can keep the block and turn off backend generation inatmos.yaml. Both methods work. .tfvarsfiles. 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
.tffiles. Pass environment variables through stackenv:. Pass Terraform variables through stackvars:.
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.
- Migrate an existing AWS/GCP/Azure CLI config, Leapp, Granted, saml2aws, or okta-aws-cli setup. Start with the matching reference in Migrating Authentication above. For authoring new auth config beyond a migration, go straight to 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 pointbase_pathat the current layout and reorganize later. - "You must rewrite all
.tfvarsfiles as YAML before you run Atmos." This is false. Native stack YAML is the best final format for inheritance and composition. But!includelets the user keep existing.tfvarsfiles during a step-by-step migration. - "Delete your workspace state and start over." This is false. Connect the existing state
with
metadata.terraform_workspaceand 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.
- "Task-runner migration requires Terraform stacks or components." Custom commands run general
automation from
atmos.yaml. Keep infrastructure adoption separate from task-runner adoption. - "Delete the existing task file before adopting Atmos." Atmos can call the existing runner. Migrate task bodies incrementally and preserve the source tool's ordering and freshness semantics.
- "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, not the final state -- change each leaf target to a custom command (see Principle 7 and "Common Problems in Task-Runner Migration" above for the concurrency/ordering details per source tool).
Every reference file is already linked, with its routing condition, from the "Decide the Migration Shape First" and "Migrating Authentication" tables above -- load a reference directly from there rather than a separate resource list.