In short
- Terraform 1.17 is BSL-licensed and owned by IBM. OpenTofu 1.13 is MPL 2.0 under the Linux Foundation. Both run the same HCL against the same providers.
- OpenTofu adds state encryption and variables in backend blocks. Terraform adds search, actions, policy and Stacks, and Stacks need HCP Terraform.
- Pulumi suits teams who want TypeScript, Python or Go, and can use any Terraform provider. CDK for Terraform was deprecated on 10 December 2025.
On this page
This is for teams choosing an infrastructure-as-code tool, or wondering whether their Terraform estate should move. It covers the licence history, current versions, the features that differ, hosted pricing, migration commands, and what I would pick in three common situations. Every version, price and date was checked on 13 November 2026 against the projects’ own release notes, docs and pricing pages.
I have used Terraform and Ansible in client work, and I run production systems on AWS, Azure, Google Cloud and Oracle Cloud. The opinions are mine; the facts come from the sources at the end.
The short answer
- Small team starting fresh, happy with HCL: OpenTofu, with encrypted state in your own object storage.
- Existing Terraform estate: stay if you use HCP Terraform features such as Stacks; otherwise OpenTofu is a cheap, reversible switch.
- Developer-heavy team: Pulumi. If you are on CDK for Terraform, plan your exit now.
How the licences got here
| Date | Event |
|---|---|
| 10 August 2023 | HashiCorp moves future releases from MPL 2.0 to the Business Source License 1.1 (BSL). APIs, SDKs and “almost all other libraries” stay MPL 2.0. |
| 20 September 2023 | The Linux Foundation announces OpenTofu, previously OpenTF, a fork of MPL-licensed Terraform. |
| 4 October 2023 | Terraform 1.6.0, the first version under the BSL. |
| 10 January 2024 | OpenTofu 1.6.0, its first stable release. |
| 24 April 2024 | IBM agrees to buy HashiCorp for $35 a share in cash, an enterprise value of $6.4 billion. |
| 27 February 2025 | IBM completes the acquisition. |
| 23 April 2025 | OpenTofu is accepted into the CNCF at Sandbox level. |
Today Terraform’s licence file names International Business Machines Corporation as licensor, HashiCorp’s pricing page sells “IBM HCP Terraform”, and the licence is still BSL 1.1. Its Additional Use Grant allows production use unless you offer Terraform to third parties, hosted or embedded, in a paid product competing with IBM’s paid versions. Internal use is explicitly allowed, so it matters only if you sell a product or managed service built on Terraform.
Each version converts to MPL 2.0 four years after publication, so 1.6.0 becomes MPL 2.0 in October 2027. Providers are separate projects: the AWS provider is still MPL 2.0, which is why OpenTofu and Pulumi can use it. OpenTofu is MPL 2.0; Pulumi’s engine and CLI are Apache 2.0.
Current versions
| Tool | Latest stable | Released | Notes |
|---|---|---|---|
| Terraform | 1.17.2 | 11 November 2026 | 1.17.0 shipped 21 October 2026; the 1.16 line still gets patches (1.16.7) |
| OpenTofu | 1.13.9 | 5 November 2026 | 1.12.12 shipped the same day; 1.13 has security support until August 2027 |
| Pulumi CLI | 3.276.0 | 12 November 2026 | A new minor release most weeks |
Terraform and OpenTofu: what differs in practice
They share the language, the providers, the plan and apply workflow and, at the fork, the state format. You type tofu instead of terraform. Since 2023 several features have landed in both, at different versions.
| Feature | Terraform | OpenTofu |
|---|---|---|
| Client-side state and plan encryption | No; the docs rely on the backend encrypting at rest, plus Hold Your Own Key in HCP Terraform | Yes, since 1.7 |
| Variables in backend blocks | No: a backend “cannot refer to named values” | Yes, since 1.8, resolved at init |
Variables in module source and version |
1.15, via const variables |
1.8 |
Provider-defined functions (provider::<name>::<function>) |
1.8, 10 April 2024 | 1.7, 30 April 2024, plus functions defined dynamically from configuration |
| Deprecated variables and outputs | 1.15 | 1.10 |
| Ephemeral values | 1.10 (ephemeral variables) | 1.11 (ephemeral resources and write-only attributes) |
destroy = false in lifecycle |
1.16 | 1.12 |
S3 native locking (use_lockfile) |
Yes; DynamoDB locking deprecated | Yes; DynamoDB locking “fully supported”, no plans to deprecate |
| Test framework | terraform test, mocks since 1.7 |
tofu test, mocks since 1.8 |
| Default registry | registry.terraform.io |
registry.opentofu.org |
OpenTofu’s state encryption
State holds every attribute of every resource, including passwords that providers return. Terraform’s docs call local state “a plaintext file” and say encryption at rest “depends on your specific backend”. OpenTofu encrypts state and plan files on the client, with PBKDF2, AWS KMS, GCP KMS, Azure Key Vault, OpenBao or external key providers and AES-GCM.
For an existing project, the documented migration adds an unencrypted fallback so the old state can be read once:
terraform {
encryption {
method "unencrypted" "migrate" {}
key_provider "aws_kms" "state" {
kms_key_id = "a4f791e1-0d46-4c8e-b489-917e0bec05ef"
region = "us-east-1"
key_spec = "AES_256"
}
method "aes_gcm" "state" {
keys = key_provider.aws_kms.state
}
state {
method = method.aes_gcm.state
fallback {
method = method.unencrypted.migrate
}
}
}
}
Run tofu apply, then remove the fallback block and set enforced = true inside state so plaintext can never be written again. Plans are only encrypted if you add a plan {} block. In CI, the same configuration can come from the TF_ENCRYPTION environment variable.
The docs’ warnings matter: lose the key and you lose the state, never rename key providers or methods after encrypting, and use a separate KMS key per state file. Terraform cannot read encrypted state, so this step ends easy rollback.
Variables in backend blocks
OpenTofu 1.8 (29 July 2024) allowed variables and locals in module sources, backend configuration and encryption blocks, so region = local.region works inside backend "s3". Values must resolve during tofu init, before state exists, and the docs advise against secrets there. Terraform has closed part of this gap: 1.15 (29 April 2026) added const variables for module source and version, and 1.17 allows variables and locals in provider requirements. Backend blocks still cannot use them, so in Terraform you keep using -backend-config files.
What Terraform has that OpenTofu does not
- Stacks, covered below, in HCP Terraform only.
- List resources and
terraform query(1.14, 19 November 2025):*.tfquery.hclfiles that search existing infrastructure and can generate import configuration. - Actions (1.14): provider-defined operations outside create, read, update and delete.
- Terraform Policy, generally available in 1.17 through the
-policiesflag. - HCP Terraform and Terraform Enterprise, which run Terraform. Moving to OpenTofu means moving runs to your own CI or another platform.
I found no equivalent of these in OpenTofu’s 1.10 to 1.13 release notes.
What OpenTofu has that Terraform does not
- State encryption and backend variables, above.
.tofufiles. Whenmain.tofuandmain.tfboth exist, OpenTofu ignores the.tffile, so module authors can ship OpenTofu-only features and stay Terraform-compatible.- OCI registry distribution of providers and modules (1.10), aimed at air-gapped environments.
- The
enabledmeta-argument (1.11), insidelifecycle, for resources that exist zero or one times.
I could not confirm whether Terraform has equivalents of the last two.
Registries
hashicorp/aws means registry.terraform.io/hashicorp/aws in Terraform and registry.opentofu.org/hashicorp/aws in OpenTofu. The OpenTofu registry reviews submissions made through GitHub issue forms and treats published versions as immutable. Pulumi also fetches Terraform providers from it by default.
Testing
Both tools run *.tftest.hcl files. This example from Terraform’s docs works under terraform test or tofu test:
# valid_string_concat.tftest.hcl
variables {
bucket_prefix = "test"
}
run "valid_string_concat" {
command = plan
assert {
condition = aws_s3_bucket.bucket.bucket == "test-bucket"
error_message = "S3 bucket name did not match expected"
}
}
The default command is apply, which builds real infrastructure; plan keeps it to logic checks. Add mock_provider "aws" {} to test without credentials. OpenTofu also reads *.tofutest.hcl, which takes precedence over a .tftest.hcl of the same name.
Terraform Stacks
Stacks became generally available on 25 September 2025 for HCP Terraform plans based on resources under management. A Stack groups modules as components (*.tfcomponent.hcl) and repeats them as deployments (*.tfdeploy.hcl), one per environment, account or region, each with its own state. Limits are 20 deployments, 100 components and 10,000 resources per Stack, and legacy team plans cannot use them.
Stacks replace what many teams do with a directory per environment and a wrapper tool. The docs describe them only inside HCP Terraform, and I could not confirm Terraform Enterprise support, so adopting Stacks means adopting HCP Terraform.
HCP Terraform pricing
HCP Terraform bills by managed resource:
| Plan | Price | Notes |
|---|---|---|
| Free | $0 | 500 managed resources; unlimited users, but no teams; 1 concurrent remote run and 1 agent run |
| Essentials | From $0.10 per resource a month ($0.00013 an hour) | Pay as you go |
| Standard | From $0.47 per resource a month ($0.00064 an hour) | Through sales |
| Premium | From $0.99 per resource a month ($0.00135 an hour) | Through sales |
| Terraform Enterprise | Custom | Self-managed |
Billing is hourly on the peak count. null_resource, terraform_data and data sources are not counted. The legacy free plan ended on 31 March 2026, and remaining organisations moved to the 500-resource tier. The pricing page does not say whether the first 500 stay free on a paid plan, so budget as if every resource is billed: 3,000 resources on Essentials is about $300 a month at list price. If you are trimming cloud spend too, see my AWS cost optimisation checklist.
Pulumi
Pulumi programs are written in TypeScript, Python, Go, .NET, Java, YAML or HCL. The program declares the desired state; pulumi preview shows the diff and pulumi up applies it. Loops, types, packages and unit tests come from the language.
Terraform providers. Any Terraform or OpenTofu provider can be used, with a typed SDK generated in your language:
pulumi package add terraform-provider hashicorp/random 3.7.1
HCL. With runtime: hcl in Pulumi.yaml (CLI 3.256.0 or later), Pulumi runs .tf files on its own engine. It ignores backend and required_version, treats a cloud block as an error, and does not read Terraform state; existing resources come in with pulumi import --from hcl <statefile>.
State. Pulumi Cloud by default, or a DIY backend with file-based locking, such as pulumi login s3://<bucket-name> (also azblob://, gs:// and --local). DIY backends lack Pulumi Cloud’s audit, drift detection and recovery from some partial failures. Pulumi Cloud can also be a state backend for Terraform and OpenTofu (hostname = "tf.pulumi.com" in a backend "remote" block), where each Terraform resource counts as a managed resource.
Testing. Unit tests run in memory with mocks in your language’s usual framework. Property tests use Pulumi policies (Node.js and Python). Integration tests deploy, check and destroy real infrastructure.
Pulumi ESC manages environments, configuration and secrets. It pulls from AWS Secrets Manager, Azure Key Vault, GCP Secret Manager, HashiCorp Vault and 1Password, issues short-lived cloud credentials, and works with applications and pulumi env run, not only Pulumi programs.
Pulumi Cloud pricing
| Edition | Price | What you get |
|---|---|---|
| Free | $0 | 1 user, no managed resource limit, 500 workflow minutes a month, 25 ESC secrets, 10,000 ESC API calls a month |
| Essentials | $40 a month, including 40 credits | Up to 500 managed resources; unlimited users; resources at $0.00025 an hour (about $0.18 a month) |
| Pro | $400 a month, including 400 credits | Up to 2,000 managed resources; SAML SSO, RBAC, audit logs, drift detection; resources from $0.0005 an hour |
| Enterprise | Custom | Self-hosting available |
One credit is $1. Usage beyond the pool is billed in arrears, and unused credits expire monthly. On Essentials, ESC secrets cost $0.50 a month each. The single-user limit is what matters: a team of two pays, or runs a DIY backend.
CDK for Terraform is gone
HashiCorp deprecated CDK for Terraform on 10 December 2025 and archived the repository the same day; there will be no further fixes. The README offers two exits: cdktf synth --hcl to generate Terraform-compatible .tf files, or AWS CDK if you are already built around it. Pulumi’s migration docs start from that same synthesised HCL, and for teams that chose CDKTF for a general-purpose language, Pulumi is the closest match.
Comparison
| Terraform | OpenTofu | Pulumi | |
|---|---|---|---|
| Licence | BSL 1.1, IBM | MPL 2.0 | Apache 2.0 (engine and CLI) |
| Governance | IBM (HashiCorp) | Linux Foundation; CNCF Sandbox | Pulumi |
| Latest stable | 1.17.2 | 1.13.9 | 3.276.0 |
| Language | HCL | HCL | TypeScript, Python, Go, .NET, Java, YAML, HCL |
| Providers | Terraform registry | OpenTofu registry, same providers | Native providers plus any Terraform provider |
| State encryption | Backend at rest; HYOK in HCP Terraform | Built in, client side | Secrets encrypted; customisable provider |
| Testing | terraform test |
tofu test |
Your language’s test framework |
| Hosted service | HCP Terraform, free to 500 resources | None from the project | Pulumi Cloud, free for 1 user |
State management and CI patterns
The same rules apply to all three.
- Remote state with locking. For S3, set
use_lockfile = true; Terraform and OpenTofu both support it. - One state per environment and blast radius, so a plan never touches everything.
- Plan on pull requests, apply on merge. HashiCorp’s automation guide uses
terraform init -input=false,terraform plan -out=tfplan -input=falseandterraform apply -input=false tfplan. A saved plan contains configuration, state and variables, so protect it, and provider versions must match between plan and apply. - No stored cloud keys. Use OIDC; my CI/CD tools comparison has the AWS trust policy.
This GitHub Actions workflow plans pull requests with a read-only role and applies main with a separate one:
name: infra
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
id-token: write
jobs:
plan:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: opentofu/setup-opentofu@v2
with:
tofu_version: 1.13.9
- uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: arn:aws:iam::123456789012:role/infra-plan
aws-region: eu-west-2
- run: tofu init -input=false
- run: tofu plan -input=false
apply:
if: github.event_name == 'push'
runs-on: ubuntu-latest
environment: production
concurrency:
group: infra-production
cancel-in-progress: false
steps:
- uses: actions/checkout@v7
- uses: opentofu/setup-opentofu@v2
with:
tofu_version: 1.13.9
- uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: arn:aws:iam::123456789012:role/infra-apply
aws-region: eu-west-2
- run: tofu init -input=false
- run: tofu plan -input=false -out=tfplan
- run: tofu apply -input=false tfplan
For Terraform, use hashicorp/setup-terraform@v4 with terraform_version: 1.17.2 and run terraform instead of tofu. Pull request jobs and jobs in the production environment present different OIDC subjects, so write each role’s trust policy for its own job.
Migration paths
Terraform to OpenTofu
OpenTofu’s guide calls the process “safe and reversible”:
- Back up state (local files, or your backend’s backups such as S3 versioning) and commit configuration on a migration branch.
- Install OpenTofu and check it with
tofu --version. - Run
tofu init. Providers now come from the OpenTofu registry; review the lock file changes. - Run
tofu plan. You should see “No changes” or the plan Terraform would produce. If not, stop. - Run
tofu apply. This can update the state format even with no infrastructure changes. - Make a small change, such as a tag, and plan and apply it.
To roll back, restore state from backup if it changed, then run terraform init and terraform plan.
If configurations share data through terraform_remote_state, migrate the ones that read state before the ones they read from. OpenTofu reads Terraform 1.x state without modification, but “Terraform may not reliably read OpenTofu state files”, so avoid OpenTofu-only features until everything has moved.
Also check cloud blocks, WinRM provisioners (removed in OpenTofu 1.13) and required_version, which older OpenTofu versions treated as an OpenTofu constraint. The documented fix is a versions.tofu file, read instead of versions.tf, holding language { compatible_with { opentofu = ">= 1.12" } }.
Terraform to Pulumi
There are four documented routes, which you can combine.
Convert the code. From the directory with your .tf files:
pulumi convert --from terraform --language typescript --out ../pulumi-infra
Python, Go and C# also work. Modules become components; anything unsupported becomes a notImplemented stub to finish by hand.
Import the state. Pull remote state to a file, then import it into a Pulumi stack. Imported resources are marked protected:
terraform state pull > terraform.tfstate
pulumi import --from terraform ./terraform.tfstate
Translate state first. pulumi plugin run terraform-migrate -- stack (with --from, --to, --out and --plugins) converts Terraform state into a file for pulumi stack import. It needs tofu on your PATH and only runs tofu init and tofu refresh, never an apply.
Coexist. Pulumi programs can read Terraform outputs with terraform.state.getRemoteReference or getLocalReference, so new services can start in Pulumi while old ones stay put.
Whichever route you take, check that pulumi preview shows no unexpected changes to existing resources before you rely on it.
What I’d pick
A small team starting fresh. OpenTofu, if the team is comfortable with HCL: an open licence, the same providers and modules, client-side state encryption and no per-resource bill. Keep state in object storage with locking, encrypt it with a KMS key from day one, and run plans from CI with OIDC. If you would rather not build that pipeline, HCP Terraform’s free tier is a real offer, but price the step after 500 resources first.
An existing Terraform estate. If you use, or plan to use, Stacks, policy or HYOK on HCP Terraform, stay and budget for managed resources. If you run the CLI in your own CI with state in a bucket, OpenTofu is a cheap change you can reverse, provided you hold off on encryption and .tofu files until you have decided. If you sell a hosted product built on Terraform, read the Additional Use Grant with your lawyer.
A developer-heavy team. Pulumi, in the language the team already uses. Infrastructure gets the same reviews, tests and packaging as application code, Terraform providers stay available, and ESC handles secrets. CDKTF users should move here or back to HCL. Budget for Essentials or a DIY backend once two people deploy, and review the code strictly: a general-purpose language makes clever code easy.
If you use AI agents on infrastructure code, HashiCorp’s Terraform MCP server is covered in MCP servers for DevOps engineers.
Migration checklist
- Inventory every state: backend, managed resource count, tool and provider versions,
terraform_remote_statelinks,cloudblocks, provisioners. - Turn on versioning for every state bucket and take a backup before any change.
- Upgrade all configurations to the same Terraform version first.
- Migrate one low-risk configuration on a branch, readers before the states they read.
- Require “No changes” from
tofu planorpulumi previewbefore the first apply. - Update CI setup steps, pin tool versions and keep OIDC credentials.
- Enable state encryption only once stable: a KMS key per state, keys backed up,
fallbackfor the switch, thenenforced = true. - Keep backups and the old tool version until several weeks pass without surprises.
- On CDKTF, run
cdktf synth --hclnow and choose between HCL and Pulumi.
Sources
- HashiCorp adopts Business Source License (HashiCorp blog) hashicorp.com
- Terraform LICENSE (GitHub) github.com
- Terraform AWS provider LICENSE (GitHub) github.com
- Announcing OpenTofu (Linux Foundation) linuxfoundation.org
- OpenTofu LICENSE (GitHub) github.com
- OpenTofu project page (CNCF) cncf.io
- IBM to acquire HashiCorp (IBM Newsroom) newsroom.ibm.com
- IBM completes acquisition of HashiCorp (IBM Newsroom) newsroom.ibm.com
- Pulumi LICENSE (GitHub) github.com
- Terraform releases (GitHub) github.com
- Terraform v1.17 changelog (GitHub) github.com
- Terraform v1.16 changelog (GitHub) github.com
- Terraform v1.15 changelog (GitHub) github.com
- Terraform v1.14 changelog (GitHub) github.com
- OpenTofu releases (GitHub) github.com
- OpenTofu v1.13 changelog (GitHub) github.com
- OpenTofu v1.13.0 (OpenTofu blog) opentofu.org
- OpenTofu v1.12.0 (OpenTofu blog) opentofu.org
- OpenTofu v1.11.0 (OpenTofu blog) opentofu.org
- OpenTofu v1.10.0 (OpenTofu blog) opentofu.org
- OpenTofu v1.8.0 (OpenTofu blog) opentofu.org
- OpenTofu v1.7.0 (OpenTofu blog) opentofu.org
- State and plan encryption (OpenTofu docs) opentofu.org
- Backend configuration (OpenTofu docs) opentofu.org
- Input variables (OpenTofu docs) opentofu.org
- OpenTofu settings (OpenTofu docs) opentofu.org
- Provider requirements (OpenTofu docs) opentofu.org
- S3 backend (OpenTofu docs) opentofu.org
- Command: test (OpenTofu docs) opentofu.org
- Migration guide (OpenTofu docs) opentofu.org
- Migrating interdependent configurations (OpenTofu docs) opentofu.org
- opentofu/registry (GitHub) github.com
- opentofu/setup-opentofu (GitHub) github.com
- Backend block configuration (Terraform docs) developer.hashicorp.com
- Variable block reference (Terraform docs) developer.hashicorp.com
- S3 backend (Terraform docs) developer.hashicorp.com
- Sensitive data in state (Terraform docs) developer.hashicorp.com
- Provider requirements (Terraform docs) developer.hashicorp.com
- Functions (Terraform docs) developer.hashicorp.com
- Terraform 1.8 improves extensibility with provider-defined functions (HashiCorp blog) hashicorp.com
- Terraform 1.8 provider functions for AWS, Google Cloud, and Kubernetes (HashiCorp blog) hashicorp.com
- Tests (Terraform docs) developer.hashicorp.com
- Mocks (Terraform docs) developer.hashicorp.com
- Stacks (Terraform docs) developer.hashicorp.com
- Scale infrastructure with new Terraform and Packer features at HashiConf 2025 (HashiCorp blog) hashicorp.com
- HashiCorp pricing hashicorp.com
- HCP Terraform plans and features (Terraform docs) developer.hashicorp.com
- Continuing HCP Terraform's enhanced free tier experience (HashiCorp blog) hashicorp.com
- Running Terraform in automation (HashiCorp tutorial) developer.hashicorp.com
- hashicorp/setup-terraform (GitHub) github.com
- hashicorp/terraform-cdk (GitHub) github.com
- CDK for Terraform (Terraform docs) developer.hashicorp.com
- Pulumi releases (GitHub) github.com
- Pulumi pricing pulumi.com
- Migrating from Terraform to Pulumi (Pulumi docs) pulumi.com
- pulumi convert (Pulumi CLI docs) pulumi.com
- Using any Terraform provider (Pulumi docs) pulumi.com
- HCL (Pulumi docs) pulumi.com
- Terraform state backend (Pulumi docs) pulumi.com
- Pulumi ESC (Pulumi docs) pulumi.com
- State and backends (Pulumi docs) pulumi.com
- Using a DIY backend (Pulumi docs) pulumi.com
- Testing Pulumi programs (Pulumi docs) pulumi.com
- actions/checkout releases (GitHub) github.com
- aws-actions/configure-aws-credentials releases (GitHub) github.com