Terraform Blast Radius: Rethinking Infrastructure Boundaries
Many infrastructure-as-code (IaC) modules of the past have followed a monolithic design strategy, often leading to a cumbersome and inefficient operational environment. Through my experience, I have found that organizing infrastructure around functionality rather than infrastructural layers or service boundaries yields significantly better operability. My perspective may be somewhat skewed, given my background in application development, where I typically manage both service and application workloads. However, even when considering shared services that provide common infrastructure to an organization, these services ultimately deliver functionality — whether directly to an end user or as a supporting component for other systems.
Organizing infrastructure around functionality better acknowledges the operational dependencies between components. For instance, consider the example of managing a storage account separately from virtual machines by placing them in different Terraform state files. While this approach might allow independent updates to the storage account and virtual machines, it also introduces silos between the teams managing each component. If these components work together to provide a specific functionality, separating their management creates unnecessary barriers.
From my perspective, if a storage account and virtual machines collectively contribute to a particular function, then maintaining their independence at the operational level is counterproductive. If the storage account fails, the fact that the virtual machines remain untouched is irrelevant — the functionality as a whole is broken. This perspective shapes how I design infrastructure, applications, and services.
As a result, I advocate for drawing boundaries around infrastructure components based on the functionality they deliver rather than purely technical distinctions. In Terraform, this means grouping the storage account, virtual machines, and associated monitoring alerts within a single Terraform state file. This approach recognizes the interdependence of these components and assigns responsibility to a single operator or team, ensuring the entire system remains healthy and operational.
That said, this doesn’t mean we should blindly lump everything together. Just as in application and service design, we should decompose functionality into smaller units to maintain control over individual components. This concept is familiar from microservices architecture. By refining the natural bounded contexts within our infrastructure, we can strike the right balance — minimizing operational overhead while respecting the structural boundaries of the organization and maintaining rational, well-defined contracts between systems.
Designing infrastructure in this way is as much an art as it is a science, much like microservice architecture. Unfortunately, many teams make poor design choices and later suffer the consequences. Rather than reflecting on the impact of their decisions, they often blame the methodology itself and revert to conventional, rigid design patterns without fully understanding the trade-offs.
This is an ongoing and evolving conversation — one that I find deeply interesting. Despite the thought I’ve put into it, I don’t claim to have all the answers. Every new system I design and every Terraform implementation teaches me something new. While working with the same cloud services repeatedly helps establish best practices, introducing new services with their own unique characteristics always presents new challenges. But that’s what makes it exciting! The ability to iterate, refine, and continuously improve infrastructure design is what keeps this work both engaging and rewarding.