# Terraform and OpenTofu Cheat Sheet: Commands and HCL Syntax

> A quick reference of the most used Terraform and OpenTofu commands (init, plan, apply, state, import, workspace) and HCL syntax, with examples to copy.

- Source: https://www.itwonderlab.com/terraform-cheat-sheet/
- Published: 2026-08-15
- Updated: 2026-08-15
- Author: Javier Ruiz Jiménez (https://www.javierruizjimenez.com/)
- Site: IT Wonder Lab (https://www.itwonderlab.com/)

---

## Terraform and OpenTofu quick reference

Replace `terraform` with `tofu` to use OpenTofu: every command below works in both. For explanations, see the [tutorials](https://www.itwonderlab.com/tutorials/terraform/).

### Main workflow

| Command | What it does |
|---|---|
| `terraform init` | Downloads providers and modules and configures the backend |
| `terraform init -upgrade` | Updates providers and modules within the version constraints |
| `terraform init -reconfigure` | Reconfigures the backend ignoring the saved configuration |
| `terraform init -migrate-state` | Moves the state to a new backend |
| `terraform fmt -recursive` | Formats the code |
| `terraform validate` | Checks syntax and consistency |
| `terraform plan` | Shows the changes that would be made |
| `terraform plan -out=tfplan` | Saves the plan to a file |
| `terraform apply` | Applies the changes after confirmation |
| `terraform apply tfplan` | Applies a saved plan, with no confirmation |
| `terraform apply -auto-approve` | Applies without asking (CI only) |
| `terraform destroy` | Destroys everything managed |

### Useful plan and apply flags

| Flag | Use |
|---|---|
| `-var 'name=value'` | Sets a variable |
| `-var-file=pro.tfvars` | Loads variables from a file |
| `-target=aws_instance.web` | Limits the run to a resource (use in emergencies only) |
| `-replace=aws_instance.web` | Forces recreating a resource |
| `-refresh-only` | Updates the state with reality, changes no infrastructure |
| `-destroy` | Plans a destroy |
| `-parallelism=20` | Number of concurrent operations (default 10) |
| `-lock=false` | Does not lock the state (avoid) |
| `-input=false` | Never asks for input (CI) |

### State

| Command | What it does |
|---|---|
| `terraform state list` | Lists the resources in the [state](https://www.itwonderlab.com/terraform-state/) |
| `terraform state show aws_instance.web` | Shows the attributes of one resource |
| `terraform state mv A B` | Renames a resource in the state |
| `terraform state rm A` | Removes a resource from the state without destroying it |
| `terraform state pull` | Prints the remote state |
| `terraform import A id` | Imports an existing resource |
| `terraform force-unlock ID` | Releases a stuck lock |

Prefer `import`, `moved` and `removed` blocks: see [import, moved and removed](https://www.itwonderlab.com/terraform-import-moved-removed/).

### Output, console and graph

```shell
$ terraform output
$ terraform output -raw vpc_id
$ terraform show
$ terraform console              # evaluate expressions
$ terraform graph | dot -Tpng > graph.png
$ terraform providers
$ terraform version
```

### Workspaces

```shell
$ terraform workspace list
$ terraform workspace new dev
$ terraform workspace select dev
$ terraform workspace delete dev
```

See [workspaces vs directories](https://www.itwonderlab.com/terraform-workspaces-vs-directories/).

### Debugging

```shell
$ export TF_LOG=DEBUG
$ export TF_LOG_PATH=terraform.log
```

More in [how to debug Terraform](https://www.itwonderlab.com/how-to-debug-terraform/).

### Environment variables

| Variable | Use |
|---|---|
| `TF_VAR_name` | Value of the variable `name` |
| `TF_LOG` | Log level: TRACE, DEBUG, INFO, WARN, ERROR |
| `TF_INPUT=0` | Disables prompts |
| `TF_CLI_ARGS_plan` | Default arguments for `plan` |
| `TF_DATA_DIR` | Location of the `.terraform` directory |
| `TF_WORKSPACE` | Workspace to use |

### HCL syntax

```hcl title="syntax.tf"
# Resource
resource "aws_instance" "web" {
  ami           = var.ami
  instance_type = "t3.micro"
}

# Data source
data "aws_region" "current" {}

# Variable, local and output
variable "name" { type = string }
locals { prefix = "ditwl-${var.name}" }
output "id" { value = aws_instance.web.id }

# Module
module "network" {
  source = "./modules/network"
  cidr   = "10.0.0.0/16"
}

# Meta-arguments
#   count, for_each, depends_on, provider, lifecycle

# Conditional
instance_type = var.env == "pro" ? "m6i.large" : "t3.micro"

# for expressions
names = [for s in var.subnets : upper(s)]
map   = { for k, v in var.items : k => v.id }

# Splat
ids = aws_instance.web[*].id
```

See [for_each vs count](https://www.itwonderlab.com/terraform-for-each-vs-count/), [dynamic blocks](https://www.itwonderlab.com/terraform-dynamic-blocks/) and [lifecycle](https://www.itwonderlab.com/terraform-lifecycle-meta-argument/).

### Common functions

| Function | Example |
|---|---|
| `merge` | `merge(local.tags, { Name = "x" })` |
| `lookup` | `lookup(var.map, "key", "default")` |
| `format` | `format("web-%02d", count.index + 1)` |
| `join`, `split` | `join(",", var.list)` |
| `toset`, `tolist`, `tomap` | `toset(var.names)` |
| `length` | `length(var.list)` |
| `cidrsubnet` | `cidrsubnet("10.0.0.0/16", 8, 1)` returns `10.0.1.0/24` |
| `file`, `templatefile` | `templatefile("init.tftpl", { name = "x" })` |
| `jsonencode`, `yamlencode` | `jsonencode({ a = 1 })` |
| `try`, `coalesce` | `try(var.obj.value, "default")` |

The full list is in [Terraform functions](https://www.itwonderlab.com/terraform-functions/).
