Clicking through a console works for the first server and fails for the tenth. Infrastructure as code (IaC) turns your environment into reviewed, versioned files so you can rebuild it, compare changes and answer the question 'what changed?' with a pull request.
A layout that scales
infra/
modules/
network/ # VPC, subnets, routing
app/ # compute, load balancer, IAM
envs/
staging/
main.tf # calls the modules with staging values
production/
main.tf # same modules, production valuesModules hold the reusable pattern, and each environment is a thin file that passes in sizes, names and settings. Staging and production then differ only in values, not in structure.
Remote state with locking
Never keep state on a laptop. Store it in a private, versioned, encrypted bucket with locking so two people cannot apply at once. Recent Terraform versions can lock directly in S3; older ones use a DynamoDB table.
terraform {
required_version = ">= 1.10"
backend "s3" {
bucket = "example-tfstate"
key = "production/network.tfstate"
region = "us-east-1"
encrypt = true
use_lockfile = true
}
}Working rules
- Keep state files small: split by layer (network, data, apps) so a mistake has a small blast radius.
- Pin Terraform and provider versions and commit the lock file.
- Run plan on every pull request and apply only from the pipeline, never from a laptop.
- Keep secrets out of variables and state where possible. Reference AWS Secrets Manager or SSM Parameter Store instead.
- Tag every resource with owner, environment and cost centre.
- Schedule a plan that reports drift, so manual console changes do not hide.
