Skip to content
Infrastructure as Code iac getting-started 5 min read

Terraform CLI Basics

The Terraform CLI is the single entry point for everything you do with infrastructure as code: initializing a working directory, previewing changes, applying them, and inspecting the resulting state. A handful of commands cover the vast majority of day-to-day work, and learning their most useful flags turns slow, error-prone sessions into a tight, predictable loop. This page tours those core commands with the AWS provider and shows realistic output so you know what success looks like. Everything here works identically with OpenTofu by swapping terraform for tofu.

The command you reach for most

Every Terraform command is a subcommand of the terraform binary, optionally followed by flags. The commands below form a natural progression: you init once, validate and fmt while editing, plan and apply to make changes, and destroy to tear them down. The table groups them by purpose.

CommandPurposeTypical use
initDownload providers and configure the backendOnce per directory, and after changing providers/backend
fmtRewrite files to canonical HCL styleBefore every commit
validateCheck syntax and internal consistencyIn editors and CI
planShow what apply would changeBefore every apply, in PR review
applyCreate, update, or delete resourcesTo make real changes
destroyRemove all managed resourcesTearing down environments
outputPrint declared output valuesReading IDs, endpoints, URLs
showRender state or a saved planAuditing current infrastructure
stateInspect and modify the state fileRefactoring, imports, surgery

Initializing a working directory

terraform init is the first command you run in any new directory. It downloads the providers declared in your required_providers block, installs modules, and configures the backend that stores state. It is safe to run repeatedly and is required again whenever you add a provider or change the backend.

terraform init

Output:

Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.0"...
- Installing hashicorp/aws v5.62.0...
- Installed hashicorp/aws v5.62.0 (signed by HashiCorp)

Terraform has been successfully initialized!

Use terraform init -upgrade to pull newer provider versions allowed by your constraints, and commit the generated .terraform.lock.hcl so every machine resolves identical versions.

Formatting and validating

terraform fmt rewrites your .tf files to the canonical style, and terraform validate checks that the configuration is syntactically valid and internally consistent. Neither talks to a cloud provider, so both are fast and ideal for editor-on-save hooks and CI gates.

terraform fmt -recursive
terraform validate

Output:

Success! The configuration is valid.

In CI, run terraform fmt -check -recursive so the pipeline fails on unformatted files instead of silently reformatting them.

Previewing with plan

terraform plan computes the difference between your configuration and the real world, printing a symbol for each action: + to create, ~ to update in place, - to destroy, and -/+ to replace. Nothing changes during a plan, which makes it the safest and most-reviewed command in the workflow.

terraform plan

Output:

Terraform will perform the following actions:

  # aws_s3_bucket.assets will be created
  + resource "aws_s3_bucket" "assets" {
      + bucket = "devcraftly-assets-prod"
      + id     = (known after apply)
      + arn    = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Save the plan to a file with -out so the subsequent apply executes exactly what you reviewed, with no chance of drift between preview and execution:

terraform plan -out=tfplan
terraform apply tfplan

Applying changes

terraform apply carries out the plan. Run with no arguments and it generates a fresh plan, shows it, and prompts for confirmation. Pass a saved plan file and it applies that exact set of changes without prompting.

terraform apply

Output:

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_s3_bucket.assets: Creating...
aws_s3_bucket.assets: Creation complete after 2s [id=devcraftly-assets-prod]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Warning: -auto-approve skips the interactive confirmation. It is essential in automated pipelines but dangerous on a developer laptop pointed at production — there is no second chance to catch an unexpected destroy in the plan.

Targeting and overriding variables

Two flags appear constantly during iterative work. -var (and -var-file) supply input variable values, and -target restricts an operation to specific resources and their dependencies.

terraform apply -var="instance_type=t3.micro" -var-file=prod.tfvars
terraform plan -target=aws_s3_bucket.assets

Tip: Treat -target as a debugging tool, not a deployment strategy. It can leave your state partially applied and out of sync with your configuration. Prefer splitting infrastructure into separate root modules over routinely targeting individual resources.

Inspecting state and outputs

After applying, several commands let you read what exists. terraform output prints declared output values, terraform show renders the full state (or a saved plan) in human-readable form, and terraform state list enumerates every managed resource address.

terraform output bucket_name
terraform state list
terraform show

Output:

"devcraftly-assets-prod"
aws_s3_bucket.assets

Add -json to output or show for machine-readable results that scripts and other tools can consume. The state command also covers advanced operations: terraform state mv renames a resource without recreating it, and terraform state rm removes an entry from state while leaving the real resource untouched — both invaluable when refactoring modules.

Tearing everything down

terraform destroy removes every resource Terraform manages in the current state. It produces a destroy plan and prompts for confirmation just like apply, and it respects -target, -var, and -auto-approve.

terraform destroy

Output:

Plan: 0 to add, 0 to change, 1 to destroy.

aws_s3_bucket.assets: Destroying... [id=devcraftly-assets-prod]
aws_s3_bucket.assets: Destruction complete after 1s

Destroy complete! Resources: 1 destroyed.

Best Practices

  • Always review terraform plan output before applying, and use -out so the apply executes exactly the reviewed plan.
  • Run terraform fmt -check and terraform validate in CI to catch style and consistency issues before merge.
  • Commit .terraform.lock.hcl and use init -upgrade deliberately so provider versions stay reproducible across machines.
  • Reserve -auto-approve for CI pipelines; keep the confirmation prompt on interactive sessions, especially against production.
  • Avoid routine use of -target; split large configurations into focused root modules instead of working around them.
  • Use -json output from output and show when integrating Terraform results into scripts or other tooling.
Last updated June 14, 2026
Was this helpful?