Terraform
Staging was created through the cloud console and has been working for months. Production now needs the same setup, but nobody remembers every firewall rule and storage setting. Repeating the clicks produces an environment that is almost the same, with the differences discovered during deployment.
Terraform lets the team put those settings in configuration files and review changes before applying them. HashiCorp released it in July 2014; co-creator Mitchell Hashimoto wanted infrastructure as code that could work across providers, rather than being tied to one cloud's provisioning system.
Providers translate resource definitions into service API calls. References between resources form a dependency graph, so Terraform can determine what must exist before something else can be created. Inside a project with an AWS provider and credentials configured, a bucket definition might look like this:
variable "bucket_name" {
type = string
}
resource "aws_s3_bucket" "uploads" {
bucket = var.bucket_name
}
output "uploads_arn" {
value = aws_s3_bucket.uploads.arn
}aws_s3_bucket.uploads is the resource's address in the configuration. Its arn comes from the provider's result. Supply a globally unique bucket name, for example through a local terraform.tfvars file; applying this definition creates a real cloud resource.
Terraform also needs to remember which actual bucket belongs to that address. State holds that binding. A plan compares the configuration with state and refreshed remote observations, then proposes the actions needed to reach the declared setup.
From the intended project and backend, initialize and inspect that plan:
terraform init # Initialize backend and providers
terraform fmt -check -recursive
terraform validate
terraform workspace show
terraform plan -lock-timeout=60s -out=reviewed.tfplan
terraform show reviewed.tfplanA setting change may replace an object rather than update it, so read the proposed actions. Commit configuration and the dependency lockfile; keep state, saved plans, and variable files containing secrets out of source control. Check the provider's account as well as the workspace name.
After review, apply the saved plan and inspect the resulting binding:
terraform apply reviewed.tfplan # Executes without another approval prompt
terraform output uploads_arn
terraform state list
terraform state show aws_s3_bucket.uploadsApplying a saved plan executes its actions without another approval prompt. A partial failure can leave some resources changed and others untouched, so inspect the result before retrying. Shared runs need a remote backend with supported locking to coordinate their state updates.
Later, somebody might change a setting in the console during an incident. Another plan can expose the resulting drift without applying changes:
terraform plan -detailed-exitcode # 0: no changes; 2: changes; 1: errorFor the bucket that already existed before Terraform was introduced, declare its intended resource and add an import block using the real bucket name:
import {
to = aws_s3_bucket.uploads
id = "your-existing-bucket-name"
}Review the resulting plan before applying it. Each remote object should have one active state owner; importing it into competing states gives two configurations control over the same object.
Teams use this workflow for networks, databases, and service identities, then share reusable modules with different inputs for each environment. The plan makes the differences visible before the next production environment is built.
Terraform and state follows ownership, locking, and interrupted-run recovery further.