HCL2 Syntax: Blocks, Arguments, Types & Identifiers
Human-readable configuration language designed specifically for infrastructure topology.
Primitives (string, number, bool) and structural types (list, map, object, tuple).
Reference any attribute with syntax: '
The Big Idea
HashiCorp Configuration Language (HCL2) is neither a general-purpose programming language like Python or Go, nor a static data serialization format like JSON or YAML. It was purposely engineered to balance human readability with powerful expressions, interpolation, and validation.
Every Terraform configuration is built from blocks and arguments. A block has a type, zero or more labels, and a body delimited by curly braces. Inside the body, arguments assign values to identifiers using the equals operator (=).
Understanding how to reference attributes across blocks is the cornerstone of writing modular Terraform. When you declare aws_subnet.public.vpc_id = aws_vpc.main.id, you are not just passing a value: you are telling the Terraform compiler that the subnet cannot be created until the VPC exists.
Jargon Buster: Plain-English Translations
- Block: A container for configuration content. Block types include terraform, provider, resource, data, variable, output, and locals.
- Argument: A key-value assignment inside a block body, such as 'cidr_block = "10.0.0.0/16"'.
- Identifier: A name given to an argument or label that must start with a letter and contain only alphanumeric characters, underscores, and hyphens.
- Interpolation: Evaluating expressions and functions dynamically within string templates and arguments.
Core Block Anatomy & Structural Types
# 1. Terraform settings block: defines engine & provider requirements
terraform {
required_version = ">= 1.9.0"
}
# 2. Local values: compute reusable values and composite objects
locals {
common_tags = {
Owner = "platform-team"
Environment = "production"
CostCenter = "infrastructure"
}
# Structural object type
network_config = {
vpc_cidr = "10.100.0.0/16"
subnet_count = 3
is_public = true
}
}
# 3. Resource block with two labels: type and local identifier
resource "aws_vpc" "core" {
cidr_block = local.network_config.vpc_cidr
enable_dns_hostnames = true
tags = merge(
local.common_tags,
{ Name = "core-enterprise-vpc" }
)
}
# 4. Referencing exported attributes
resource "aws_subnet" "public_a" {
vpc_id = aws_vpc.core.id # Implicit dependency reference
cidr_block = "10.100.1.0/24"
availability_zone = "us-east-1a"
tags = merge(local.common_tags, { Name = "public-subnet-1a" })
}
Beginner Traps & Gotchas
1. Confusing resource labels with cloud tag names
The Gotcha: Thinking 'resource "aws_vpc" "core"' names the VPC 'core' in AWS Console.
The Fix: The second label ('core') is purely internal to Terraform for local addressing. Use the 'tags = { Name = "..." }' block to set AWS console names.
2. Using quotes around boolean and numeric values
The Gotcha: Writing 'enable_dns_hostnames = "true"' instead of the native boolean 'true'.
The Fix: HCL2 is strongly typed. Use literal booleans (true/false) and unquoted numbers to avoid validation errors.