HCL, Resource Addresses, and Project Layout

Read Terraform blocks, expressions, references, and version constraints, and organize a root module clearly.

Beginner⏱ 2 min readLesson 3 of 16#terraform#hcl#configuration#versions

Read a block

Terraform usually uses HashiCorp Configuration Language, or HCL. A block contains a type, optional labels, and a body. An argument assigns an expression to a name.

resource "local_file" "notes" {
  filename = "${path.module}/notes.txt"
  content  = "Review the plan before apply.\n"
}

The block type is resource. The labels identify the resource type and local name. filename and content are arguments defined by the local provider.

locals {
  project = "academy"
  tags = {
    Project   = local.project
    ManagedBy = "Terraform"
  }
}

Locals name expressions. They cannot be overridden through CLI flags like input variables. Refer to a local as local.project, an input as var.environment, and a resource attribute as aws_vpc.main.id.

Values and expressions

FormExampleMeaning
String"academy"Text
Number3Numeric value
BooleantrueTrue/false
List["web", "api"]Ordered collection
Map/object{ team = "platform" }Named values
NullnullAbsence; often omit an optional argument
Conditionalvar.production ? 3 : 1Choose one of two expressions
Functionlower(var.project)Transform a value

Terraform is declarative: moving a block to the top of a file does not make it execute first. References establish dependencies.

One directory is one module

A common root layout is:

storage-lab/
  versions.tf    # CLI and provider constraints
  providers.tf   # region and provider settings
  main.tf        # resources
  variables.tf   # input contract
  outputs.tf     # results for callers
  terraform.tfvars
  .terraform.lock.hcl

All top-level .tf files in that directory are combined. Filenames organize code for humans. Subdirectories are separate modules and are not loaded automatically.

Constraints versus locking

version = "~> 2.5" allows versions from 2.5 up to, but excluding, 3.0. "~> 2.5.0" stops before 2.6.0. A constraint describes acceptable releases; .terraform.lock.hcl records the selected provider versions and checksums.

Use terraform init -upgrade only when you intend to reconsider provider selections. Review the lock-file diff and resulting plan together. The lock file does not lock remote module versions: pin registry modules explicitly and Git modules to immutable refs when reproducibility matters.

Explore with the console

terraform console

Try upper("academy"), length(["api", "web"]), and { for name in ["api", "web"] : name => upper(name) }. Exit with exit.

Practice: split the local lab across versions.tf, main.tf, and outputs.tf. Run plan. Reorganizing unchanged blocks within one module should not change infrastructure.

Further reading: Configuration syntax, dependency lock file.