--- name: atmos-migration description: "Migrate to Atmos from native Terraform, Terraform Workspaces, Terramate, Terragrunt, Make, Just, or Task; migrate tool versions from mise or Aqua CLI; migrate AWS/GCP/Azure CLI configs, Leapp, Granted, saml2aws, or okta-aws-cli to atmos auth; and replace GitHub Actions CI (dflook, tfcmt, cloud OIDC, component updater, TFLint, Checkov, Trivy, KICS, Infracost, tfsec) with Atmos Native CI. Use for incremental adoption that preserves layout, state, task behavior, and CI enforcement." 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 - references/from-aws-config.md - references/from-gcp-config.md - references/from-azure-config.md - references/from-leapp.md - references/from-granted.md - references/from-aws2saml.md - references/from-okta-cli.md - references/to-native-ci.md - references/to-native-ci-scanners.md --- # Migrating 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. For full tutorials for end users, see: - [Migrating from Native Terraform](https://atmos.tools/migration/native-terraform) - [Migrating from Terraform Workspaces](https://atmos.tools/migration/terraform-workspaces) - [Migrating from Terragrunt](https://atmos.tools/migration/terragrunt) -- see [from-terragrunt.md](references/from-terragrunt.md) for agent recipes - Migrating from Terramate -- see [from-terramate.md](references/from-terramate.md) (no atmos.tools tutorial yet) - [Migrating from Makefiles](https://atmos.tools/migration/makefile) - [Migrating from Justfiles](https://atmos.tools/migration/justfile) - [Migrating from Taskfile.yml](https://atmos.tools/migration/taskfile) ## 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. **Terraform migration can preserve the existing layout.** 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](../atmos-yaml-functions/SKILL.md) and [atmos-templates](../atmos-templates/SKILL.md) skills for the boundary. 6. **Start with one working command.** For task-runner adoption, start with `atmos build` or another existing task. For Terraform adoption, start with a plan. Add configuration structure only when it serves the selected migration. 7. **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`, or `task` during 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](references/from-native-terraform.md) | | Multiple TF root modules in scattered dirs | [from-native-terraform.md](references/from-native-terraform.md) | | `terraform.workspace`-driven environments with shared state backend | [from-terraform-workspaces.md](references/from-terraform-workspaces.md) | | `.tm.hcl` files, `stack.tm.hcl`, `generate_hcl` blocks (Terramate project) | [from-terramate.md](references/from-terramate.md) | | Need to read outputs from un-migrated TF (legacy or another repo) | [remote-state-bridge.md](references/remote-state-bridge.md) | | User has a Makefile driving builds/tests/deploys | [from-makefile.md](references/from-makefile.md) | | User has a Justfile (`just` command runner) | [from-justfile.md](references/from-justfile.md) | | User has a Taskfile.yml (go-task) | [from-taskfile.md](references/from-taskfile.md) | | `cloudposse/github-action-atmos-component-updater` | [from-component-updater.md](references/from-component-updater.md) | | Terragrunt (`terragrunt.hcl` or `terragrunt.stack.hcl`) | [from-terragrunt.md](references/from-terragrunt.md) | | mise config (`mise.toml`, `.mise.toml`, `.mise/config.toml`, `.tool-versions`) for tool versions | [from-mise.md](references/from-mise.md) | | `aqua.yaml` (Aqua CLI) for tool versions | [from-aqua.md](references/from-aqua.md) | | CI on GitHub Actions (setup-terraform, configure-aws-credentials, dflook, tfcmt) | [to-native-ci.md](references/to-native-ci.md) | | Scanner actions (TFLint, Checkov, Trivy, KICS, Infracost, tfsec) | [to-native-ci-scanners.md](references/to-native-ci-scanners.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, matching `dependencies.commands`/`dependencies.workflows` directly. Make and Just run dependencies sequentially by default (`make -j` is required for concurrency) -- moving an ordinary Make/Just chain to `dependencies.commands` changes the order and can introduce a race. Preserve ordered steps for a sequential source chain; reach for `dependencies.commands` only 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`/`artifacts` at the step level, not to a whole recipe/task.** Task's `sources:`/`generates:` and non-`.PHONY` Make targets skip the *entire* recipe when nothing changed. Atmos's step-level `inputs.sources`/`artifacts.paths` (implicitly `when: checksum.changed`) only skip *that step* -- later steps in the same command still run. Combine multi-command recipes into one `shell`/`script` step if the freshness gate must cover all of them together; this doesn't carry over automatically. `require`/`assert` only checks existence, not freshness. - **`workflows.base_path` must be set explicitly** once the user has their own `atmos.yaml` (`atmos workflow ` 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 as `workflows.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](references/from-aws-config.md) | | `gcloud` CLI config, ADC, or service-account keys | [from-gcp-config.md](references/from-gcp-config.md) | | `az` CLI config, service principals, or Managed Identity | [from-azure-config.md](references/from-azure-config.md) | | Leapp (desktop credential manager) | [from-leapp.md](references/from-leapp.md) | | Granted (the `assume` CLI) | [from-granted.md](references/from-granted.md) | | saml2aws | [from-aws2saml.md](references/from-aws2saml.md) | | okta-aws-cli | [from-okta-cli.md](references/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 1. Install Atmos and create `atmos.yaml` in the existing project. 2. Add one custom command that calls an existing task, such as `make build`, `just build`, or `task build`. 3. Run `atmos build` and confirm it produces the same result as the original command. 4. 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. 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: ```yaml # 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_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](../atmos-design-patterns/SKILL.md). ## 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](../atmos-templates/SKILL.md) 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](../atmos-design-patterns/SKILL.md). - **Build abstract components, inheritance, or catalog patterns.** Use [atmos-components](../atmos-components/SKILL.md). - **Use deep merging, imports, or overrides.** Use [atmos-stacks](../atmos-stacks/SKILL.md). - **Vendor third-party components.** Use [atmos-vendoring](../atmos-vendoring/SKILL.md). - **Migrate an AWS/GCP/Azure CLI, Leapp, Granted, saml2aws, or okta-aws-cli setup.** Start with the matching reference in [Migrating Authentication](#migrating-authentication) above. For authoring new auth config beyond a migration, go straight to [atmos-auth](../atmos-auth/SKILL.md). - **Add validation policies, such as OPA or JSON Schema.** Use [atmos-validation](../atmos-validation/SKILL.md). - **Set up CI/CD with affected-component detection.** Use [atmos-ci](../atmos-ci/SKILL.md). - **Migrate third-party GitHub Actions CI** (dflook, tfcmt, etc.) to Native CI. Use [to-native-ci.md](references/to-native-ci.md). - **Share data between components through a store.** Use [atmos-stores](../atmos-stores/SKILL.md). ## 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. - **"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.