The Invoice ■ Episode 14
“Manage infrastructure as code! Declarative, predictable, multi-cloud!”
Splendid. Let us examine what you are actually paying for.
You wanted to provision a server. A reasonable ambition. What arrived was a proprietary language that cannot express a conditional, a state file that stores your database passwords in plain text, and a licence that changed overnight after nine years of open-source trust. Three invoices for one abstraction. Shall we itemise them.
The Language Invoice
HCL, the HashiCorp Configuration Language, is not a programming language.
It is a configuration format that grew ambitions. No conditionals, only
ternary expressions. No loops, only
count,
a numerical index that was never designed as an iteration primitive but
became one because nothing else was available. No first-class functions.
Resource references that routinely exceed 100 characters. A language
whose expressiveness peaked at key = value and was then
asked to orchestrate global infrastructure.
The canonical example of HCL's limitations is the count hack:
count = var.enabled ? 1 : 0. A looping construct abused as
a boolean. It functions adequately until you have a list of resources
indexed by count and remove an item from the middle. Terraform does not
track resources by identity. It tracks them by index. Remove item two
from a list of five and items three, four, and five are destroyed and
recreated. Your infrastructure, rebuilt because an array shifted.
The for_each construct arrived later as a partial remedy,
but it cannot be used everywhere count can, and the damage
to existing codebases was already done. A configuration language that
destroys infrastructure when you reorder a list is not declarative. It
is a trap with documentation.
The State Invoice
terraform.tfstate is a JSON file containing every secret your
infrastructure possesses. Database passwords, API keys, connection strings,
private IP addresses. In
plain text.
By default, stored locally on whatever machine ran terraform apply.
The recommended solution: remote state backends. S3 with encryption, Terraform Cloud, Consul. Each backend introduces its own failure modes, access control requirements, and operational overhead. You are now managing infrastructure to manage the tool that manages your infrastructure. One admires the recursion.
At scale, the state file becomes the bottleneck. At
1,000 resources the state file reaches 20 MB and consumes gigabytes of RAM
to parse. Each terraform plan fires over 1,000 API calls to
reconcile declared state against reality. Beyond
2,000 resources, plan time grows exponentially.
The
recommended maximum: 200 resources per state.
One deployment, a dozen state files. One rather admires the elegance.
Then there is drift. Someone edits a security group in the console.
Terraform does not know. The next apply overwrites it
silently. According to
Spacelift,
90 per cent of drift is caused by humans using the very console the tool
was meant to replace. The tool that promises to be the single source of
truth quietly becomes the single source of surprise.
The Licence Invoice
In August 2023, HashiCorp switched Terraform from the Mozilla Public Licence to the Business Source Licence. Nine years of open-source trust, revised in one announcement. The BSL permits use but prohibits competing products. Your infrastructure tooling, now governed by a licence whose boundaries are defined by the vendor's competitive anxieties.
The community responded with remarkable speed. Within a month, OpenTofu forked under the Linux Foundation and collected 33,000 GitHub stars and pledges from over 140 companies. In February 2025, IBM acquired HashiCorp for $6.4 billion. Your infrastructure tooling now belongs to IBM. Quite the plot twist for a tool that promised vendor independence.
The numbers tell the rest of the story. 45 per cent of DevOps teams are now evaluating alternatives. Yet 80 per cent of IaC work still runs on Terraform. The gap between those two figures is not satisfaction. It is inertia. The same force that keeps teams on Jira long after the joy has left.
The Abstraction Invoice
Terraform promises cloud-agnosticism. In practice, you learn three things: HCL, the cloud provider's API, and the provider plugin's quirks. Three knowledge layers for one abstraction. The AWS provider maps AWS resources. The Azure provider maps Azure resources. Each with its own naming conventions, its own argument structure, its own undocumented behaviours. The abstraction does not abstract. It translates, and the translation has an accent.
The multi-cloud promise is the centrepiece of the sales deck. In practice, nobody is going multi-cloud. You pick AWS or Azure or GCP, you commit, and you stay. The portability argument justifies the tool, not the architecture. When was the last time you saw an organisation migrate from AWS to Azure because Terraform made it easy? Quite.
The Alternative
OpenTofu is the same tool without the licence risk. A drop-in replacement under the Linux Foundation, maintained by the community that built most of the providers in the first place. If your codebase is already Terraform, this is the path of least resistance and the most sensible first step.
Pulumi takes the opposite approach: actual programming languages. TypeScript, Python, Go. Real conditionals. Real loops. Real functions. The infrastructure code lives in the same language as the application code, and the IDE already knows how to help you write it.
Ansible is agentless, idempotent, and mature. No state file, no drift by design, no proprietary language. It describes what should exist and makes it so. Shell scripts and cloud CLIs: transparent, debuggable, no state file, no abstraction layer, no licence concern. They have provisioned servers for decades.
Or simpler still, do it the Unix way: a Makefile, a CLI, and a deployment that fits in your head.
The Pattern
Infrastructure was already manageable. Shell scripts provisioned servers for decades. Ansible configured them. Cloud CLIs controlled them. The tools existed. The workflows existed. The knowledge existed.
Then Terraform added a DSL that cannot express a conditional, a state file that stores secrets in plain text, and a licence that belongs to IBM. Three new problems for one abstraction. The infrastructure was already there. The tooling was already there. The invoice was not.
A configuration language that destroys infrastructure when you reorder a list, stores your secrets in plain text, and changed its licence overnight is not infrastructure as code. It is infrastructure as risk.
The invoice is not a single line item. It is the compound of a language without conditionals, a state file without encryption, a licence without continuity, and an abstraction that requires three knowledge layers to operate. You wanted to provision a server. What arrived was a new class of problems dressed as a solution.
Rather an expensive abstraction.