Essential Terraform CLI commands, HCL syntax patterns, state management, modules, functions, and lifecycle rules. Bookmark this page for quick reference during your infrastructure-as-code workflows.
| Command | Description |
terraform init | Initialize working directory, download providers and modules |
terraform init -upgrade | Re-initialize and upgrade all providers to latest allowed versions |
terraform init -backend-config=FILE | Initialize with external backend configuration file |
terraform validate | Check configuration syntax and internal consistency |
terraform plan | Preview changes without applying (dry run) |
terraform plan -out=plan.tfplan | Save plan to file for exact apply later |
terraform plan -target=aws_instance.web | Plan changes for a specific resource only |
terraform plan -var="region=us-west-2" | Pass a variable value on the command line |
terraform apply | Apply changes to reach desired infrastructure state |
terraform apply plan.tfplan | Apply a previously saved plan file (no confirmation prompt) |
terraform apply -auto-approve | Apply without interactive confirmation |
terraform destroy | Destroy all managed infrastructure resources |
terraform destroy -target=aws_instance.web | Destroy a specific resource only |
terraform fmt | Format configuration files to canonical style |
terraform fmt -recursive | Format all .tf files in subdirectories recursively |
terraform show | Display current state or a saved plan in human-readable form |
terraform version | Print Terraform and provider versions |
terraform providers | List providers required by the configuration |
| Command | Description |
terraform state list | List all resources tracked in state |
terraform state show aws_instance.web | Show attributes of a single resource in state |
terraform state mv aws_instance.old aws_instance.new | Rename or move a resource in state (avoids destroy/recreate) |
terraform state rm aws_instance.web | Remove a resource from state without destroying it |
terraform state pull | Download and print current remote state as JSON |
terraform state push local.tfstate | Upload a local state file to the remote backend |
terraform state replace-provider hashicorp/aws registry.example.com/aws | Replace provider in state after registry migration |
terraform refresh | Reconcile state with real infrastructure (read-only update) |
| Command | Description |
terraform workspace list | List all workspaces (current marked with *) |
terraform workspace new staging | Create a new workspace named "staging" |
terraform workspace select production | Switch to the "production" workspace |
terraform workspace show | Print the name of the current workspace |
terraform workspace delete staging | Delete a workspace (must be empty and not current) |
terraform.workspace | Reference current workspace name in HCL config |
| Command | Description |
terraform import aws_instance.web i-0123456789 | Import existing resource into Terraform state |
terraform import module.vpc.aws_vpc.main vpc-abc123 | Import a resource inside a module |
terraform taint aws_instance.web | Mark resource for forced recreation on next apply |
terraform untaint aws_instance.web | Remove taint mark, prevent forced recreation |
terraform apply -replace=aws_instance.web | Force replacement of a resource (preferred over taint) |
| Command | Description |
terraform output | Display all output values from state |
terraform output instance_ip | Display a specific output value |
terraform output -json | Display all outputs in JSON format |
terraform output -raw instance_ip | Display output without quotes (useful for scripts) |
terraform console | Open interactive console to evaluate expressions |
terraform graph | dot -Tpng > graph.png | Generate visual dependency graph (requires Graphviz) |
| Pattern | Description |
variable "name" { type = string } | Declare an input variable with type constraint |
variable "name" { default = "value" } | Variable with a default value |
variable "name" { sensitive = true } | Mark variable to hide value from CLI output |
var.name | Reference a variable value |
output "name" { value = aws_instance.web.id } | Declare an output value |
locals { env = "prod" } | Define local values for reuse within a module |
local.env | Reference a local value |
data "aws_ami" "latest" { ... } | Declare a data source to read external information |
data.aws_ami.latest.id | Reference a data source attribute |
terraform { required_version = ">= 1.5" } | Constrain the required Terraform version |
terraform { required_providers { ... } } | Declare required provider versions |
| Pattern | Description |
resource "aws_instance" "web" { ... } | Declare a resource (type + local name) |
aws_instance.web.id | Reference a resource attribute |
aws_instance.web[0].id | Reference resource created with count by index |
aws_instance.web["key"].id | Reference resource created with for_each by key |
depends_on = [aws_vpc.main] | Explicit dependency between resources |
provisioner "local-exec" { command = "echo done" } | Run local command after resource creation |
provisioner "remote-exec" { inline = [...] } | Run commands on the remote resource via SSH |
connection { type = "ssh" ... } | Configure SSH connection for remote provisioners |
| Pattern | Description |
provider "aws" { region = "us-east-1" } | Configure the AWS provider with a region |
provider "aws" { alias = "west" } | Create an aliased provider for multi-region setups |
resource "aws_instance" "w" { provider = aws.west } | Use a specific provider alias in a resource |
provider "google" { project = "my-proj" } | Configure the Google Cloud provider |
provider "azurerm" { features {} } | Configure the Azure provider |
| Pattern | Description |
module "vpc" { source = "./modules/vpc" } | Use a local module from relative path |
module "vpc" { source = "terraform-aws-modules/vpc/aws" } | Use a module from the Terraform Registry |
module "vpc" { source = "git::https://example.com/vpc.git" } | Use a module from a Git repository |
module "vpc" { source = "..." version = "~> 3.0" } | Pin module version with constraint |
module.vpc.vpc_id | Reference a module output value |
terraform get | Download modules referenced in configuration |
terraform get -update | Update already-downloaded modules |
| Pattern | Description |
backend "s3" { bucket = "tf-state" key = "prod/terraform.tfstate" region = "us-east-1" } | Store state in AWS S3 |
backend "s3" { ... dynamodb_table = "tf-locks" } | Enable state locking with DynamoDB |
backend "azurerm" { storage_account_name = "tfstate" container_name = "state" } | Store state in Azure Blob Storage |
backend "gcs" { bucket = "tf-state" prefix = "prod" } | Store state in Google Cloud Storage |
backend "local" { path = "terraform.tfstate" } | Store state locally (default backend) |
terraform { cloud { organization = "my-org" } } | Use Terraform Cloud / HCP Terraform as backend |
terraform init -migrate-state | Migrate state when changing backend configuration |
| Function | Description |
lookup(map, key, default) | Look up a key in a map with fallback default value |
merge(map1, map2) | Merge two or more maps together (last wins) |
join(", ", list) | Join list elements into a single string with separator |
split(",", string) | Split a string into a list by delimiter |
format("Hello, %s!", var.name) | Format a string with printf-style verbs |
lower(string) / upper(string) | Convert string to lowercase or uppercase |
replace(string, search, replace) | Replace occurrences in a string |
length(list_or_map) | Return number of elements in a list or map |
flatten([list1, [list2]]) | Flatten nested lists into a single flat list |
toset(list) | Convert a list to a set (removes duplicates) |
try(expression, fallback) | Return first expression that does not error |
coalesce(val1, val2, ...) | Return first non-null, non-empty value |
cidrsubnet("10.0.0.0/16", 8, 1) | Calculate subnet CIDR within a network prefix |
file("path/to/file") | Read contents of a file as a string |
templatefile("tpl.tftpl", { name = "val" }) | Render a template file with variables |
jsonencode(value) / jsondecode(string) | Encode value as JSON / decode JSON string |
base64encode(string) / base64decode(string) | Base64 encode or decode a string |
keys(map) / values(map) | Return all keys or values from a map |
element(list, index) | Return element at index (wraps around) |
compact(list) | Remove empty strings from a list |
| Pattern | Description |
condition ? true_val : false_val | Ternary conditional expression |
count = var.create ? 1 : 0 | Conditionally create a resource using count |
count = 3 | Create 3 identical copies of a resource |
count.index | Current index (0-based) inside a count loop |
for_each = toset(["a", "b", "c"]) | Create one resource per item in a set |
for_each = var.instances | Create one resource per entry in a map |
each.key / each.value | Reference current key and value in for_each |
[for s in var.list : upper(s)] | Transform a list with a for expression |
{for k, v in var.map : k => upper(v)} | Transform a map with a for expression |
[for s in var.list : s if s != ""] | Filter a list with a for expression |
dynamic "ingress" { for_each = var.rules content { ... } } | Generate repeated nested blocks dynamically |
dynamic "ingress" { ... content { port = ingress.value } } | Reference iterator value inside dynamic block |
| Pattern | Description |
lifecycle { prevent_destroy = true } | Reject any plan that would destroy this resource |
lifecycle { ignore_changes = [tags] } | Ignore external changes to specific attributes |
lifecycle { ignore_changes = all } | Ignore all external attribute changes |
lifecycle { create_before_destroy = true } | Create replacement before destroying original (zero downtime) |
lifecycle { replace_triggered_by = [aws_ami.latest.id] } | Force replacement when referenced value changes |
lifecycle { precondition { condition = ... error_message = "..." } } | Validate assumption before creating resource |
lifecycle { postcondition { condition = ... error_message = "..." } } | Validate result after creating resource |
What is the difference between terraform plan and terraform apply?
terraform plan creates an execution plan showing what changes Terraform will make to your infrastructure without actually applying them. It is a safe dry run that lets you review additions, modifications, and deletions before committing. terraform apply executes those changes against your real infrastructure. Best practice is to always run plan first, review the output carefully, and then apply. You can save a plan with -out=plan.tfplan and apply that exact plan to ensure consistency.
How do I import existing infrastructure into Terraform state?
Use terraform import RESOURCE_TYPE.NAME RESOURCE_ID to bring existing infrastructure under Terraform management. First, write a matching resource block in your .tf file, then run the import command with the cloud provider's resource ID. For example: terraform import aws_instance.web i-0123456789abcdef0. After importing, run terraform plan to verify your configuration matches the actual resource and adjust any differences.
What is the purpose of Terraform workspaces?
Terraform workspaces let you manage multiple distinct sets of infrastructure from a single configuration directory. Each workspace maintains its own separate state file. This is commonly used for dev, staging, and production environments. Create a workspace with terraform workspace new staging, switch with terraform workspace select production, and reference the current workspace name in your config using terraform.workspace to vary settings per environment.
When should I use count vs for_each in Terraform?
count creates a given number of identical resources referenced by numeric index. for_each creates one resource per item in a map or set, referenced by key. Prefer for_each in most cases because removing an item from the middle of a list with count causes all subsequent resources to shift indexes and be recreated, while for_each only affects the specific item removed.
How do I prevent Terraform from destroying a critical resource?
Add a lifecycle block with prevent_destroy = true inside the resource definition. This makes Terraform reject any plan that would destroy that resource, including terraform destroy. To eventually remove it, first set prevent_destroy = false and apply, or use terraform state rm to remove the resource from state without destroying the actual infrastructure.
What is a Terraform backend and why should I use a remote one?
A Terraform backend determines where state data is stored and how operations are executed. The default local backend saves state to a file on disk. Remote backends like S3, Azure Blob Storage, or GCS store state centrally, enabling team collaboration, state locking to prevent concurrent modifications, versioning, and encryption at rest. Using a remote backend is essential for any team or production Terraform workflow.