---
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 <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 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.
