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.
| Command | Purpose | Typical use |
|---|---|---|
init | Download providers and configure the backend | Once per directory, and after changing providers/backend |
fmt | Rewrite files to canonical HCL style | Before every commit |
validate | Check syntax and internal consistency | In editors and CI |
plan | Show what apply would change | Before every apply, in PR review |
apply | Create, update, or delete resources | To make real changes |
destroy | Remove all managed resources | Tearing down environments |
output | Print declared output values | Reading IDs, endpoints, URLs |
show | Render state or a saved plan | Auditing current infrastructure |
state | Inspect and modify the state file | Refactoring, 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-approveskips 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 unexpecteddestroyin 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
-targetas 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 planoutput before applying, and use-outso the apply executes exactly the reviewed plan. - Run
terraform fmt -checkandterraform validatein CI to catch style and consistency issues before merge. - Commit
.terraform.lock.hcland useinit -upgradedeliberately so provider versions stay reproducible across machines. - Reserve
-auto-approvefor 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
-jsonoutput fromoutputandshowwhen integrating Terraform results into scripts or other tooling.