linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
Intro
Lessons
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Введение
  • Уроки
  • How it works
  • База знаний
  • Шпаргалка
  • Capstone
  • Собеседование
home/terraform/lessons/tf-beginner-11-fmt-validate-console

lesson ── terraform-beginner ── ~12 мин ── 5 шагов

HCL hygiene: fmt, validate, console

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 минут, без регистрации.

запустить sandbox →

stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя

Шаги

  1. 01

    Create deliberately messy HCL

    In ~/tf-hygiene, create main.tf with deliberately bad formatting:

    hcl
    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.

  2. 02

    terraform fmt: fixed in a second

    Run:

    bash
    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:

    bash
    cat main.tf

    Now it has:

    • Two-space indentation.
    • Spaces around =.
    • The Owner and Project values aligned on =.
    • No stray blank lines.

    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.

  3. 03

    terraform validate: a check without the cloud

    Let's plant a typo in the HCL. Open main.tf and change bucket to bucket_name:

    hcl
    resource "aws_s3_bucket" "demo" {
      bucket_name = "linuxlab-hygiene-${random_id.suffix.hex}"
      # ^^^^^^^^^^^ intentional typo
      tags = {
        Owner   = "student"
        Project = "hygiene-demo"
      }
    }

    Run init + validate:

    bash
    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.

  4. 04

    Fix the typo and confirm it's clean

    Change bucket_name back to bucket:

    hcl
    resource "aws_s3_bucket" "demo" {
      bucket = "linuxlab-hygiene-${random_id.suffix.hex}"
      tags = {
        Owner   = "student"
        Project = "hygiene-demo"
      }
    }
    bash
    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.

    The same thing on OpenTofu

    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.

    • → OpenTofu parity
  5. 05

    terraform console. A REPL for expressions

    Before you drop a complex expression into HCL, test it in the console. First apply the configuration so there is state:

    bash
    terraform apply -auto-approve

    Now:

    bash
    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.

    What type() does and why

    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:

    • When writing a module (does the input variable take a list or a set?).
    • With for_each (a set or map is required, not a list).

    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.

    • → HCL data types
    • → terraform console in full

Что ты узнал

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 modules
  • terraform validatechecks HCL without reaching the cloud
  • terraform consoleREPL for expressions and state inspection
  • echo 'type(var.X)' | terraform consolenon-interactive type check

концепции

  • · fmt does not change meaning, only style: always run it
  • · validate catches syntax and unknown arguments, it does not catch logic
  • · console: the most underrated Terraform tool

← предыдущий

Drift detection, scheduled plan, and alerting

следующий →

Utility providers: random, time, archive, external

Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies