lesson ── terraform-beginner ── ~12 мин ── 5 шагов
The previous ten lessons were about what Terraform does. This lesson
is about three commands you will run every day, the ones that prevent 80%
of the embarrassment on code review: fmt, validate, console.
This is not a "bonus", it is basic hygiene. Without them HCL turns into
spaghetti, typos in attribute names surface during plan or apply
(and without validate in pre-commit, sometimes even later), and complex
expressions get debugged by "edit, plan, read, edit".
интерактивный sandbox
Поднимется пара контейнеров: terraform 1.9 и localstack 3.8 в одной сети. В браузере откроется терминал, можно сразу terraform init. Каждый шаг проверяется автоматически. TTL 45 минут, без регистрации.
stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя
In ~/tf-hygiene, create main.tf with deliberately bad formatting:
resource "aws_s3_bucket" "demo" { bucket="linuxlab-hygiene-${random_id.suffix.hex}" tags={Owner="student"
Project ="hygiene-demo"
}
}
resource "random_id" "suffix" {byte_length=4
}
Broken indentation, missing spaces around =, no consistency. This is
the typical look after merging a PR from three developers with different
IDE settings.
Do not run terraform yet, first we will see how fmt handles this.
You can paste the whole thing with `cat > main.tf <<EOF ... EOF`. Do not fix the indentation: leave it messy.
✓ File saved. Now let's clean it up.
Run:
cd /home/student/tf-hygiene
terraform fmt
The output shows main.tf. That means the file was unformatted and is
now fixed.
Open the file and look:
cat main.tf
Now it has:
=.Owner and Project values aligned on =.This is the same code, not a different one. Only the style. terraform plan
would produce the same result both before and after fmt.
The habit: fmt after every edit. Or set it to run in your IDE on
save. See tf-fmt.
If fmt printed nothing, the file is already formatted. Copy the file with the messy indentation again.
✓ After fmt -check the check is clean: the style is canonical.
Let's plant a typo in the HCL. Open main.tf and change bucket to
bucket_name:
resource "aws_s3_bucket" "demo" { bucket_name = "linuxlab-hygiene-${random_id.suffix.hex}"# ^^^^^^^^^^^ intentional typo
tags = {Owner = "student"
Project = "hygiene-demo"
}
}
Run init + validate:
terraform init -backend=false -input=false
terraform validate
You will get:
Error: Unsupported argument
on main.tf line 2, in resource "aws_s3_bucket" "demo":
2: bucket_name = "linuxlab-hygiene-..."
An argument named "bucket_name" is not expected here.
Did you mean "bucket"?
Notice Did you mean "bucket"?, an automatic suggestion based on
similar names. It saves you on typos.
Validate does not reach the cloud. This is one second of debugging
instead of a minute of apply -> Error from AWS -> investigation.
If validate says 'no provider': you forgot `terraform init -backend=false`.
✓ validate caught the typo. Now we fix it and confirm it's clean.
Change bucket_name back to bucket:
resource "aws_s3_bucket" "demo" { bucket = "linuxlab-hygiene-${random_id.suffix.hex}" tags = {Owner = "student"
Project = "hygiene-demo"
}
}
terraform fmt
terraform validate
Validate should say:
Success! The configuration is valid.
This is the right workflow: write HCL, fmt, validate. Only after that does it make sense to run plan/apply.
In CI this same workflow is automated through pre-commit hooks and a set of linters. See tf-validate.
If validate still complains: search for `bucket_name` in the file (`grep -n bucket_name main.tf`), it might be left somewhere.
✓ The HCL is valid. You can move on to plan/apply.
OpenTofu keeps its CLI and state compatible with Terraform for the
commands in this step: migration usually goes through mv .terraform .terraform.bak; tofu init -upgrade. But on the first switch, back up
your state and run on a feature branch, the differences concentrate
in the newer features (variables in backend, state encryption, OCI
registry-backed modules). See tf-opentofu-parity for the full
matrix.
Before you drop a complex expression into HCL, test it in the console. First apply the configuration so there is state:
terraform apply -auto-approve
Now:
terraform console
A > prompt appears. Try:
> 1 + 2 * 3
7
> upper("terraform")"TERRAFORM"
> length(aws_s3_bucket.demo.tags)
2
> aws_s3_bucket.demo.arn
"arn:aws:s3:::linuxlab-hygiene-..."
> [for k, v in aws_s3_bucket.demo.tags : "${k}=${v}"][
"Owner=student",
"Project=hygiene-demo",
]
To exit, exit or Ctrl+D.
This is the main use case for console: check an expression before you drop it into HCL. It will save you hours on the intermediate track (modules, dynamic blocks, complex locals). See tf-console.
If console fails with 'configuration is invalid': validate first, then console.
✓ Console works, sees the state, evaluates expressions. This is your main HCL debugging tool.
The console has a special operator type(...) that prints the
inferred type of an expression:
> type(var.region)
string
> type(aws_s3_bucket.demo.tags)
map of string
> type([1, 2, 3])
tuple([number, number, number])
It saves you in two places:
Often a code review question of "why this type and not the other" is settled with one line in console. See hcl-types for the differences between types.
Three tools, three habits. fmt after every edit. validate before
every plan. console for checking locals and expressions before you
drop them into HCL.
команды
terraform fmt -recursiveformatting, including modulesterraform validatechecks HCL without reaching the cloudterraform consoleREPL for expressions and state inspectionecho 'type(var.X)' | terraform consolenon-interactive type checkконцепции