TrademarkTrademark
Features
Documentation

Structuring Terraform and OpenTofu: Part 4/4

Part 4 of a Platform Engineer’s guide details structuring Terraform & OpenTofu repos for scalable environments, policy-driven governance and efficient CI/CD.
Sebastian StadilMarch 6, 2026Updated March 31, 2026
Structuring Terraform and OpenTofu: Part 4/4
Key takeaways
  • As organizations grow, IaC structures must adapt to more environments, teams, and complexity through iterative refactoring rather than big-bang rewrites.
  • Mature IaC composes higher-level stacks from focused modules and manages cross-stack dependencies using terraform_remote_state data sources and output chaining.
  • Terragrunt is an open-source wrapper that keeps Terraform configurations DRY and orchestrates dependencies and runs across many modules in the correct order.
  • Balancing team autonomy with central governance relies on golden-path modules and policy as code via tools like Open Policy Agent enforced in CI/CD pipelines.
  • Measure IaC success with DORA-style metrics such as lead time for changes, deployment frequency, change failure rate, and mean time to recovery, refined through feedback loops.

Part 4: Scaling Structures and Advanced IaC Patterns

This is the last part of the series. Part 1 covered the "why" and "what" of structuring Terraform and OpenTofu. Part 2 dealt with modules and repository strategies, and Part 3 with code organization and environment strategies. Part 4 picks up where most teams start to feel real strain: scaling the structure as the organization and its infrastructure grow. We'll look at the advanced patterns that handle that growth, where orchestration tools fit in, and how to keep refining the setup over time.

1. Scaling Your Structure: From Startup Roots to Enterprise Branches

A structure that worked fine for a small team and a modest amount of infrastructure starts to strain as the organization grows. Seeing the pressure coming and adjusting ahead of it is what keeps you in control instead of reacting after the fact.

  • How IaC Structuring Needs Evolve with Growth:
    • Increased Number of Environments: Beyond dev/staging/prod, you might need QA, UAT, performance testing, or ephemeral environments for feature branches.
    • More Teams and Contributors: Multiple teams (platform, application, security, networking) may contribute to or consume IaC, requiring clearer boundaries, ownership, and collaboration models.
    • Greater Infrastructure Complexity: Managing more diverse services, interdependencies, and potentially multiple cloud providers or regions.
    • Demand for Self-Service: Application teams may want more autonomy to provision their own infrastructure within centrally defined guardrails.
    • Stricter Governance and Compliance: As the organization matures, so do the requirements for security, compliance auditing, and cost management.
  • Strategies for Refactoring and Adapting Existing Structures:
    • Iterative Refinement: Treat your IaC structure as living code. Don't be afraid to refactor iteratively as pain points emerge. Avoid "big bang" refactoring if possible.
    • Module Decomposition/Composition: Break down overly large or complex modules into smaller, more focused ones. Conversely, compose new, higher-level service modules from existing foundational modules.
    • Introduce Abstraction Layers: As complexity grows, you might introduce layers of abstraction (e.g., platform-level modules consumed by application-level configurations) to simplify things for different user groups.
    • Adopt or Refine Repository Strategy: What worked initially (e.g., a simple monorepo) might need to evolve. Consider moving shared modules to dedicated repositories or restructuring your monorepo for better CI performance and clarity.
  • Managing Shared Services and Platform Components:
    • Dedicated Configurations/States: Shared services (e.g., core networking, identity management, Kubernetes clusters, artifact registries) should typically be managed as separate Terraform configurations with their own state files.
    • Clear Interfaces (Outputs): These shared service configurations should expose well-defined outputs that other application or service configurations can consume (e.g., VPC IDs, cluster API endpoints, private DNS zone names).
    • Versioning and Lifecycle Management: Manage shared components like internal products, with clear versioning and communication for updates or breaking changes.
  • Team Autonomy vs. Central Governance at Scale:
    • Platform as a Product: The platform team can provide a set of versioned, validated modules and patterns (a "golden path") for application teams to consume.
    • Policy as Code (PaC): Implement tools like Open Policy Agent (OPA) to enforce organizational standards, security best practices, and cost controls automatically within CI/CD pipelines, allowing teams more autonomy within those guardrails.
    • Tiered Ownership: Define clear ownership for different layers of infrastructure (e.g., platform team owns core network, app teams own their application-specific resources within that network).

2. Advanced Patterns: Composition, Dependencies, and Orchestration

Once your infrastructure spans many configurations and teams, you need better ways to manage how the pieces connect. The patterns below handle composition, dependencies between stacks, and running changes across many modules at once.

  • Composition: Building Larger Solutions from Smaller Modules:
    • This is the essence of a mature modular approach. Instead of monolithic configurations, you compose higher-level infrastructure "stacks" or "services" by assembling multiple, focused modules.
    • For example, an "application stack" module might internally call separate modules for compute instances, a load balancer, a database, and DNS records, orchestrating their creation and wiring them together.
  • Managing Dependencies:
    • Implicit Dependencies: Terraform automatically infers dependencies based on resource interpolations (e.g., an EC2 instance depending on a subnet ID).
    • Explicit Dependencies (depends_on): Use depends_on sparingly when Terraform cannot automatically infer an order (e.g., for non-Terraform managed resources or specific timing issues). Overuse can slow down plans and make configurations harder to understand.
    • Data Sources for Cross-Stack Dependencies: When one Terraform configuration (and state file) needs information from another (e.g., an application stack needing the VPC ID from a separately managed network stack), use data sources like terraform_remote_state to read outputs from the other state file. This creates a loosely coupled dependency.
    • Output Chaining: One root module applies and its outputs are fed as inputs into another root module. This is often managed by an overarching CI/CD pipeline or an orchestration tool.
  • Introduction to Orchestration Tools: The Role of Terragrunt Once you're managing many Terraform configurations, especially with a separate state file per environment or component, a meta-tool or orchestration layer really starts to pay off. Terragrunt is a popular open-source wrapper for Terraform that helps keep your configurations DRY (Don't Repeat Yourself) and manage remote state and dependencies more systematically.
    • Keeping Configurations DRY: Terragrunt allows you to define common configurations (like backend settings, provider versions, common input variables) once in a parent terragrunt.hcl file and inherit them into child configurations. This significantly reduces boilerplate.
    • Remote State Configuration: Terragrunt can automatically configure your remote state backend based on the directory structure or other conventions, ensuring consistency.
    • Inter-Module/Stack Dependencies: Terragrunt can manage dependencies between different Terraform configurations (each with its own state). You can define that one Terragrunt configuration depends on the outputs of another, and Terragrunt will ensure they are applied in the correct order using its dependency blocks and run-all commands.
    • Executing Commands Across Multiple Modules: Commands like terragrunt run-all plan or terragrunt run-all apply can execute Terraform across multiple modules/configurations in the correct dependency order.

3. Measuring Success and Continuous Improvement

A good IaC structure is something you keep refining, not something you set up once and leave alone. The question is how you tell whether it's working and where to look when it isn't.

  • Key Metrics or Indicators of a Well-Structured IaC Setup:
    • Lead Time for Changes: the time it takes to safely get an infrastructure change into production.
    • Deployment Frequency: how often the team ships infrastructure updates.
    • Change Failure Rate: the share of deployments that fail or need a rollback.
    • Mean Time to Recovery (MTTR): how long recovery takes after a failed deployment or an infrastructure incident.
    • Code Reusability: Are common patterns effectively captured in modules and reused?
    • Onboarding Time for New Engineers: How quickly can new team members become productive with the IaC codebase?
    • Developer Satisfaction: Do engineers find the IaC easy to work with, or is it a source of friction?
  • Establishing Feedback Loops for Refinement:
    • Regular Code Reviews: Enforce peer reviews for all IaC changes, focusing not just on correctness but also on structure and adherence to conventions.
    • Retrospectives: In team retrospectives, surface where the IaC structure is helping and where it's causing friction, and act on the recurring complaints.
    • Automated Testing and Linting: Implement CI checks for formatting (terraform fmt), validation (terraform validate), static analysis (e.g., tfsec, Checkov), and potentially plan checks against policies (OPA).
    • Monitor CI/CD Pipeline Performance: Slow or unreliable IaC pipelines can indicate structural issues or overly complex dependency chains.
    • Stay Updated: The IaC ecosystem (Terraform, OpenTofu, providers, tooling) evolves. Regularly evaluate new features or tools that could improve your structure or workflows.

Structuring Terraform and OpenTofu well is never really finished. Your modules, your repository layout, and your code organization all keep shifting as the team and the infrastructure grow, and that's fine. Make deliberate choices, revisit them when they start to hurt, and you'll end up with an IaC platform your engineers can actually move quickly on.

About the author
Sebastian StadilCEO at Scalr
Sebastian Stadil is the CEO of Scalr with 15+ years of DevOps experience. He started with AWS in 2004 and advised early Microsoft Azure and Google Cloud.