Snippets target HCL2 with Terraform 1.7+. OpenTofu is a drop-in CLI (tofu)
and an open-source fork of Terraform under the Linux Foundation. Pin both
required_version and provider versions in every config
— no drift between dev and prod.
install · daily-loopSetup
bash
# Install
brew tap hashicorp/tap && brew install hashicorp/tap/terraform
# Alternative: open-source fork
brew install opentofu # tofu is a drop-in for `terraform`
# New project
mkdir infra && cd infra
terraform fmt -recursive
terraform init # downloads providers + initialises backend
terraform validate
terraform plan -out=tfplan
terraform apply tfplan
terraform destroy
# Inspect state
terraform state list
terraform state show aws_s3_bucket.assets
terraform output -json
# Auth (cloud)
export AWS_PROFILE=prod
export ARM_USE_CLI=true
gcloud auth application-default login
the languageHCL essentials
resource "type" "name" { … }
Declares a resource of a provider type.
data "type" "name" { … }
Read-only lookup.
variable "x" { type = string default = … }
Input variable.
output "x" { value = … }
Output value.
locals { name = "app-${var.env}" }
Module-local computed values.
module "x" { source = "./modules/x" }
Composition. Use a registry source for shared modules.
terraform { … }
Top-level settings: version, providers, backend.
type constraints: string, number, bool, list, set, map, object, tuple, any
Spell out variable shapes.
${expr} interpolation
In strings: "name-${var.env}". Outside strings, drop the wrapper.
heredoc: <<-EOT … EOT
Multi-line strings.
where resources come fromProviders
required_providers { aws = { source, version } }
Pin source + version constraint.
version = "~> 5.60"
Pessimistic constraint: 5.60.x.
version = ">= 5, < 6"
Range constraint.
provider "aws" { region = … }
Provider config block.
provider "aws" { alias = "us_west" region = "us-west-2" }
Named alias for multi-region.
resource "…" { provider = aws.us_west }
Point a resource at an aliased provider.
default_tags { tags = { … } }
Apply to every taggable resource.
.terraform.lock.hcl
Lock file. Commit it. Pins exact provider versions.
A KMS key + a map of S3 buckets each with server-side encryption, tagged via provider defaults, names
keyed by env. The pattern most teams converge on.
Save the plan, apply the plan.terraform plan -out=tfplan → review → apply tfplan.
Avoids the “plan moved while I was reviewing” race.
Use for_each, not count, when you can.
Removing an item from the middle of a count list shifts every later
resource’s address — destroying and re-creating things you didn’t mean to touch.
Pin everything.required_version, every provider version, every module version. Commit
.terraform.lock.hcl. Floating versions = surprise rollouts.
Common trapsWatch out for
State has secrets, even if your config doesn’t.
Random passwords, generated tokens, RDS connection strings — all in state. Encrypt the backend; lock
down read access; rotate exposed credentials immediately.
-target lies about the rest of the world.
Useful as an emergency tool, dangerous as a habit. Drift accumulates outside the targeted set; your next
full plan will rediscover surprises.
External changes get reverted on apply.
Manual fixes in the console get overwritten next apply. Either import the
change back into HCL, or use lifecycle.ignore_changes.
Terraform is an open-source infrastructure-as-code (IaC) tool by HashiCorp. You declare cloud resources (VMs, databases, networks, DNS, etc.) in HCL configuration files, and Terraform plans and applies the changes across any provider — AWS, GCP, Azure, Kubernetes, and hundreds more. It keeps a state file to track real-world infrastructure and produce diffs.
What is Terraform state and why does it matter?
State is a JSON file (terraform.tfstate) that maps your configuration to real infrastructure. Terraform uses it to compute diffs, prevent duplicate resource creation, and track dependencies. For team use, always store state in a remote backend (S3, GCS, Terraform Cloud, or Azure Blob) with locking enabled so concurrent runs don't corrupt it.
What is the difference between terraform plan and terraform apply?
terraform plan performs a dry run — it shows what changes Terraform would make (create, update, destroy) without modifying anything. terraform apply executes those changes. Always review the plan output before applying, especially for destroy actions. Use terraform plan -out=tfplan then terraform apply tfplan to guarantee the applied plan matches what you reviewed.
What are Terraform modules and when should I use them?
A module is a reusable collection of resources grouped in a directory. Use modules to encapsulate repeatable patterns — a VPC, a Kubernetes node pool, a serverless function — so you can instantiate them with different variables instead of copying code. The Terraform Registry hosts community modules; for teams, use private module registries or monorepo modules.
What is the difference between Terraform and OpenTofu?
OpenTofu is a community fork of Terraform 1.5 created after HashiCorp changed the Terraform licence to BUSL (non-OSI) in August 2023. OpenTofu remains MPL-licensed (fully open source) and is maintained by the Linux Foundation. The two are largely compatible at the HCL level, though they are diverging with new features.
Is Terraform free?
The Terraform CLI is free under the BUSL licence for most use cases (commercial use is restricted for managed service providers). OpenTofu is MPL-2.0 and fully free. Terraform Cloud has a free tier for individuals; team and business plans are paid. For most individual or team IaC use, the free CLI plus a remote state backend is sufficient.