Terraform HCL Syntax

Every Terraform file speaks one language called HashiCorp Configuration Language, or HCL. Picture a recipe card with a title, a list of ingredients, and a few short instructions. HCL works the same way. It uses blocks, arguments, and expressions that follow a small set of rules. Learn those rules once and every Terraform file you meet becomes easy to read.

The Anatomy of a Block

A block is a container that describes one thing. It has a type, optional labels, and a body wrapped in curly braces. The body holds arguments and sometimes nested blocks.

Diagram: Parts of a Block

resource "aws_s3_bucket" "photos" {
   |           |              |
   |           |              +-- label 2: your own name for this block
   |           +----------------- label 1: the kind of resource
   +----------------------------- block type

  bucket = "my-photo-bucket-2026"     (argument: name = value)

  tags = {                            (argument holding a map)
    Team = "media"
  }
}

Common Block Types

Block TypePurposeLayman Meaning
terraformSets Terraform and provider versionsThe rulebook for the project
providerConfigures a cloud platformThe phone line to the cloud
resourceCreates a piece of infrastructureAn item on the shopping list
dataReads something that already existsA lookup in a directory
variableAccepts an input valueA blank field on a form
outputPrints a resultThe receipt at the end
localsStores a named value inside the fileA sticky note for reuse
moduleCalls a reusable package of codeA ready-made kit

Arguments

An argument assigns a value to a name using the equals sign. The name sits on the left and the value sits on the right. Write one argument per line. The order of arguments inside a block never changes the result.

instance_type = "t3.micro"
count         = 2
monitoring    = true

Data Types

Every value in HCL has a type. Terraform checks the type before it builds anything, so a wrong type produces a clear error early.

TypeExampleUse
string"us-east-1"Names, regions, IDs
number3 or 0.5Counts, sizes, ports
booltrue or falseOn and off switches
list["a", "b", "c"]Ordered collection
map{ env = "dev", team = "web" }Key and value pairs

Expressions and References

An expression computes a value. The simplest expression is a literal such as "dev". Most expressions point to another part of the configuration.

Reference Patterns

var.region                    # an input variable
local.bucket_name             # a local value
aws_s3_bucket.photos.id       # an attribute of a resource
data.aws_ami.linux.id         # an attribute of a data source
module.network.vpc_id         # an output of a module

Never wrap a reference in quotes by itself. Write var.region, not "var.region". Quotes turn the reference into plain text.

String Interpolation

Use ${ } inside a quoted string to insert a value into text.

bucket = "${var.project}-${var.env}-logs"

If project is shop and env is dev, Terraform builds the name shop-dev-logs.

Conditional Expression

instance_type = var.env == "prod" ? "t3.large" : "t3.micro"

Read it as a question: is the environment prod? Terraform picks the value before the colon for yes and the value after the colon for no.

Comments

Comments explain your thinking to the next reader. Terraform ignores them.

# A single-line comment
// Another single-line style
/* A comment that
   spans several lines */

Files and Formatting

Terraform reads every file ending in .tf inside a folder and treats them as one configuration. File names carry no special meaning, yet teams follow a common pattern.

  • main.tf holds the main resources.
  • variables.tf holds input variables.
  • outputs.tf holds outputs.
  • providers.tf holds provider and version settings.

Run terraform fmt to align equals signs and fix indentation automatically. Run terraform validate to catch syntax and type mistakes before planning.

Common Syntax Mistakes

  • Writing name: "web" with a colon. HCL needs name = "web".
  • Adding commas between arguments inside a block. Blocks use new lines, not commas.
  • Forgetting quotes around string values such as region = us-east-1.
  • Using single quotes. HCL accepts double quotes only.
  • Leaving a curly brace unclosed after copying a snippet.

Key Points

  • HCL builds everything from blocks, arguments, and expressions.
  • A block has a type, labels, and a body inside curly braces.
  • An argument follows the pattern name = value.
  • Core types are string, number, bool, list, and map.
  • References such as var.region never take quotes on their own.
  • Use ${ } to place values inside strings and ? : for yes or no choices.
  • Run terraform fmt and terraform validate often.