Back
OpenTofu vs. Terraform: Migrating Off the BUSL License Safely

Author: Steven Crickman
Edited by: Vahe Aslanyan

1. Introduction

For nine years, Terraform was open source in the way most engineers mean the term: from its first release in 2014 through mid-2023, every line shipped under the Mozilla Public License 2.0 (MPL 2.0), an OSI-approved license with no restrictions on commercial use, redistribution, or competing products. In August 2023, HashiCorp changed course, relicensing all new Terraform releases under the Business Source License 1.1 (BUSL, sometimes written BSL). <cite index="9-1">This license permitted the community to fork the codebase, modify it, contribute to it, and deploy it on their own servers, without placing new restrictions on typical end users — but it did strictly impact distributors selling managed Terraform products, who now needed a commercial license from HashiCorp.</cite>

The community response was immediate. <cite index="6-1">Within weeks, major cloud providers, infrastructure vendors, and individual contributors formed the OpenTofu initiative</cite>, forking the last MPL-licensed release (Terraform 1.5.x) and placing the result under Linux Foundation governance as a permanently MPL 2.0-licensed project.

In December 2024, IBM completed its acquisition of HashiCorp for $6.4 billion, adding another layer of consideration for organizations already uneasy about the license change: Terraform's roadmap, pricing, and long-term stewardship are now a function of IBM's broader cloud portfolio strategy, not an independent company's. <cite index="1-1">2025–2026 saw the two projects diverge in meaningful technical ways — OpenTofu shipped native state encryption and provider-defined functions faster than Terraform, while Terraform focused on AI-assisted features and deeper HCP Terraform integration.</cite> As of early-to-mid 2026, <cite index="1-1">OpenTofu has roughly 12% adoption among IaC practitioners, with over a quarter of teams planning to evaluate or expand its use, while Terraform still commands the largest overall market share.</cite> This is no longer a hypothetical decision — it is one that many platform teams are actively working through.

This handbook is a practical guide for engineering teams evaluating or executing a migration off Terraform's BUSL license onto OpenTofu: what the license actually restricts, what's technically different between the two tools today, and how to execute a safe, low-drama migration of real infrastructure.

2. Understanding the BUSL License

2.1 What Changed, Precisely

Under the MPL 2.0, Terraform's source code could be used, modified, and redistributed — including as the core of a competing commercial product — with the only real obligation being that modifications to MPL-licensed files themselves stay under MPL. <cite index="7-1">MPL 2.0 is what most engineers mean when they say "open source": use it for anything, sell products built on it, modify it as long as modifications to MPL files go back under MPL. There is no clause that asks what business you're in.</cite>

BUSL 1.1 is structured differently. <cite index="7-1">The license question only surfaces when somebody asks who else is allowed to use the software commercially, and specifically, who is allowed to compete with the licensor.</cite> Under Terraform's BUSL terms:

  • Running Terraform internally to manage your own infrastructure — the overwhelming majority of usage — is unaffected.
  • Building and selling a product whose core value proposition is "hosted/managed Terraform" (i.e., directly competing with HCP Terraform or Terraform Enterprise) requires a separate commercial agreement with HashiCorp.
  • <cite index="10-1">Each individual source file automatically re-licenses to MPL 2.0 four years after its publication date, but HashiCorp can continue publishing new Terraform releases under BUSL indefinitely</cite> — so the "it becomes open source eventually" property applies file-by-file to old code, not to the project as a whole going forward.

2.2 Who Is Actually Affected

It's worth being precise here, because a lot of anxiety about the BUSL change is disproportionate to its actual legal effect for most teams:

  • Internal platform teams running Terraform against your own cloud accounts: not materially restricted. This is the largest population of Terraform users and the license change doesn't change what you're allowed to do.
  • Managed service providers or SaaS companies whose product is "Terraform-as-a-service": directly and materially affected — this is precisely the use case the license was written to restrict, and legal review or a commercial agreement is necessary.
  • TACOS platforms (Terraform Automation and Collaboration Software — Spacelift, env0, Scalr, Atlantis, Harness, etc.) that orchestrate Terraform runs on customers' behalf: a gray area that most vendors in this space resolved either by obtaining commercial agreements, by supporting OpenTofu as their primary engine, or both.
  • Consultancies and platform vendors building tooling on top of Terraform's CLI (not reselling Terraform itself, but building products that shell out to it): generally unaffected, but this is exactly the kind of judgment call that benefits from actual legal review rather than a blog post's opinion, given how BUSL's "competing use" language is not always crisp at the edges.

2.3 Why License Ambiguity Is a Real Cost Even When the Legal Risk Is Low

Even for organizations in the "clearly fine" bucket, the BUSL change introduced a category of cost that's easy to underrate: governance friction. <cite index="6-1">Organizations with an open-source program office or strict vendor-neutrality requirements often can't treat this as pure legal paperwork — the license change becomes a real governance risk that has to be evaluated, documented, and periodically re-reviewed</cite>, even if the conclusion each time is "we're fine." That recurring review cost, multiplied across every internal tool and audit cycle that touches "what license is our IaC tooling under," is a real and ongoing tax that a permanently MPL-licensed tool doesn't impose.

<cite index="7-1">The OSI does not approve BUSL 1.1 as an open-source license</cite>, which matters concretely for organizations with procurement policies that require OSI-approved licenses for foundational tooling, or that report open-source usage as part of compliance, security, or ESG processes.

3. OpenTofu: Origins and Governance

3.1 The Fork

<cite index="3-1">OpenTofu is a community-driven, MPL-licensed fork of Terraform created in 2023 after HashiCorp relicensed Terraform under the Business Source License.</cite> <cite index="3-1">A coalition of vendors and community members forked Terraform 1.5, the last MPL version, with endorsing companies at fork time including Spacelift, env0, Gruntwork, Harness, Scalr, and Cloud Posse, among others.</cite> The project was placed under the Linux Foundation rather than any single company, specifically to prevent the exact scenario that motivated the fork in the first place: a single vendor unilaterally changing the license on code the ecosystem depends on.

3.2 Governance Model

<cite index="6-1">OpenTofu is governed by the Linux Foundation with a Technical Steering Committee drawn from multiple organizations, maintaining command-line compatibility with Terraform while operating under MPL 2.0.</cite> This is the same governance pattern used by Kubernetes, and it matters structurally: <cite index="2-1">it's a foundation-backed, vendor-neutral control plane with a defined contribution model and long-term stability commitment</cite>, rather than a single company's roadmap. Decisions about core features go through a public RFC process rather than being decided unilaterally, and — critically for the trust question that motivated the fork — <cite index="6-1">no single vendor can unilaterally change the license again.</cite>

3.3 Legal Provenance of the Fork

A natural question for legal teams evaluating OpenTofu: is there any residual licensing risk inherited from forking a HashiCorp-originated codebase? <cite index="7-1">OpenTofu was forked from a pre-BUSL, MPL-licensed release, so the BUSL does not apply to OpenTofu's codebase, and the Linux Foundation and OpenTofu's legal counsel reviewed the licensing position before the fork was published.</cite> There was friction along the way: <cite index="9-1">in 2024, HashiCorp sent OpenTofu's maintainers a cease-and-desist letter alleging that OpenTofu had incorporated code from Terraform releases published after the license change</cite>, a dispute that was resolved without altering OpenTofu's licensing position. Organizations with particularly strict provenance requirements may still want their own counsel to review the history, but the public record on this is well documented and has been independently examined by multiple parties with an interest in getting it right.

4. Technical Comparison

4.1 What's Identical

For the overwhelming majority of day-to-day usage, OpenTofu and Terraform are the same tool. They share:

  • The HCL configuration language and syntax.
  • The provider plugin protocol — <cite index="7-1">the same binaries and release pipelines, with the only difference being which registry your required_providers block resolves against by default, which can be pinned explicitly either way</cite>.
  • The core workflow: init, plan, apply, destroy, workspaces, modules, remote backends.
  • <cite index="3-1">Most core functionality and configuration language, though the two diverge on licensing, governance, and a growing list of individual features.</cite>

A configuration file that works in Terraform will, in the large majority of cases, work unmodified in OpenTofu:

# main.tf — valid in both Terraform and OpenTofu without modification
terraform {
 required_providers {
   aws = {
     source  = "hashicorp/aws"
     version = "~> 5.0"
   }
 }
}

provider "aws" {
 region = "us-east-1"
}

resource "aws_instance" "web" {
 ami           = "ami-0c55b159cbfafe1f0"
 instance_type = "t3.micro"

 tags = {
   Name        = "web-server"
   Environment = "production"
 }
}

Running this through either binary produces the same plan:

$ terraform init && terraform plan
# ...identical resource graph...

$ tofu init && tofu plan
# ...identical resource graph...

4.2 Where OpenTofu Has Pulled Ahead

Since the fork, OpenTofu has shipped several features that do not exist in Terraform's open-source CLI, largely because they were community priorities that HashiCorp had deprioritized or reserved for HCP Terraform:

Native state encryption (OpenTofu 1.7+). OpenTofu can encrypt state at rest without a third-party backend or wrapper:

# encryption.tf — OpenTofu only
terraform {
 encryption {
   key_provider "pbkdf2" "mykey" {
     passphrase = var.state_encryption_passphrase
   }

   method "aes_gcm" "mymethod" {
     keys = key_provider.pbkdf2.mykey
   }

   state {
     method = method.aes_gcm.mymethod
   }

   plan {
     method = method.aes_gcm.mymethod
   }
 }
}

<cite index="6-1">This matters because state files contain sensitive data — API keys, database passwords, infrastructure topology — and Terraform requires external solutions such as encrypted S3 buckets, Terraform Cloud, or third-party state backends to achieve the same protection.</cite>

Provider-defined functions and for_each on provider configurations (OpenTofu 1.9+). <cite index="7-1">OpenTofu has shipped provider for_each (v1.9) and the -exclude flag (v1.9), none of which exist in Terraform's open-source CLI.</cite>

# provider for_each — OpenTofu only, useful for multi-region/multi-account setups
provider "aws" {
 for_each = var.regions
 alias    = "by_region"
 region   = each.value
}

resource "aws_s3_bucket" "regional" {
 for_each = var.regions
 provider = aws.by_region[each.key]
 bucket   = "my-app-${each.key}"
}

Early variable evaluation, including in backend configuration (OpenTofu 1.8+). This removes a long-standing Terraform limitation where backend configuration blocks couldn't reference variables:

# backend.tf — OpenTofu only; variables usable directly in backend config
variable "environment" {
 type = string
}

terraform {
 backend "s3" {
   bucket = "my-tfstate-${var.environment}"
   key    = "infra/terraform.tfstate"
   region = "us-east-1"
 }
}

Targeted exclusion (-exclude flag, v1.9), the inverse of -target:

# Apply everything except the specified resource
tofu apply -exclude=aws_instance.legacy_bastion

Ephemeral resources and write-only attributes (OpenTofu 1.11, late 2025/2026). <cite index="7-1">OpenTofu 1.11 adds ephemerality, ephemeral resources, write-only attributes, and the enabled meta-argument, continuing to evolve independently from Terraform's roadmap.</cite> Ephemeral resources let you reference values (e.g., a short-lived credential) that are used during a plan/apply but never persisted to state:

# Ephemeral resource — value is used but never written to state
ephemeral "random_password" "db_admin" {
 length = 20
}

resource "aws_db_instance" "main" {
 # ...
 password = ephemeral.random_password.db_admin.result
}

The enabled meta-argument offers a cleaner alternative to the common count = var.enabled ? 1 : 0 pattern:

# enabled meta-argument — OpenTofu only, replaces the count-as-boolean idiom
resource "aws_instance" "optional" {
 enabled       = var.create_instance
 ami           = data.aws_ami.this.id
 instance_type = "t3.micro"
}

4.3 Where Terraform Still Has an Edge

It would be dishonest to present this as a one-sided comparison. Terraform retains real advantages, concentrated mostly in the surrounding platform rather than the CLI itself:

  • HCP Terraform: <cite index="8-1">a managed execution environment with RBAC, audit logging, run history, and policy enforcement — not just remote state.</cite> Platform teams that have built internal developer platforms on top of HCP Terraform face a real rebuild cost if they migrate, since this isn't a CLI feature you can swap out independently.
  • Sentinel: <cite index="8-1">deeply embedded in large enterprise environments for cost control policies, tagging enforcement, resource type restrictions, and compliance guardrails expressed as policy-as-code.</cite> OpenTofu has no direct Sentinel equivalent; organizations typically substitute OPA/Conftest or Checkov, which is a genuine migration project, not a drop-in swap.
  • Terraform Stacks: <cite index="7-1">the main Terraform-exclusive capability remaining, and it lives in HCP Terraform rather than the open binary</cite>, so it's only relevant to teams already committed to the HCP ecosystem.
  • Provider update priority: <cite index="1-1">Terraform Registry hosts over 4,800 providers, and OpenTofu uses the same registry and has the same providers, but HashiCorp-owned providers (Vault, Consul, Nomad) receive updates for Terraform first.</cite> For teams heavily dependent on those specific providers, this is a real (if usually small) lag to account for.
  • Ecosystem depth and market share: Terraform's <cite index="1-1">ecosystem depth, enterprise tooling maturity, and sheer ubiquity</cite> mean more Stack Overflow answers, more consultants familiar with it, and more third-party integrations that assume Terraform specifically rather than "an IaC tool with an HCL-compatible CLI."

4.4 Ecosystem and Tooling Support

<cite index="8-1">Major cloud providers, Kubernetes operators, and TACOS platforms (Spacelift, Scalr, env0, Atlantis) all support OpenTofu</cite>, and this is a meaningfully different picture than it was in 2023: <cite index="8-1">the ecosystem gap argument from the fork's early days has largely closed.</cite> <cite index="4-1">OpenTofu's own registry hosts 3,900+ providers and 23,600+ modules</cite>, covering the practical needs of the large majority of infrastructure teams.

5. Migration Mechanics: State, Providers, and Workflow

5.1 State File Compatibility

This is usually the first and biggest practical concern, and the news is good. <cite index="7-1">State produced by Terraform 1.5.x generally works in OpenTofu without conversion, and many later Terraform states also work as long as the configuration doesn't depend on Terraform-only features.</cite> <cite index="5-1">OpenTofu is confirmed to work with existing state files created with Terraform versions up through the 1.5.x line</cite>, and in practice compatibility extends further for most common configurations.

The safe verification procedure:

# 1. Back up existing state before touching anything
terraform state pull > terraform-state-backup-$(date +%Y%m%d).json

# 2. Install OpenTofu alongside Terraform (don't remove Terraform yet)
brew install opentofu    # macOS, or use your platform's package manager

# 3. Run a plan with OpenTofu against the existing state — do NOT apply yet
tofu init
tofu plan

# 4. Compare the plan output carefully — it should show no changes
#    (an empty diff means OpenTofu read the state and config identically to Terraform)

<cite index="7-1">On the first tofu apply, the terraform_version marker in state is rewritten to OpenTofu's version, but the resource entries themselves are not modified</cite> — meaning the migration is non-destructive to the actual resource data in state, and reversible if you decide to roll back before running an apply that changes infrastructure.

5.2 The Migration Checklist

A structured, low-risk sequence for migrating a single workspace or root module:

  1. Inventory your Terraform-specific dependencies. Grep your codebase for HCP-specific blocks, Sentinel policy references, and any Terraform Cloud-specific remote backend configuration.

# Search for HCP/Terraform Cloud-specific configuration that needs special handling
grep -r "cloud {" --include="*.tf" .
grep -r "backend \"remote\"" --include="*.tf" .

  1. Pin your provider versions explicitly before migrating, so a registry switch doesn't also silently pick up an unplanned provider upgrade:

terraform {
 required_providers {
   aws = {
     source  = "hashicorp/aws"
     version = "5.42.0"   # exact pin, not a range, during migration
   }
 }
 required_version = "~> 1.6"
}

  1. Install OpenTofu side-by-side with your existing Terraform binary. They coexist fine on the same machine and even reference the same .terraform provider cache in most cases.
  2. Run tofu init and tofu plan against a non-production workspace first. Never validate a migration path for the first time against production state.

tofu workspace select staging
tofu init -upgrade=false
tofu plan -out=migration-check.tfplan

  1. Diff the plan output against Terraform's plan for the same state and config, to confirm parity:

terraform plan -out=terraform-check.tfplan
tofu plan -out=opentofu-check.tfplan
# Convert both to JSON and diff programmatically for a large number of resources
terraform show -json terraform-check.tfplan > tf.json
tofu show -json opentofu-check.tfplan > tofu.json
diff <(jq -S . tf.json) <(jq -S . tofu.json)

  1. Migrate CI/CD pipelines to invoke tofu instead of terraform. Most CI systems just need the binary swapped and any explicit terraform CLI calls renamed:

# GitHub Actions — before
- name: Terraform Plan
 run: terraform plan -out=tfplan

# GitHub Actions — after
- uses: opentofu/setup-opentofu@v1
 with:
   tofu_version: "1.9.0"
- name: OpenTofu Plan
 run: tofu plan -out=tfplan

  1. Update remote state backend configuration if migrating away from Terraform Cloud/HCP-specific remote backends, since the cloud block is HCP-specific and has no OpenTofu equivalent — an S3, GCS, or Azure Blob backend works identically under either tool:

# Portable backend configuration, works identically under Terraform and OpenTofu
terraform {
 backend "s3" {
   bucket         = "company-tfstate"
   key            = "infra/prod/terraform.tfstate"
   region         = "us-east-1"
   dynamodb_table = "terraform-locks"
   encrypt        = true
 }
}

  1. Roll out gradually by workspace or environment, starting with the lowest-risk environment (dev/staging) and expanding to production only after several successful apply cycles have run cleanly with no plan drift.
  2. Keep the Terraform binary available for a defined rollback window (commonly 2-4 weeks) in case an edge case surfaces, but avoid running both tools' applies against the same state concurrently — pick one tool as the source of truth per workspace at any given time to avoid state lock or version-marker confusion.
  3. Replace Sentinel policies with OPA/Conftest or Checkov equivalents if applicable, before decommissioning Terraform entirely — this is usually the single largest piece of migration work for enterprises with mature policy-as-code investments, and should be scoped and planned as its own project rather than squeezed into the CLI-swap timeline.

5.3 Handling Provider Registry Differences

By default, required_providers blocks without an explicit source resolve against each tool's own default registry. To make configuration fully portable between both tools during a transition period, pin the source explicitly:

terraform {
 required_providers {
   aws = {
     source  = "registry.opentofu.org/hashicorp/aws"  # explicit OpenTofu registry
     version = "5.42.0"
   }
 }
}

<cite index="5-1">OpenTofu will not have its own providers; Terraform providers have not altered their licenses, and OpenTofu works with the current Terraform providers, but uses a separate registry that mirrors it.</cite> In practice this means provider behavior is identical regardless of which registry resolved it — the distinction matters for registry availability and update timing, not functionality.

5.4 Module Compatibility

Public and private Terraform modules generally work unmodified in OpenTofu, since modules are just HCL and inherit whatever provider/version constraints their calling root module specifies. The main thing to verify is that any module doesn't hard-code a required_version constraint that excludes OpenTofu version strings:

# A module's version constraint written defensively for both tools
terraform {
 required_version = ">= 1.5.0"   # avoid tool-specific version assumptions
}

6. Decision Framework

Rather than a universal recommendation, the right choice depends on a small number of concrete factors specific to your organization:

Stay on Terraform if:

  • You're deeply invested in HCP Terraform's managed execution, RBAC, and audit logging, and the cost of rebuilding that operational layer exceeds the value of resolving license ambiguity.
  • Sentinel policies are extensively embedded in your compliance and governance processes, and you don't have bandwidth to re-platform them onto OPA/Conftest in the near term.
  • You're not a vendor, MSP, or SaaS company building a competing product — meaning the BUSL's actual restriction never applies to you in practice, and the governance-review cost is tolerable for your organization.

Migrate to OpenTofu if:

  • You have an open-source program office or procurement policy that requires OSI-approved licenses for foundational infrastructure tooling.
  • You want native state encryption without adopting a third-party backend or HCP Terraform specifically.
  • Vendor-neutral, foundation-backed governance is itself a requirement — for example, due to prior experience with a vendor unilaterally changing license terms.
  • You're building or operating a platform/product where BUSL's competing-use restriction creates real legal exposure or ongoing review burden.
  • You value the features OpenTofu has shipped since the fork (state encryption, provider for_each, early variable evaluation, ephemeral resources) more than you value staying on HashiCorp's specific roadmap.

This is not an irreversible decision. <cite index="7-1">Same binaries, same release pipelines — the only difference is which registry your required_providers resolves against by default, and you can pin to either explicitly if it matters.</cite> Organizations can and do run both tools during a transition period, and the migration mechanics in Section 5 are largely reversible if a rollback becomes necessary within the state-compatibility window.

7. Common Pitfalls During Migration

  • Running terraform apply and tofu apply against the same state file interchangeably without a clear cutover point. This works technically in the near term but makes it unclear which tool is authoritative, complicating troubleshooting if plan output ever diverges.
  • Assuming HCP-specific configuration (the cloud block, Sentinel policy sets) migrates automatically. It doesn't — these require deliberate re-platforming, not a find-and-replace.
  • Skipping the plan-diff verification step (Section 5.2, step 5) for large or business-critical state files, and discovering a subtle divergence only after an apply has already run.
  • Upgrading provider versions at the same time as switching tools. Keep these changes separate so that if something breaks, you know immediately whether the cause was the tool switch or the provider upgrade.
  • Underestimating the Sentinel-to-OPA migration effort for enterprises with mature policy-as-code investments — this is frequently the actual critical path of a "simple CLI swap," not the CLI swap itself.
  • Forgetting CI/CD runners and local developer machines both need the new binary — a partial rollout where some pipelines still invoke terraform while local plans use tofu produces confusing, hard-to-reproduce discrepancies.

8. Validating a Migration Before It Reaches Production

A migration that "looks fine" in a quick manual check can still hide subtle drift, especially across large state files with hundreds of resources. A more rigorous validation approach is worth the extra hour it takes:

8.1 Automated Plan-Diff Tooling

Rather than eyeballing two plan outputs, script the comparison so it can run in CI on every migration-related change:

#!/usr/bin/env bash
# validate-migration.sh — compares terraform and tofu plan output for parity
set -euo pipefail

WORKSPACE="${1:-staging}"

echo "Running Terraform plan..."
terraform workspace select "$WORKSPACE"
terraform plan -out=tf.plan -input=false
terraform show -json tf.plan | jq -S '.resource_changes' > tf-changes.json

echo "Running OpenTofu plan..."
tofu workspace select "$WORKSPACE"
tofu plan -out=tofu.plan -input=false
tofu show -json tofu.plan | jq -S '.resource_changes' > tofu-changes.json

if diff -q tf-changes.json tofu-changes.json > /dev/null; then
 echo "PASS: plans are identical for workspace $WORKSPACE"
 exit 0
else
 echo "FAIL: plan divergence detected for workspace $WORKSPACE"
 diff tf-changes.json tofu-changes.json || true
 exit 1
fi

Running this against every workspace before cutover turns "we think it's fine" into a verifiable, repeatable check, and gives you a concrete artifact to point to if anyone later asks how the migration was validated.

8.2 Testing Module Behavior Explicitly

For teams with a mature testing culture, both tools support native testing frameworks that can be run under either binary to confirm behavioral parity:

# tests/instance.tftest.hcl — works under both `terraform test` and `tofu test`
run "creates_instance_with_correct_tags" {
 command = plan

 assert {
   condition     = aws_instance.web.tags["Environment"] == "production"
   error_message = "Environment tag was not set correctly"
 }
}

run "instance_type_matches_variable" {
 command = plan

 variables {
   instance_size = "t3.small"
 }

 assert {
   condition     = aws_instance.web.instance_type == "t3.small"
   error_message = "Instance type did not match input variable"
 }
}
terraform test   # run once under Terraform
tofu test        # run again under OpenTofu — results should match

A test suite that passes identically under both binaries is strong evidence that a module's behavior — not just its plan output for one specific state — is portable, which matters more than a single plan diff if the module gets reused across many environments over time.

8.3 Staged Rollout Metrics to Track

During a phased migration, track a small set of concrete signals per workspace rather than relying on anecdotal "it seems fine" reports:

  • Plan drift rate: percentage of tofu plan runs showing unexpected changes relative to the last known-good Terraform plan for the same commit.
  • Apply failure rate: comparing failure rates for tofu apply against the historical baseline failure rate for terraform apply on the same infrastructure.
  • Time-to-detect for divergence: how quickly a plan or apply discrepancy is caught — ideally in CI, not in a postmortem.
  • Rollback frequency: how often a workspace needed to fall back to Terraform during the transition window; a high rate here is a signal to slow the rollout rather than push through.

9. CI/CD and Platform Integration Details

9.1 GitHub Actions

The official setup-opentofu action mirrors the ergonomics of hashicorp/setup-terraform, making pipeline migration mostly mechanical:

name: Infrastructure CI
on:
 pull_request:
   paths: ["infra/**"]

jobs:
 plan:
   runs-on: ubuntu-latest
   defaults:
     run:
       working-directory: infra
   steps:
     - uses: actions/checkout@v4

     - uses: opentofu/setup-opentofu@v1
       with:
         tofu_version: "1.9.0"

     - name: Init
       run: tofu init -input=false

     - name: Validate
       run: tofu validate

     - name: Plan
       run: tofu plan -out=tfplan -input=false

     - name: Post plan summary as PR comment
       uses: actions/github-script@v7
       with:
         script: |
           // plan output already captured in previous step's logs
           console.log("Plan complete — see job logs for details");

9.2 GitLab CI

stages:
 - validate
 - plan
 - apply

variables:
 TOFU_VERSION: "1.9.0"

.tofu_setup: &tofu_setup
 before_script:
   - curl -Lo /usr/local/bin/tofu <https://github.com/opentofu/opentofu/releases/download/v${TOFU_VERSION}/tofu_${TOFU_VERSION}_linux_amd64>
   - chmod +x /usr/local/bin/tofu
   - tofu init -input=false

validate:
 stage: validate
 <<: *tofu_setup
 script:
   - tofu validate

plan:
 stage: plan
 <<: *tofu_setup
 script:
   - tofu plan -out=tfplan
 artifacts:
   paths: [tfplan]

apply:
 stage: apply
 <<: *tofu_setup
 script:
   - tofu apply -input=false tfplan
 when: manual
 only:
   - main

9.3 Atlantis

Atlantis, a popular self-hosted PR-automation tool for Terraform/OpenTofu workflows, supports OpenTofu natively via its executable configuration:

# atlantis.yaml
version: 3
projects:
 - name: production-infra
   dir: infra/prod
   workflow: opentofu

workflows:
 opentofu:
   plan:
     steps:
       - run: tofu init -input=false
       - run: tofu plan -out=$PLANFILE
   apply:
     steps:
       - run: tofu apply -input=false $PLANFILE

9.4 Pre-Commit Hooks

Keeping formatting and validation consistent across a team migrating gradually is easier with shared pre-commit hooks that work against either binary:

# .pre-commit-config.yaml
repos:
 - repo: local
   hooks:
     - id: tofu-fmt
       name: OpenTofu format check
       entry: tofu fmt -check -recursive
       language: system
       files: \.tf$

     - id: tofu-validate
       name: OpenTofu validate
       entry: bash -c 'tofu init -backend=false && tofu validate'
       language: system
       files: \.tf$
       pass_filenames: false

10. Estimating Migration Cost and Timeline

For a mid-sized platform team (a handful of platform engineers, on the order of 50-150 Terraform-managed workspaces), a realistic migration breakdown looks like this:

  • Inventory and risk assessment (identifying HCP-specific dependencies, Sentinel policies, provider version spread): 1-2 weeks.
  • Tooling and CI/CD pipeline updates (binary swap, pre-commit hooks, pipeline YAML changes): 1-2 weeks, largely parallelizable across teams once a template pipeline exists.
  • Per-workspace validation and cutover (plan-diff verification, staged rollout starting with non-production): roughly 1-3 days per workspace once the process is templated, though the first few workspaces will take longer while the team works out the kinks.
  • Sentinel-to-OPA policy re-platforming, if applicable: 4-8 weeks, and frequently the true critical path for enterprises with a mature policy-as-code investment — this should be scoped as its own project rather than folded into the general timeline.
  • Rollback buffer: maintaining both binaries and the ability to revert for 2-4 weeks after the last production cutover, before fully decommissioning Terraform.

For an organization without HCP Terraform or Sentinel dependencies, a full migration is often achievable within 4-6 weeks end to end. For one with both deeply embedded, 2-4 months is a more realistic planning assumption once the Sentinel re-platforming work is included.

11. Conclusion

The BUSL license change did not, for most organizations, create an urgent legal emergency — <cite index="10-1">most internal teams who use Terraform for infrastructure automation will not notice substantial changes in day-to-day usage.</cite> But it did create a durable governance question that doesn't resolve itself: whether your organization's foundational infrastructure tooling should depend on a single vendor's licensing discretion, now filtered further through IBM's ownership. OpenTofu answers that question structurally, through Linux Foundation governance and a permanent MPL 2.0 license, and has used the years since the fork to ship real technical improvements — state encryption, provider for_each, ephemeral resources — rather than simply tracking Terraform feature-for-feature.

The migration itself, for most teams, is mechanically straightforward: state files are compatible, HCL syntax is unchanged, and the provider ecosystem is shared. The genuine complexity concentrates in the parts of the stack that were never just about the CLI to begin with — HCP Terraform's managed execution environment and Sentinel's policy-as-code investment — and those pieces deserve their own deliberate migration plan rather than being treated as an afterthought to a simple binary swap. Approached that way, moving off BUSL is a safe, well-trodden path rather than a risky leap.

Appendix A: Glossary

  • BUSL / BSL (Business Source License 1.1) — The license HashiCorp applies to Terraform releases since version 1.6, restricting competing commercial use while permitting most internal usage; individual source files convert to MPL 2.0 four years after publication.
  • MPL 2.0 (Mozilla Public License 2.0) — The OSI-approved, permissive license Terraform used prior to August 2023 and that OpenTofu uses permanently.
  • TACOS — Terraform Automation and Collaboration Software; third-party platforms (Spacelift, env0, Scalr, Atlantis) that orchestrate Terraform/OpenTofu runs.
  • HCP Terraform — HashiCorp's managed execution platform, providing remote state, RBAC, audit logging, and run history beyond what the open-source CLI offers alone.
  • Sentinel — HashiCorp's proprietary policy-as-code framework, tightly integrated with HCP Terraform and Terraform Enterprise.
  • Ephemeral resource — An OpenTofu/Terraform resource type whose value is used during plan/apply but never persisted to state, useful for short-lived credentials.
  • Provider registry — The catalog from which a required_providers block resolves plugin binaries; Terraform and OpenTofu maintain separate but functionally equivalent registries.

Appendix B: Pre-Migration Checklist

  • [ ]  Back up all state files before any tooling changes
  • [ ]  Inventory HCP-specific configuration (cloud blocks, Terraform Cloud remote backends)
  • [ ]  Inventory Sentinel policy dependencies and scope a re-platforming plan if present
  • [ ]  Pin provider versions explicitly before switching tools
  • [ ]  Install OpenTofu alongside Terraform without removing the latter
  • [ ]  Run and script plan-diff validation for every workspace before cutover
  • [ ]  Update CI/CD pipelines, pre-commit hooks, and local developer tooling together
  • [ ]  Migrate lowest-risk environments first; production last
  • [ ]  Maintain a defined rollback window before decommissioning Terraform entirely