TrademarkTrademark
Features
Documentation

Structuring Terraform and OpenTofu: Part 1/4

Platform engineer’s 4-part guide to structuring Terraform & OpenTofu: modules, envs, remote state, CI/CD, testing and governance, step by step.
Sebastian StadilMarch 6, 2026Updated March 31, 2026
Structuring Terraform and OpenTofu: Part 1/4
Key takeaways
  • Part 1 of this four-part guide covers why deliberate Terraform and OpenTofu code structure is mission-critical for maintainability, scalability, collaboration, onboarding, troubleshooting, and governance.
  • Standard configuration files include main.tf, variables.tf, outputs.tf, versions.tf, and terraform.tfvars, with sensitive tfvars typically excluded from version control.
  • Consistent naming uses underscores, singular and descriptive resource names, positive boolean flags, and units on numeric variables to keep code readable.
  • Group related resources into logically named files such as network.tf, compute.tf, storage.tf, and iam.tf rather than one file per resource.
  • Modules act like functions that encapsulate reusable infrastructure, providing reusability, abstraction, complexity management, consistency, and organization.

Part 1: Foundations of IaC Structure – The Why and What

This is the first part of a series on how to structure your Terraform and OpenTofu code. Getting started with Infrastructure as Code (IaC) is easy enough. The hard part is building a structure that still holds up once the codebase grows, more people touch it, and you have to find your way back into it months later. Part 1 covers the why behind a deliberate structure and the what: the basic files and conventions every IaC project sits on.

1. Introduction: Why IaC Structure is Mission Critical

How you organize your Terraform or OpenTofu code isn't just a matter of taste. The gap between a well-structured codebase and a messy one shows up the moment you need to:

  • Ensure Maintainability: A logical structure makes it easier to understand, update, and debug your infrastructure configurations. As your infrastructure grows, a clear organization prevents your codebase from becoming an unmanageable monolith.
  • Enhance Scalability: Well-defined structures allow your IaC to grow gracefully alongside your infrastructure. Whether you're adding new environments, regions, or services, a scalable structure accommodates this expansion without requiring heroic refactoring efforts.
  • Improve Team Collaboration: Clear conventions and organization enable multiple team members, and even different teams, to work on the same IaC codebase concurrently and efficiently. It reduces friction and the likelihood of conflicting changes.
  • Streamline Onboarding: New team members can get up to speed much faster when the codebase is intuitively organized and follows predictable patterns.
  • Simplify Troubleshooting: When issues arise (and they will), a well-structured codebase allows engineers to quickly pinpoint the relevant configurations, understand dependencies, and resolve problems faster.
  • Support Compliance and Governance: It's easier to implement and audit security policies, compliance controls, and organizational standards when your infrastructure code is consistently structured.

Without a deliberate approach to structure, even simple changes get risky and slow. You pile up technical debt and spend more time fighting the codebase than shipping with it. On any IaC deployment of real size, structure isn't optional.

2. The Building Blocks: Foundational Files and Naming Conventions

Every Terraform/OpenTofu project rests on a set of standard configuration files and a consistent approach to naming. Start here before anything else.

  • Standard Terraform/OpenTofu Files:
    • main.tf: Traditionally the primary entry point for your configuration, defining resources, modules, and data sources.
    • variables.tf: Contains declarations for all input variables used in the configuration. Defining types, descriptions, and default values here is crucial for clarity and usability.
    • outputs.tf: Defines the output values from your configuration, which can be used by other Terraform configurations, scripts, or for informational purposes.
    • versions.tf (or terraform.tf for older versions): Specifies provider requirements (e.g., AWS, Azure, Google Cloud) and their versions, as well as the required Terraform/OpenTofu version itself. This ensures consistent behavior across different environments and executions.
    • terraform.tfvars: Contains the specific values for the variables declared in variables.tf. This file is often used to customize a configuration for a particular environment (e.g., dev.tfvars, prod.tfvars) and should typically be excluded from version control if it contains sensitive information.
  • Best Practices for Naming Conventions: Consistency in naming is paramount for readability and maintainability.
    • Resources and Data Sources:
      • Use underscores (_) to separate words (e.g., aws_instance.web_server, google_compute_network.main_vpc).
      • Make resource names singular where appropriate (e.g., aws_s3_bucket.app_data not app_datas).
      • If a resource is the primary one of its type within a module or configuration, consider naming it main for simplicity (e.g., aws_vpc.main).
      • Be descriptive yet concise. The name should give a clear indication of the resource's purpose.
    • Variables:
      • Use descriptive names that reflect their purpose or usage (e.g., instance_count, database_admin_username).
      • Include units for numeric values where ambiguity might exist (e.g., ram_size_gb, disk_size_gb), as cloud provider APIs may not have standard units.
      • Use positive names for boolean flags (e.g., enable_monitoring instead of disable_monitoring) to avoid double negatives in logic.
    • Outputs:
      • Name outputs descriptively based on the value they provide (e.g., web_server_public_ip, database_endpoint_address).
    • Files:
      • Group related resources into logically named files (e.g., network.tf for networking resources, database.tf for database resources, security_groups.tf).
      • Avoid creating a separate file for every single resource, as this can lead to excessive fragmentation. Find a balance that enhances readability without clutter.
  • Logical Grouping of Resources into Files: While Terraform/OpenTofu processes all .tf files in a directory as a single configuration, organizing resources into different files based on their function or type significantly improves human readability and navigation. For example:
    • network.tf: VPCs, subnets, route tables, internet gateways.
    • compute.tf: Virtual machines, auto-scaling groups, launch configurations.
    • storage.tf: S3 buckets, EBS volumes, file shares.
    • iam.tf: IAM roles, policies, users.

3. The Building Blocks: Essential Modularity Principles (Initial Overview)

Modularity is what keeps a growing IaC codebase manageable. A Terraform/OpenTofu module is a container for a set of resources you use together to build a reusable component. Think of it like a function in regular programming. It wraps up logic, takes inputs (variables), and returns outputs.

  • What are Modules? A module is a canonical, reusable, and defined set of Terraform/OpenTofu configurations that manages a collection of related infrastructure resources. Modules can be sourced from various locations:
    • Local paths within your own project.
    • Public registries like the Terraform Registry.
    • Private module registries.
    • Git repositories.
  • Benefits of Using Modules:
    • Reusability: Define a common infrastructure pattern once (e.g., a standard web server setup, a VPC with specific security configurations) and reuse it multiple times across different environments or projects with varying inputs. This drastically reduces code duplication.
    • Abstraction: Modules hide the complexity of the underlying resource configurations. Consumers of a module only need to understand its inputs and outputs, not the intricate details of every resource it creates. This allows teams to work with higher-level concepts.
    • Complexity Management: Breaking down a large, complex infrastructure into smaller, manageable, and independently testable modules makes the overall system easier to understand, develop, and maintain.
    • Consistency: Enforce organizational standards and best practices by encapsulating them within modules. Every deployment using that module will adhere to the defined patterns.
    • Organization: Modules help in logically structuring your codebase, making it more navigable.
  • Core Principles for Designing Effective Modules (Initial Overview): While we'll cover module design in more depth in Part 2, some initial principles include:
    • Clear Purpose: A module should have a well-defined and focused responsibility.
    • Well-Defined Interface: Inputs (variables) and outputs should be clear, documented, and represent the configurable aspects and resultant values of the module.
    • Avoid Over-Complexity: Don't try to make a single module do too much.
    • Consider Reusability from the Start: Think about how the module might be used in different contexts.

With these basics in place, why structure matters, the standard files and naming conventions, and what modules buy you, you've got the foundation for Terraform and OpenTofu configurations that hold up as they grow.

Next in the Series (Part 2): Mastering Modules and Repository Strategies.

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.