A fast, practical reference for everyday Terraform work — installation, init → plan → apply workflow, variables & modules, state, workspaces, backends, and common HCL patterns.
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
terraform -install-autocomplete
terraform versioncurl -fsSL https://releases.hashicorp.com/terraform/ | head -n 20  # find latest version
curl -O https://releases.hashicorp.com/terraform/1.7.5/terraform_1.7.5_linux_amd64.zip
unzip terraform_1.7.5_linux_amd64.zip
sudo mv terraform /usr/local/bin/
terraform -install-autocomplete
terraform versiongit clone https://github.com/tfutils/tfenv.git ~/.tfenv
echo 'export PATH="$HOME/.tfenv/bin:$PATH"' >> ~/.bash_profile
source ~/.bash_profile
tfenv install 1.7.5
tfenv use 1.7.5project/
├─ main.tf          # root module (resources, data, providers)
├─ variables.tf     # input variables
├─ outputs.tf       # outputs
├─ providers.tf     # provider blocks
├─ versions.tf      # required_version, etc.
├─ terraform.tfvars # variable defaults (don’t commit secrets)
└─ modules/         # reusable child modules
Recommended .gitignore:
.terraform/
.terraform.lock.hcl
*.tfstate
*.tfstate.*
crash.log
*.tfvars
*.auto.tfvars
override.tf*
*_override.tf*
terraform init                    # first command in a new repo
terraform init -upgrade           # upgrade providers/modules
terraform init -backend-config=backend.hcl -reconfigure
terraform providers               # list required providers
terraform -help <command>         # built-in helpExample backend.hcl (AWS S3 + DynamoDB):
bucket         = "my-tf-state-bucket"
key            = "envs/dev/terraform.tfstate"
region         = "ap-south-1"
dynamodb_table = "tf-state-locks"
encrypt        = trueterraform fmt -recursive                 # format
terraform validate                       # validate HCL
terraform plan -out=plan.tfplan          # generate plan
terraform apply plan.tfplan              # apply saved plan
terraform apply [-auto-approve]          # direct apply
terraform destroy [-auto-approve]        # destroy infraTargeted & replace:
terraform plan -target=aws_s3_bucket.site
terraform apply -replace=aws_instance.webTroubleshooting:
TF_LOG=DEBUG TF_LOG_PATH=tf.log terraform apply
terraform console
terraform graph | dot -Tpng > graph.pngvariables.tf
variable "aws_region" { type = string, default = "ap-south-1" }
variable "tags" {
  type = map(string)
  default = { project="demo", owner="Atul" }
}terraform.tfvars
aws_region = "ap-south-1"
tags = { project="demo", owner="Atul" }CLI overrides:
terraform apply -var 'aws_region=us-east-1'
terraform apply -var-file=prod.tfvarsLocals:
locals {
  prefix = "${var.project}-${terraform.workspace}"
}Outputs:
output "website_url" {
  value = "http://${aws_s3_bucket.site.bucket_regional_domain_name}"
}Resource:
resource "aws_s3_bucket" "site" {
  bucket = "${local.prefix}-site"
  tags   = var.tags
}Data source:
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners = ["amazon"]
  filter {
    name   = "name"
    values = ["al2023-ami-*"]
  }
}Meta-args:
count, for_each, depends_on, lifecycleExample reusable VPC module:
module "vpc" {
  source = "./modules/vpc"
  cidr   = "10.0.0.0/16"
}Pull/update:
terraform get -update=trueterraform workspace new dev
terraform workspace select dev
terraform workspace list
terraform.workspace   # reference in codeInspect/move/import:
terraform show
terraform state list
terraform state show aws_s3_bucket.site
terraform state mv aws_iam_role.old module.iam.aws_iam_role.new
terraform state rm aws_s3_bucket.legacy
terraform state pull > terraform.tfstate
terraform state push terraform.tfstate
terraform state replace-provider hashicorp/aws registry.opentofu.org/awsImport existing:
terraform import aws_s3_bucket.imported my-existing-bucketterraform login
terraform logoutConfigure with cloud {} or backend "remote" {}.
main.tf:
resource "aws_s3_bucket" "site" {
  bucket = "tf-site-${terraform.workspace}"
}
resource "aws_s3_bucket_website_configuration" "site" {
  bucket = aws_s3_bucket.site.id
  index_document { suffix = "index.html" }
  error_document { key = "index.html" }
}
output "url" {
  value = "http://${aws_s3_bucket_website_configuration.site.website_endpoint}"
}Commands:
terraform init -backend-config=backend.hcl
terraform workspace new dev
terraform plan -out=plan.tfplan
terraform apply plan.tfplan
terraform output urlTaint & unlock:
terraform taint aws_instance.my_ec2
terraform untaint aws_instance.my_ec2
terraform force-unlock LOCK_IDConsole:
echo 'join(",",["foo","bar"])' | terraform consoleGraph:
terraform graph | dot -Tpng > graph.png- Pin provider versions & commit .terraform.lock.hcl.
- Never commit *.tfstateor secrets.
- Use for_eachinstead ofcountfor stable keys.
- Minimize use of -target.
- Use terraform plan -refresh-onlyfor drift detection.
- Limit concurrency with -parallelism=N.
terraform version
terraform init [-backend-config=FILE] [-upgrade] [-reconfigure]
terraform fmt -recursive
terraform validate
terraform plan [-out=plan.tfplan] [-target=ADDR] [-replace=ADDR] [-refresh-only]
terraform apply [plan.tfplan] [-auto-approve]
terraform destroy [-target=ADDR] [-auto-approve]
terraform output [-raw NAME] [-json]
terraform providers
terraform console
terraform graph | dot -Tpng > graph.png
terraform workspace list|new|select|show
terraform state list|show|mv|rm|pull|push|replace-provider
terraform import <ADDR> <ID>
terraform taint|untaint <ADDR>
terraform force-unlock <LOCK_ID>