How-to

OpenTofu Migration Checklist

Move from Terraform to OpenTofu by pinning the binary, rehearsing plans in a clone of state, then cutting over CI — not by rewriting modules. A checklist for backends, providers, policy, and rollback.

OpenTofu migration · reference

Migrating to OpenTofu is a toolchain cutover. Read Terraform vs OpenTofu first. If you do not have a license or runner reason, stop here.

The checklist below assumes you already run remote state and CI plans. If you apply from laptops, fix that before you change the binary.

Before you touch production

  • Written reason (legal, embed/redistribute, or a concrete CLI feature).
  • Current Terraform version recorded (terraform version) for every workspace.
  • Target OpenTofu version recorded. Do not float latest.
  • required_providers and required_version constraints reviewed.
  • List of wrappers: make apply, custom images, pre-commit, Atlantis-style runners, policy CLIs that shell out to terraform.

Rehearsal (do this on a copy)

  1. Copy state to a scratch backend, or clone the workspace if your platform supports it.
  2. Install OpenTofu next to Terraform — different binary name, same working directory.
  3. Run init with the same backend config and provider pins.
  4. Run a plan. Require either no changes or a diff you can explain in one paragraph.
  5. If the diff is “noise” (provider bug, attribute reordering), file it. Do not invent a rewrite.
tofu version
tofu init -input=false
tofu plan -out=rehearsal.tfplan
tofu show -no-color rehearsal.tfplan

If rehearsal wants to recreate stateful resources, stop. That is a key or lifecycle problem, not an OpenTofu branding issue. See for_each vs count.

CI and policy

  • Images and GitHub Actions call tofu (or a pinned wrapper) explicitly.
  • OIDC roles still trust the same pipeline (OIDC for Terraform still applies).
  • Policy engines read OpenTofu plan JSON. Confirm the schema path you parse.
  • terraform test or equivalent module tests run under the new binary (terraform test).
  • Docs and runbooks say which CLI production uses.

Cutover

  • State lock works under OpenTofu against your backend (S3, Azure, GCS, etc.).
  • One workspace, one CLI. Remove the old job so two applies cannot race.
  • First production apply is a no-op or a trivial, reversible change.
  • Previous Terraform version remains installable for a rollback window (several successful applies, not 45 minutes).

After

  • Drift scan still runs — detection does not care what the binary is called (how to detect drift).
  • Approvals still attach to a saved plan (who approves).
  • New workspaces get the OpenTofu image by default so you do not fork the fleet.

You do not have to rewrite modules to change the CLI. If you generate HCL at all, keep the same rehearsal on Terraform / OpenTofu generation.

FAQ

Do I need to rewrite Terraform modules for OpenTofu?

Almost never. Rehearse with a plan on a state copy. Rewrite only if a feature or provider constraint forces it.

Can Terraform and OpenTofu share the same state file?

They speak the same state format today, but you should not let two CLIs apply the same workspace at once. Cut over, lock the backend, and keep one runner per workspace.

What is the fastest way to fail a migration?

Swapping the CI binary on production Friday without a rehearsal plan, or leaving scripts that still call terraform apply against the same backend.

Platform evaluation

See OrchesTerra on your infrastructure

Request access to generate architecture from a repository, review the plan, and run governed reconciliation across AWS, Azure, GCP, and OCI.