Stage 4 · Provision
Terraform State Operations
Workspace & Stack Boundaries
Workspaces, directory layouts, state data sources, and minimizing cross-stack coupling — designing state boundaries.
State Boundaries
State boundaries determine what resources are managed together. The right boundaries minimize blast radius while keeping related resources in the same state. Wrong boundaries create fragile cross-state dependencies.
| Boundary | Blast Radius | Coupling | Complexity |
|---|---|---|---|
| All resources in one state | Maximum | None | Low |
| Per-environment | Per environment | Cross-env references | Low |
| Per-component | Per component | Cross-component references | Medium |
| Per-team | Per team | Cross-team references | Medium |
| Per-service | Per service | Shared infrastructure | High |
Workspace Models
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "networking/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
locals {
env = terraform.workspace
config = {
dev = {
instance_type = "t3.micro"
min_count = 1
}
prod = {
instance_type = "m5.large"
min_count = 3
}
}
}Workspaces use the same backend with different state files. The key parameter can include workspace-specific paths. The locals block maps workspace names to environment-specific values.
Directory Layouts
infrastructure/
networking/
vpc/
main.tf
backend.tf
outputs.tf
subnets/
main.tf
backend.tf
compute/
eks/
main.tf
backend.tf
database/
rds/
main.tf
backend.tf
shared/
dns/
main.tf
backend.tf
iam/
main.tf
backend.tfEach directory is a separate root module with its own backend. Changes to networking do not affect compute. The shared directory holds resources used by multiple components.
Cross-Stack Data
# In compute/eks/main.tf
data "terraform_remote_state" "networking" {
backend = "s3"
config = {
bucket = "my-terraform-state"
key = "networking/vpc/terraform.tfstate"
region = "us-east-1"
}
}
module "eks" {
source = "../../modules/eks"
vpc_id = data.terraform_remote_state.networking.outputs.vpc_id
subnet_ids = data.terraform_remote_state.networking.outputs.private_subnet_ids
}terraform_remote_state reads outputs from another state file. The dependency is explicit. Changes to the networking state may affect the EKS state. This is the primary mechanism for cross-state data sharing.
# Look up the VPC by tag instead of remote state
data "aws_vpc" "existing" {
tags = {
Name = "production-vpc"
}
}
data "aws_subnets" "private" {
filter {
name = "vpc-id"
values = [data.aws_vpc.existing.id]
}
tags = {
Tier = "private"
}
}
module "eks" {
source = "../../modules/eks"
vpc_id = data.aws_vpc.existing.id
subnet_ids = data.aws_subnets.private.ids
}Data sources query AWS directly instead of reading from another state file. This reduces cross-state coupling. The downside is less explicit — changes to the VPC name or tags break the lookup.
Minimizing Coupling
- Use data sources instead of terraform_remote_state when possible.
- Export only necessary values — not entire resource objects.
- Use consistent naming conventions for cross-state lookups.
- Document cross-state dependencies explicitly.
- Consider a shared state for common resources (DNS, IAM).
Blast Radius Management
# Root module delegates to child modules
module "networking" {
source = "./modules/networking"
cidr = "10.0.0.0/16"
}
module "compute" {
source = "./modules/compute"
vpc_id = module.networking.vpc_id
}
# Each module can have its own state file
# if split into separate directoriesModules help organize code. To reduce blast radius, split modules into separate directories with separate state files. This way, a networking change does not affect the compute state.
Begin with one state file per environment. Split into per-component state when the state file becomes too large or when team autonomy requires it. Over-splitting adds complexity without benefit.
terraform_remote_state creates implicit dependencies between state files. If the source state changes its output structure, the consuming state breaks. Always version your outputs and use data sources when possible.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.