Lesson 2 of 16
Section 1: Foundations, HCL2 Grammar & The Core Lifecycle30 min read & walkthrough

HCL2 Syntax: Blocks, Arguments, Types & Identifiers

01 / GRAMMAR
HCL2 Standard

Human-readable configuration language designed specifically for infrastructure topology.

02 / TYPES
Type System

Primitives (string, number, bool) and structural types (list, map, object, tuple).

03 / SCOPE
Resource Addressing

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.