I adopted Terraform because I was told it was an answer to cloud lock-in.
The argument was easy to understand. Entire businesses were being built on AWS. Terraform offered one language above the providers, so infrastructure would no longer belong entirely to one of them. If circumstances changed, we would still have options.
That is the pitch I heard. It is also important to say what the historical record does and does not support. HashiCorp announced Terraform in July 2014 as a way to manage infrastructure spanning multiple providers. Its launch-era documentation called the product cloud agnostic and emphasized combining multiple providers and services. But HashiCorp generally did not promise that an AWS resource definition could be moved unchanged to Azure. Its own technical explanation is explicit: Terraform intentionally does not abstract similar services across vendors because the result would tend toward a lowest-common-denominator interface. HashiCorp later distinguished workload portability from the thing Terraform could actually provide: workflow portability.
So this is not an accusation that Terraform failed to implement its documented architecture. The architecture works as designed. It is an argument about the stronger portability story I understood when I adopted it—and about the dependency I accepted in return.
The resource definitions were never portable
Terraform gives users a common language across providers, not a common resource model. You do not declare a generic virtual machine. You declare an aws_instance, with an AMI, an AWS instance type, an AWS subnet, and AWS lifecycle behavior. An Azure virtual machine is a different resource with different arguments, network objects, identity semantics, disks, and images.
There is no switch that points the first resource at the second provider. Moving it means replacing the resource and redesigning the infrastructure around it.
This is not a missing feature. It is the honest consequence of the problem. Clouds compete through the services and semantics that distinguish them. An abstraction can preserve a portable common subset, but every vendor-specific capability it exposes narrows that portability; erase enough differences and users are left with the intersection of the platforms’ capabilities. Terraform chose the useful tradeoff: a consistent workflow over provider-defined schemas.
Teams can build narrower abstractions in modules. A company can define its own opinionated “service” interface and implement it differently on two clouds. That can reduce migration work within the boundary the company controls. It does not turn the underlying provider resources into substitutes, and it does not translate the identity, network, managed-service, and failure semantics surrounding them. A substantial cloud migration remains a redesign. Terraform can organize that work; it cannot make it disappear.
A second layer of dependency
Terraform did not remove cloud lock-in. It put an additional toolchain switching cost beside it.
The first part is state. Terraform needs a snapshot that binds configuration addresses to remote objects and caches resource attributes. The raw file is an internal JSON format, not a secret proprietary encoding, and OpenTofu can read Terraform 1.x state. But it is still a tool-controlled operational artifact. Lose those bindings and Terraform can lose track of live resources. Corrupt them and recovery may mean imports or careful state repair against a running system.
CloudFormation represents resources as an AWS-managed stack. Azure Resource Manager reconciles deployments against live Azure resources and records deployment history without requiring a separate state file. In both cases, the provider keeps the bookkeeping inside its control plane. With self-managed Terraform, the team must design state storage, locking, access, partitioning, migration, and recovery.
Then come the providers and modules. An estate accumulates provider binaries, version constraints, module interfaces, registry dependencies, and house conventions. Terraform supports alternate registry hosts and provider mirrors, so its public registry is not an unavoidable single distribution channel. The switching cost is broader: years of infrastructure have been expressed through provider-defined resource types and schemas and Terraform’s module model.
The deepest dependency may be the workflow. Teams build review gates, CI systems, operating procedures, and staff fluency around plan and apply. HashiCorp then built a commercial layer around that workflow: remote state, managed runs, access controls, policy, audit, and collaboration in HCP Terraform and Terraform Enterprise.
None of this makes Terraform bad. Useful standards create switching costs because people organize around them. But it does mean the tool became another vendor relationship whose governance mattered.
When the governance risk became visible
On 10 August 2023, HashiCorp announced that future Terraform releases would move from MPL 2.0 to the Business Source License 1.1. Existing MPL releases remained MPL. The first stable BSL release was Terraform 1.6.0 on 4 October 2023.
The practical effect on most internal users was small. HashiCorp’s additional-use terms continued to permit internal production use and consulting. The restriction chiefly affected vendors hosting or embedding Terraform in an offering that competed with HashiCorp’s products. The same FAQ says providers, APIs, SDKs, and most related libraries remained MPL. This was not a moment when ordinary teams suddenly had to pay to keep running Terraform.
It was still a governance event. A tool embedded in build pipelines across the industry would no longer publish new versions immediately under an open-source licence. Under the BSL, each version converts to MPL 2.0 four years after its publication, so current releases sit on a rolling four-year source-available delay.
The response was unusually fast. The OpenTF manifesto appeared on 15 August 2023. A fork was announced on 25 August, and its repository became public on 5 September, when the project reported more than 33,000 manifesto stars. On 20 September, it joined the Linux Foundation as OpenTofu. Its first stable release, OpenTofu 1.6.0, reached general availability on 10 January 2024.
IBM announced its planned HashiCorp acquisition on 24 April 2024 and completed it on 27 February 2025, at an enterprise value of $6.4 billion. The acquisition did not cause the licence change; it was announced more than eight months later. Its relevance is simpler: ownership of a load-bearing tool can change, just as its licence can.
OpenTofu materially reduces that governance risk. It is an MPL 2.0-licensed implementation under Linux Foundation governance, with a separate registry. It deliberately preserves Terraform’s state, provider, and workflow architecture, so it does not make cloud resources portable. But its compatibility provides an alternate engine and distribution path. That is a real reduction in single-vendor dependence, and it is evidence against overstating Terraform’s lock-in as total.
The value Terraform earned
The provider ecosystem that justifies Terraform today is much stronger than the one available at launch. Terraform v0.1 shipped with six built-in providers: AWS, Cloudflare, Consul, DigitalOcean, DNSimple, and Heroku. By 2018, HashiCorp described more than 100. It later counted more than 3,000 providers.
That breadth changed the practical case for the tool. The estates I have worked in did not end at their cloud boundaries. There was DNS, source control, identity, monitoring, incident response, data platforms, and a long list of SaaS control planes. Terraform can put changes to all of them through a common configuration language and review process. That is real agnosticism—not portable resources, but a portable operating model.
Terraform also helped make infrastructure-as-code mainstream beyond any one cloud. Its best idea is plan: a reviewable preview of proposed production changes before they are applied. A plan is not a guarantee; speculative plans can become stale. But the expectation that infrastructure changes should be previewed, reviewed, and logged was a major improvement in engineering practice.
HCL was designed to be read and written by humans, and in my experience it works well in mixed-skill organizations. Modules provide a useful reuse mechanism. Terraform skills transfer between employers even when the resource code does not. Those are substantial benefits, and they explain why the workflow became difficult to replace.
The operational bill
The same architecture imposes costs. State is not merely a file to put in object storage. It needs encryption, locking, access control, backups, sensible blast-radius boundaries, and tested recovery. Large monolithic states can slow operations and let one bad change reach too far. Splitting them well is architecture work.
State and saved plan files can contain sensitive resource attributes in plaintext. Marking a value sensitive redacts its display; it does not remove it from state. Remote backends can encrypt the artifact at rest, and newer ephemeral and write-only features can omit supported values, but plans, state, and backups still require deliberate handling. In a regulated environment, an unencrypted or broadly accessible state store containing secrets can become a control finding.
Some incidental complexity has improved. Terraform 1.10 introduced opt-in native S3 locking through use_lockfile, implemented with an S3 conditional write. Terraform 1.11 made it generally available and deprecated the DynamoDB lock-table arguments. That removed an extra moving part for teams that migrated to the new mechanism.
HCL’s simplicity also has a limit. The language accumulated conditionals, comprehensions, for_each, dynamic blocks, and error-handling expressions. Each feature is reasonable. Together they can produce code that wants the structure and testability of a general-purpose language without actually being one. HashiCorp itself warns that overusing dynamic blocks and try makes configurations difficult to read.
Terraform can detect drift, but it cannot prevent an authorized person or system from changing infrastructure outside the pipeline. Prevention belongs to IAM, policy, and process. Similarly, -target and manual state commands are valid recovery tools, but Terraform documents targeting as exceptional. If they become routine, the design or workflow is telling you something.
Vendor risk belongs in the build path
Terraform is one of several build-tool dependencies whose terms changed after they became load-bearing.
Serverless Framework v4 introduced authentication and paid licensing for organizations above $2 million in annual revenue, while remaining free for individuals and smaller organizations. The open-source v3 line remained available but became unmaintained. I was part of the decision to leave and migrated that program to AWS SAM.
Docker Desktop followed the same pattern earlier. On 31 August 2021, Docker changed its subscription terms. Desktop remained free for personal use, education, non-commercial open-source work, and small businesses with fewer than 250 employees and less than $10 million in annual revenue; professional use in larger organizations required a paid subscription after a five-month grace period. Docker was explicit that the change did not alter the licensing of Docker Engine or the Moby open-source projects. We replaced Docker Desktop on our Macs with Colima, which supplied the Linux container runtime while preserving the Docker command-line workflow.
LocalStack took a different path. It ended support and regular updates for its legacy Community image on 23 March 2026. The maintained distribution requires an account and authentication token; its permanent Hobby tier is free for non-commercial use, while commercial use requires a paid plan. Updates had also repeatedly broken our team. I built a working replacement. Leadership declined it because one maintainer would have become its own concentration risk—a decision I understood and supported—so I validated external alternatives and left two that passed every test.
Then Terraform changed the licence governing future releases, and HashiCorp was acquired.
Eventually, many of the great developer tools of the previous decade reached the same point: their makers had to make money. That was reasonable. The mistake was treating free access and permissive terms as permanent properties of the architecture. These examples show several ways a free tool can become load-bearing before its maintenance or commercial terms change. Build tools often enter with less vendor-risk scrutiny than purchased platforms even though the eventual dependency can be just as deep.
The engineering response is not cynicism. It is placement. Ask what happens if a tool is no longer free, open, or maintained in three years. Prefer documented interfaces and tested exits. Prove that configuration and state can move to the intended alternative before adopting incompatible features. Account for the risk that an internal replacement merely exchanges vendor concentration for maintainer concentration.
What I would choose now
I would choose Terraform for the honest reason: a broad estate can benefit from one reviewed workflow across many control planes. If the estate is genuinely AWS-only, CloudFormation and AWS CDK deserve a serious hearing. AWS CDK offers general-purpose-language abstractions and testing while remaining explicitly AWS-specific.
I would consider OpenTofu when licence and governance risk are live concerns. I would not migrate a working estate merely to protest HashiCorp’s licence change; migration cost is real, and most internal users can still use Terraform as before.
I would not choose Terraform, CloudFormation, or CDK expecting provider-neutral resource portability. I would design for one production path, split state by blast radius, minimize secrets in state, and secure every state or plan artifact that must contain them.
The objections
“HashiCorp never promised drop-in multi-cloud portability.” Correct. Its own technical materials generally promised multi-provider workflow portability. My argument is about the stronger pitch I encountered, reinforced by launch-era “cloud agnostic” language and the later promise to “onboard any cloud” through one workflow “without vendor lock-in”. The distinction should have been clearer in my decision; it should be explicit in anyone’s decision now.
“Terraform is multi-cloud.” It is. It can manage multiple clouds through one workflow. That is different from making resources portable between them, and the former is valuable without pretending it delivers the latter.
“Every abstraction has limits.” Yes. Terraform Core’s particular choice was not to provide a common cloud-resource abstraction at all. Teams can build bounded module interfaces, but provider-specific implementations remain underneath them.
“The BSL change barely affected end users.” Mostly true. The immediate practical effect fell on vendors hosting or embedding Terraform in competing products, and on the ecosystem around them. Its importance here is as a governance event, not a claim that ordinary users lost access to Terraform.
“OpenTofu solved it.” It solved a meaningful part of it. An open fork under Linux Foundation governance reduces dependence on HashiCorp’s engine, licence, and registry. It preserves the architecture, so state, providers, modules, and workflow remain—but engine-level substitutability is much better than having none.
“This is hindsight.” Partly. Provider-specific resource types made the limitation visible from the beginning. I did not interrogate the portability premise when I adopted Terraform because a cloud exit was not the work in front of me. That is precisely why the lesson applies beyond Terraform: a dependency’s exit path is easiest to ignore before the dependency becomes important.
Terraform is good software. The resource portability argument that drove my adoption was not true, but the tool earned its place through a different value: consistent infrastructure practice across an increasingly broad estate.
Gregor Hohpe argued in 2019 that trying too hard to avoid lock-in can itself become a trap. The further lesson I take from Terraform is that vendor risk belongs in the build path as well as the production platform.
Lock-in is not something you eliminate. It is a set of switching costs you place deliberately. The useful question is not whether you have any, but whether you chose where they sit and tested an exit.
