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 Type | Purpose | Layman Meaning |
|---|---|---|
terraform | Sets Terraform and provider versions | The rulebook for the project |
provider | Configures a cloud platform | The phone line to the cloud |
resource | Creates a piece of infrastructure | An item on the shopping list |
data | Reads something that already exists | A lookup in a directory |
variable | Accepts an input value | A blank field on a form |
output | Prints a result | The receipt at the end |
locals | Stores a named value inside the file | A sticky note for reuse |
module | Calls a reusable package of code | A 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 = trueData 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.
| Type | Example | Use |
|---|---|---|
| string | "us-east-1" | Names, regions, IDs |
| number | 3 or 0.5 | Counts, sizes, ports |
| bool | true or false | On 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 moduleNever 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.tfholds the main resources.variables.tfholds input variables.outputs.tfholds outputs.providers.tfholds 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 needsname = "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.regionnever take quotes on their own. - Use
${ }to place values inside strings and? :for yes or no choices. - Run
terraform fmtandterraform validateoften.
