Vivian Voss

Terraform: The Abstraction Layer

devops cloud licensing tooling

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 Count Index Hack Before: Server A [0] Server B [1] Server C [2] Server D [3] Server E [4] remove B After: Server A [0] Server B C → [1] destroy + recreate D → [2] destroy + recreate E → [3] destroy + recreate Terraform tracks by index, not identity. Remove one item. Three servers destroyed. Marvellous.

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.

State File Scaling Resources Cost 200 OK best practice 500 5 MB 1,000 20 MB GB of RAM 2,000 exponential 5K+ Each plan: 1,000+ API calls. Secrets in plain text JSON. Quite the state of affairs.

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 Licence Timeline 2014 Terraform released under MPL 2.0 Open source. Community builds providers. Trust established Aug 2023 Licence switched to BSL Nine years of open-source trust, revised overnight Sep 2023 OpenTofu fork under Linux Foundation 33,000 GitHub stars in one month. 140+ companies pledged support Feb 2025 IBM acquires HashiCorp for $6.4 billion Your infrastructure tooling belongs to IBM 2026 45% of DevOps teams evaluating alternatives

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.

Three Knowledge Layers for One Abstraction Layer 1: HCL No conditionals No loops, no functions + Layer 2: Cloud API AWS, Azure, GCP Each with own semantics + Layer 3: Plugin Provider quirks Undocumented behaviour You are not going multi-cloud. Nobody is. The portability argument funds the sales deck, not the architecture The abstraction does not abstract. It translates. 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 Alternatives OpenTofu Drop-in replacement MPL-licensed Linux Foundation Least resistance Pulumi TS, Python, Go Real conditionals Real functions Your IDE already helps Ansible No state file Agentless Idempotent No drift by design Shell + CLI Transparent Debuggable No state file Decades of proof Or simpler: 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.