Practical Linux, Windows Server and cloud guides for IT pros.

How to Structure Your Terraform Code: Step-by-Step Guide

This guide walks you through how to structure your terraform code, with the practical steps that matter and the bits most likely to catch you out along the way.

Filed under

, , ,

Published

Written by

Last updated

Turbogeek.co.uk - What is Terraform

TL;DR

  • Split by environment (dev/staging/prod) and by component (network, app, data) — never one giant root module.
  • Use modules for reusable abstractions; pass variables in, expose outputs, never hard-code state references.
  • State per environment, per component — small blast radius beats one big state file.
  • Pin provider and module versions explicitly. Floating versions = surprise breakage.

What is structuring Terraform code?

Terraform code structure is the layout decisions that make a repo of .tf files manageable as it grows past one root module. Get it right and changes stay local and predictable; get it wrong and every change has to think about the whole infrastructure at once.

The dominant pattern in 2026 is environment-per-directory + component-per-subdirectory — for example live/prod/network/, live/prod/app/, modules/network/, modules/app/. State files mirror that structure, so a change in app can never accidentally touch network.

Prerequisites

  • Terraform 1.5 or later installed.
  • A version-control repo where this code will live.
  • Decisions on backend (S3+DynamoDB, Terraform Cloud, GCS) and on naming convention before the first commit.

How to use this guide

The sections below walk through the practical commands and options. After the main content you’ll find a Verification block (sanity-check it actually worked), a Troubleshooting block (common error messages and what to do), and Related reading for follow-on topics.

Terraform represents a robust tool that automates codebase management by creating a private, version-controlled repository and configuring source control systems like Git to ensure synchronization. This article will elucidate the optimal approaches for setting up and utilizing Terraform in conjunction with Git.

By adhering to these guidelines, you will establish a folder structure that facilitates code management, rendering it more accessible while enhancing team productivity.

Why Structure Your Code?

Establishing a standardized structure for your code fosters cohesion, improves readability, and simplifies code creation. Commands remain consistent throughout your codebase. For instance, initializing a development repository can be accomplished using the same command each time:

terraform init --backend-conf=./config/dev/backend.conf
terraform init --backend-conf=./config/test/backend.conf
terraform init --backend-conf=./config/prod/backend.conf

It’s the same with var files.

terraform apply --var-file=./config/dev/vars.tfvars
terraform apply --var-file=./config/test/vars.tfvars
terraform apply --var-file=./config/prod/vars.tfvars

It makes everything much easier when using CI/CD tooling like Jenkins.

Tree Structure

Within this post, we will delve into the importance of maintaining a directory tree structure along with the associated best practices. Additionally, we will provide a helpful diagram illustrating the directory tree to illustrate our points effectively.

By employing a directory tree structure, you can easily locate and access the necessary files and folders. Furthermore, this structure facilitates efficient file and folder management. By adhering to best practices, you can establish a clean and organized directory tree.

.

├── .pre-commit-config.yaml
├── .terraform-docs.yml
├── .gitignore
├── jenkinsfile
├── README.md
├── bitbucket-pipelines.yml
└── tf
├── README.md
├── config
│   ├── dev
│   │   ├── backend.conf
│   │   └── vars.tfvars
│   ├── prod
│   │   ├── backend.conf
│   │   └── vars.tfvars
│   └── test
│   ├── backend.conf
│   └── vars.tfvars
├── main.tf
├── .terraform-version
├── .terraform.lock.hcl
├── outputs.tf
├── providers.tf
├── variables.tf
├── modules
   └── module-1
   └── README.md


Tree Structure Breakdown

.terraform-versionUsed by tfenv to set Terraform version automatically.
Jenkins picks up this file to select the version
.terraform.lock.hclUsed to lock versions of specific providers within Terraform; to upgrade this, use the Terraform init -upgrade command. More information available at Hashicorps Terraform Website
configper environment directory for the backend.conf & vars.tfvars
backend.confUsed by Jenkins to define specific terraform backends
vars.tfvarsenvironment-specific variable values
.pre-commit-config.yaml & .terraform-docs.yamlUsed for terraform documentation within README.md files
JenkinsfileUsed by Jenkins to specify a branch
Modules FolderAll code should be modularized, and the main.tf file should reference each module. This keeps the code clean and portable.
Outputs.tfOutput values make information about your infrastructure available on the command line and can expose information for other Terraform configurations to use. Output values are similar to return values in programming languages.

Module Versioning

When using Terraform, tracking which versions of your modules you are using is essential. This is especially important when upgrading or using modules in a collaborative environment.

“When using Terraform, the version of a module can be determined by the version number in the themodule’ss filename. For example, the terraform module” aws-ec” has a version number of”1.5.0″. “When using Terraform, the version of a module can be determined by the version number in the module’s filename. For example, the terraform module “aws-ec2” has a version number of “1.5.0”.

When using Terraform, tracking which versions of your modules you are using is important. This is especially important when upgrading or using modules in a collaborative environment.

v1.0.0 would be the first user-ready version of a module.

v1.0.0 to v1.0.1 would be for a bug fix

v1.0.0 to v1.2.0 would be for new features, with no breaking changes

v1.0.0 to v2.0.0 would be for new features, with breaking changes

A breaking change forces people using the module to make changes even if they’re not using the new features.

For Ansible Roles, versioning has to be in a semantic versioning format, which means it cannot start with a v like the above example.

Verification

Sanity-check the change actually worked:

  • terraform fmt -recursive exits clean — formatting is consistent.
  • terraform validate in each component directory exits clean.
  • terraform plan from one component shows no unexpected resources from other components — proves state isolation.

Troubleshooting

Module references break after restructuringterraform state mv moves resources within a state; the docs have the exact syntax for nested modules.

Provider version conflicts between environments — Pin provider versions in required_providers; use ~> 5.0 for minor-only floats.

Plan shows drift on every run — Usually means a parameter is being set outside Terraform (manual console change, autoscaling resizing). Either ignore via lifecycle or bring the change into Terraform.

Authoritative sources

References: Terraform style guide (official), Module development.

Related reading

One response to “How to Structure Your Terraform Code: Step-by-Step Guide”

  1. […] If you want to learn more about the core command lists above – check out this page on my site. […]

Leave a Reply

Your email address will not be published. Required fields are marked *

Find more on the site

Keep reading by topic.

If this post was useful, the fastest way to keep going is to pick the topic you work in most often.

Want another useful post?

Browse the latest posts, or support TurboGeek if the site saves you time regularly.

Translate »