Avoid Provider-Specific Infrastructure as Code
Provider-specific infrastructure as code locks your whole architecture to one cloud. How to write portable IaC so your infrastructure can move when you do.
Infrastructure as code is supposed to make your stack portable and reproducible. Done wrong, it does the opposite: it encodes your entire architecture in one cloud's proprietary dialect so deeply that the definition of your infrastructure only makes sense on that provider. Now your servers are locked in and so is the blueprint for rebuilding them. When people say their cloud migration would take months, the provider-specific IaC is a big part of why. Writing portable infrastructure code costs a little discipline up front and keeps the door open forever. It is one of the least glamorous and most important pieces of owning your stack.
Why provider-specific IaC is a lock-in trap
The lock-in hides in a good practice. You define your infrastructure in code, which is correct. But if that code is built entirely out of one provider's proprietary resource types, managed services, and identity model, the code is only meaningful on that provider. You have written a detailed, version-controlled description of a thing that cannot exist anywhere else.
This compounds the problem in a way that surprises people. Without IaC, migrating means rebuilding infrastructure by hand, which is painful but conceptually simple. With deeply provider-specific IaC, migrating means rewriting the entire blueprint, because the abstractions you built on have no equivalent elsewhere. The tool meant to save you now encodes your dependence. It is the same dynamic as serverless being the deepest lock-in: the more you lean on proprietary building blocks, the higher the wall around you.
What portable infrastructure code looks like
Portability does not mean avoiding IaC or avoiding cloud services. It means drawing a clear line between the parts that are inherently portable and the parts that are not, and keeping the proprietary parts contained.
Build on standard primitives where you can. A plain virtual machine, a block volume, a network, a load balancer, these concepts exist on every provider and on your own hardware. Infrastructure defined in terms of these moves with modest effort. Infrastructure defined in terms of one vendor's exclusive managed platform does not.
Run your actual workloads in containers. A container is the same everywhere, so the thing running your app is portable by construction even if the machine under it changes. That is the same reason git-push deploy on your own box works identically whether the box is a cloud VM or hardware you own.
Isolate the proprietary parts. If you do use a provider-specific managed service, wrap it behind an interface in your own code so swapping it later means changing one module, not the whole system. Contain the lock-in to a corner instead of letting it spread through the blueprint.
Keep the config declarative and readable. The value of IaC is that the definition is the documentation. A portable definition tells you what to rebuild anywhere, which is what makes disaster recovery when you self-host a script instead of a scramble.
How to keep IaC portable in practice
A few habits keep the door open without slowing you down.
Prefer tools and formats that target more than one backend. Infrastructure code that can point at multiple providers, or at your own machines, forces you toward the portable subset by design. Code that only speaks one cloud's API pulls you the other way.
Separate the durable from the disposable. Your data and state live on infrastructure you control, defined simply, while stateless compute can run wherever is cheapest. That split is the same one behind a hybrid stack: own the base, keep the elastic part swappable.
Test the rebuild. The real proof that your IaC is portable is that you can stand the stack up somewhere else from the definition. Rehearse it, the same way you would run a vendor exit fire drill. If the rebuild only works on one provider, you found the lock-in before it trapped you.
When provider-specific IaC is acceptable
I am not dogmatic about this. If you have genuinely committed to one cloud for the long haul and have no intention of ever leaving, leaning into its native IaC is efficient, and fighting it for portability you will never use is wasted effort. Purity for its own sake is a cost too.
But most people have not actually made that decision; they drifted into deep provider-specific IaC by convenience and only discover the lock-in when they want out. The cheap insurance is to keep the durable core portable and contain the proprietary parts, so that leaving is a decision rather than a rebuild. That is the whole own-your-stack principle applied to the blueprint itself: your infrastructure code should describe something you can run anywhere, including on hardware you own at HostSSH, not just on the one cloud that wrote the dialect.