I have written a lot of Terraform across AWS, Azure and GCP, including an open-source Python helper for generating it across all three. The most expensive lesson in that work is about abstraction. The instinct is to write one "database" module that works on any cloud. It is almost always a mistake.
Why the universal module fails
Managed services differ in ways that matter: networking models, identity, backup semantics, failover, pricing. A module that hides all of that has two options. It exposes only what every cloud supports, so you lose the features you're paying for. Or it grows a flag for every provider difference, until it is harder to use than the raw resources.
HashiCorp's own documentation makes the point that Terraform deliberately doesn't abstract over vendors, and suggests building lightweight multi-cloud abstractions through composition instead1.
Composition instead
The composition pattern keeps modules small and lets a root module wire them together. Child modules receive what they depend on, such as network and subnet IDs, as inputs rather than creating them, so the same blocks can be assembled differently per environment1. Real configurations should almost always be built from modules like this2.
| Layer | Abstract it? | Why |
|---|---|---|
| Naming, tagging, labels | Yes, fully | Same rules everywhere; drives cost allocation and ownership |
| Policy: encryption, logging, regions | Yes, as shared inputs and checks | Compliance should not depend on which cloud a team picked |
| Kubernetes clusters | Partly: shared interface, provider modules underneath | Workloads target Kubernetes, not the cloud |
| Networking | No, provider-specific | Models differ too much to hide safely |
| Managed databases and AI services | No, provider-specific | Their differences are the reason to use them |
Abstract the things that should be the same everywhere: names, tags, policy, and the contract your applications see. Leave the cloud-specific parts cloud-specific.
The shape I use
- A conventions module that only computes names and tags from a few inputs. Every other module consumes it.
- Provider-specific building blocks for network, cluster, database and secrets, each small and documented.
- Thin "product" root modules per environment that compose the blocks and nothing else.
- Policy checks in CI that fail a plan which, for example, creates an unencrypted bucket or deploys outside approved regions. This matters for data residency, which I discuss in UAE PDPL and AI.
- Kubernetes as the portability layer. If applications need to move between clouds, make them depend on Kubernetes interfaces, not on cloud APIs. Cost controls such as scale-to-zero then work the same everywhere, as in my Kubernetes FinOps playbook.
Terraform or OpenTofu?
In August 2023 HashiCorp moved Terraform from the Mozilla Public License to the Business Source License. The community fork, OpenTofu, was accepted by the Linux Foundation that September and remains MPL-licensed34.
For most companies using Terraform internally, either works. The decision usually comes down to licence policy, whether you build a product that competes with HashiCorp, and which newer features each tool offers. My practical advice is to keep modules free of features specific to one tool unless you need them, so switching later stays cheap. I'd record the choice in an architecture decision record either way.
Signs your IaC needs work
- Changes are made in the console "just this once" and never reach code.
- Each environment has its own copy of the configuration instead of composing shared modules.
- Nobody can say which team owns a resource, because tags are inconsistent.
- A plan for a small change touches hundreds of resources.
These show up in almost every technical due diligence, and they predict how expensive the next year of operations will be.
How I can help
I design infrastructure-as-code foundations across AWS, Azure and GCP that teams can actually maintain. As a fractional CTO I can also own the platform roadmap. Or analyse your challenge and bring the result to a call.
- 1Module compositionHashiCorp Developer
- 2Modules overviewHashiCorp Developer
- 3Linux Foundation joins OpenTF to fork Terraform into OpenTofuThe New Stack · Sep 2023
- 4