Skip to main content
Hey everyone — welcome back. This lesson explains how to structure Terraform configurations for clarity, reuse, and safe collaboration. Whether you work with a single .tf file or a multi-directory layout for multiple environments, organizing files consistently will make day-to-day operations and long-term maintenance much easier. Below we walk through Terraform block types, recommended filenames, state handling, environment separation patterns, and best practices for secrets and version control. When authoring Terraform, you declare different block types inside .tf files to describe the infrastructure you want to deploy. Application or infrastructure teams state requirements (for example: an S3 bucket, a VM, or a Kubernetes cluster), and Terraform converts those requirements into configuration blocks. Common Terraform block types:
  • variable — declare inputs to the configuration.
  • resource — describe real-world infrastructure objects to create and manage.
  • output — expose values after deployment (for use by people, other configurations, or orchestration).
  • provider — configure the provider plugins (e.g., AWS, GCP, Azure, Kubernetes).
Terraform loads any file with the .tf extension in the working directory when you run terraform plan, terraform apply, or other commands, and it merges them into a single configuration at runtime.
The image is a diagram showing infrastructure requirements and their corresponding configuration in a Terraform configuration file. It includes elements like an S3 bucket, virtual machine, and Kubernetes cluster mapped to various Terraform constructs such as variables and resources.
Why split files?
  • A single large .tf file becomes brittle and hard to review.
  • Logical separation improves readability, team ownership, and reuse.
  • Terraform treats all .tf files in the directory as one configuration, so cross-file references (for example, referencing a resource defined in another file) work automatically.
A typical strategy is to group related blocks in separate files by concern:
  • variables.tf — input variable declarations
  • network.tf — network-related resources (VPCs, subnets, firewalls)
  • kubernetes.tf — Kubernetes resources or provider configuration for clusters
  • outputs.tf — outputs to expose information after apply
Because Terraform merges files in the working directory, you don’t need special imports or wiring to reference a resource defined in a different file.
The image illustrates how Terraform processes files in a working directory, detailing a sequence involving variables, resources, and outputs.
Recommended, practical file layout
  • Using conventional filenames increases predictability across teams.
  • Below is a concise reference you can adopt as a starting point.
FilePurposeExample / Notes
main.tfPrimary configuration: networks, clusters, compute resourcesPut core resource and module blocks here
variables.tfDeclare input variablesUse variable "env" {} declarations
outputs.tfExpose outputs after applyUse output "bucket_name" {} blocks
providers.tfProvider configuration and provider blocksExamples: provider "aws" { region = "us-east-1" }
terraform.tfvarsAssign values to variables (auto-loaded)Use for non-sensitive environment defaults; Terraform loads this automatically if present
Notes on using terraform.tfvars and per-environment tfvars:
  • You can create environment-specific files like prod.tfvars and pass them with -var-file=prod.tfvars.
  • Do not store secrets in plaintext in .tfvars files that are committed to version control.
The image lists common Terraform files with their purposes: main.tf for infrastructure components, variables.tf for variable definitions, outputs.tf for output blocks, providers.tf for provider configurations, and terraform.tfvars for variable values.
Terraform runtime and state files Terraform also creates and updates several files to track its operations. These are not part of your source configuration and generally should not be manually edited:
  • terraform.tfstate — the current state file recording managed infrastructure.
  • terraform.tfstate.backup — prior state backup created before updates.
  • .terraform.lock.hcl — dependency lock file for provider versions.
  • .terraform/ — directory storing downloaded provider plugins and modules.
Because state often contains resource IDs, IP addresses, and sometimes sensitive attributes, treat state as sensitive data.
The image describes two Terraform files: terraform.tfstate, which stores the state, and terraform.tfstate.backup, which is a backup of the previous state file. It also shows a directory listing with various Terraform-related files.
Always add terraform.tfstate, terraform.tfstate.backup, .terraform/, and .terraform.lock.hcl to your repository’s .gitignore. This prevents accidentally committing sensitive or environment-specific data into version control.
Do not store secrets (API keys, passwords, private keys) in plaintext inside .tfvars or Terraform files. Use environment variables, a secret manager (e.g., AWS Secrets Manager, HashiCorp Vault), or remote backends with proper access controls instead.
Example .gitignore snippet
Working at scale: environments, projects, and modules
  • Most organizations manage multiple environments (for example: dev, staging, prod) and several projects or components.
  • Rather than a single monolithic working directory, create a top-level terraform/ folder and separate environments and projects into subdirectories.
Common directory pattern:
  • repo/terraform/<project>/prod/ — contains main.tf, variables.tf, outputs.tf, and its own state.
  • repo/terraform/<project>/dev/ — separate working directory and state for development.
  • Each subdirectory is an independent Terraform working directory with its own backend configuration and lifecycle.
Benefits:
  • Reduced blast radius: operations in dev cannot accidentally modify prod because they use separate state files (local or remote).
  • Clear boundaries make it easier to adopt reusable modules: extract repeated infrastructure into modules/ and call them from environment working directories.
When using remote backends (recommended for collaboration), configure the backend per working directory to store state remotely (for example: S3 with DynamoDB for locking, or Terraform Cloud). Remote state helps with team collaboration, locking, and recovery.
The image illustrates a Git-based workflow for HashiCorp Terraform, showing push and pull actions with a repository leading to various environment directories, each containing Terraform files like main.tf, variables.tf, and outputs.tf.
Wrapping up — best practices checklist
  • Use multiple .tf files in a single working directory to organize by concern: variables, resources, outputs, and providers.
  • Adopt conventional filenames (main.tf, variables.tf, outputs.tf, providers.tf, terraform.tfvars) for consistency across teams.
  • Keep each environment or project in its own working directory with separate state to minimize risks.
  • Protect state files and lock files via .gitignore and prefer remote backends (S3, Azure Storage, Google Cloud Storage, or Terraform Cloud) for collaboration and state locking.
  • Avoid committing secrets; use environment variables, secret managers, or secure backend integrations.
Further reading and references

Watch Video