Skip to main content
Use this skill
atmos ai skill install atmos-migration
SKILL.md19.8 KB
View on GitHub

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.

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. 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 and atmos-templates 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 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

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 profilesfrom-aws-config.md
gcloud CLI config, ADC, or service-account keysfrom-gcp-config.md
az CLI config, service principals, or Managed Identityfrom-azure-config.md
Leapp (desktop credential manager)from-leapp.md
Granted (the assume CLI)from-granted.md
saml2awsfrom-aws2saml.md
okta-aws-clifrom-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:
    # 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.
  • 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 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.