Modules · wrap-up
Cheat sheet & self-check
Every command from this section on one page.
The cycle
terraform init | Download providers and modules, set up the backend |
terraform fmt -recursive | Format all files |
terraform validate | Check syntax and references |
terraform plan | Show what would change |
terraform apply | Make the changes (after confirmation) |
terraform destroy | Remove everything this configuration manages |
Look around
terraform show | Current state in readable form |
terraform output | Output values |
terraform console | Try expressions interactively |
Inspect state
terraform state list | Every resource address in state |
terraform state show 'aws_s3_bucket.logs' | Recorded attributes of one resource |
terraform plan -refresh-only | Show how reality differs from state, without proposing changes |
Change state (prefer code blocks)
import { to = aws_s3_bucket.logs id = "acme-logs" } | Adopt an existing resource (Terraform 1.5+) |
moved { from = aws_s3_bucket.old to = aws_s3_bucket.logs } | Rename or move without destroying |
removed { from = aws_s3_bucket.logs lifecycle { destroy = false } } | Stop managing a resource without deleting it (1.7+) |
terraform plan -generate-config-out=generated.tf | Generate config for import blocks |
Setting values (lowest → highest precedence)
default in the variable block | Fallback |
TF_VAR_name environment variable | Environment |
terraform.tfvars / *.auto.tfvars | Loaded automatically |
-var-file=prod.tfvars / -var name=value | Command line, in the order given |
Expressions
var.name, local.name, module.x.output | References |
for_each = toset(var.names) | One resource per item |
{ for k, v in var.map : k => upper(v) } | Transform a map |
coalesce(var.a, "default") | First non-null/non-empty value |
Using modules
source = "./modules/bucket" | A local module |
source = "git::https://github.com/acme/tf-modules.git//bucket?ref=v1.4.0" | A module from Git, pinned to a tag |
source = "terraform-aws-modules/vpc/aws" version = "~> 5.0" | A registry module with a version constraint |
terraform get -update | Refresh downloaded modules |
Module layout
main.tf variables.tf outputs.tf versions.tf README.md | Conventional files |
examples/ tests/ | Usage examples and tests |
Bootstrap options (best first)
Pre-built image (Packer, image pipeline) | Everything baked in; fastest, most reproducible boots |
user_data = templatefile("cloud-init.yaml", {…}) | First-boot configuration via cloud-init |
Configuration management (Ansible…) | Ongoing configuration of long-lived hosts |
provisioner "remote-exec" / "local-exec" | Last resort: imperative steps Terraform can't track |
Useful pieces
terraform_data with triggers_replace | Re-run something when inputs change (replaces null_resource) |
cloud-init status --wait | On the machine: wait for cloud-init to finish |
sudo cat /var/log/cloud-init-output.log | On the machine: what cloud-init did |
Host setup (Ubuntu)
sudo apt install -y qemu-kvm libvirt-daemon-system genisoimage | KVM, libvirt, and a tool to build cloud-init ISOs |
sudo usermod -aG libvirt $USER | Allow your user to manage VMs (log in again) |
virsh list --all | VMs defined on this host |
virsh net-dhcp-leases default | IP addresses handed out to VMs |
Troubleshooting
virsh console node-0 | Serial console (Ctrl+] to exit) |
virsh domblklist node-0 | Disks attached to a VM |
sudo virsh pool-list --all | Storage pools (the provider needs one, usually 'default') |
Quality gates
terraform fmt -check -recursive && terraform validate | Formatting and syntax |
tflint --recursive | Lint with provider-aware rules |
checkov -d . / trivy config . | Security misconfiguration scanning |
terraform test | Run .tftest.hcl tests (Terraform 1.6+) |
Policy on plans
terraform plan -out=tfplan && terraform show -json tfplan > plan.json | Machine-readable plan |
conftest test plan.json -p policy/ | Evaluate OPA/Rego policies against the plan |
terraform plan -detailed-exitcode | Drift check: exit 2 = changes |