All guides
Practice now
D1 · IaC and Terraform Fundamentals

HCL syntax: blocks, arguments, and types

HCL (HashiCorp Configuration Language) is the declarative language you use to describe all of your infrastructure in Terraform. Mastering its syntax is the foundation for both the exam and day-to-day work.

Fundamental HCL blocks

Everything in Terraform is defined inside blocks. Each block has a type, optional labels, and a body of arguments between braces.

BlockLabelsPurpose
terraform {}noneConfiguration of Terraform itself (required version, providers, backend)
provider "aws" {}"aws"
resource "aws_vpc" "main" {}"aws_vpc" "main"
data "aws_ami" "ubuntu" {}"aws_ami" "ubuntu"
variable "env" {}"env"
output "vpc_id" {}"vpc_id"
locals {}noneDefines reusable intermediate values without exposing them as a variable
module "vpc" {}"vpc"
# Minimal structure of a root module
terraform {
  required_version = ">= 1.6.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
}

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
  tags = {
    Name = local.vpc_name
  }
}

variable "aws_region" {
  type    = string
  default = "us-east-1"
}

locals {
  vpc_name = "vpc-${var.aws_region}"
}

output "vpc_id" {
  value = aws_vpc.main.id
}

Argument types

Arguments inside blocks have types. Terraform validates them at plan time.

Primitive types

string"us-east-1"

Text. With interpolation: "vpc-${var.env}"

number3

Integer or decimal: 3, 3.14

booltrue / false

Boolean. true or false (without quotes)

Complex types

list(string)

Ordered list. Access by index: var.list[0]

set(string)

Set with no duplicates and no guaranteed order

map(string)

Key-value map. Access: var.map["key"]

object({...})

Structure with specific typed keys

tuple([...])

List with heterogeneous types in fixed positions

Expressions and references

References connect blocks to each other. Terraform uses them to build the dependency graph automatically.

ReferenceExampleDescription
<type>.<name>.<attribute>aws_vpc.main.idAttribute of a resource
var.<name>var.aws_regionValue of an input variable
local.<name>local.vpc_nameValue of a computed local
data.<type>.<name>.<attrib>data.aws_ami.ubuntu.idAttribute of a data source
module.<name>.<output>module.vpc.vpc_idOutput of a child module
path.modulepath.modulePath of the current module directory
terraform.workspaceterraform.workspace
# Interpolation in strings: ${ ... }
resource "aws_subnet" "public" {
  vpc_id            = aws_vpc.main.id          # direct reference
  cidr_block        = "10.0.1.0/24"
  availability_zone = "${var.aws_region}a"    # interpolation
  tags = {
    Name = "subnet-${terraform.workspace}-public"
  }
}

💡 Interpolation vs. direct reference

When the argument directly accepts a reference, don't use interpolation: write vpc_id = aws_vpc.main.id, not vpc_id = "${aws_vpc.main.id}". Interpolation converts the value to a string, losing the original type.

Comments and style

# Single-line comment (preferred style in HCL)

// Also valid (C-style) — less common in Terraform

/* Comment
   spanning
   multiple lines */

# terraform fmt reorders arguments alphabetically
# and aligns the = signs in blocks with several arguments:
resource "aws_s3_bucket" "logs" {
  bucket        = "my-logs-bucket"   # short argument
  force_destroy = true               # long argument aligned
}

🎯 For the exam

  • • HCL supports three comment styles: #, //, and /* */. The exam may ask which ones are valid.
  • • terraform fmt uses 2-space indentation and aligns = in blocks. Never use tabs manually.
  • • Terraform files must have the .tf extension (or .tf.json for native JSON).

File organization

Terraform loads all the .tf files in the working directory as if they were a single file. Splitting them into separate files is purely for human organization.

main.tf

Main resources of the module. The conceptual entry point.

variables.tf

All variable {} declarations. Only the declaration, not the values.

outputs.tf

All output {} blocks.

providers.tf

terraform {} block with required_providers plus provider {} blocks.

locals.tf

locals {} block with intermediate values. Optional for small modules.

versions.tf

Alternative to providers.tf for separating the terraform {} block from the providers.

⚠️ Files that should NOT be committed to VCS

  • • terraform.tfstate / *.tfstate.backup — contain sensitive data in plain text
  • • .terraform/ — directory of downloaded plugins (heavy, regenerable with init)
  • • *.tfvars with secrets — credentials, tokens, passwords
  • • .terraform.lock.hcl — this one SHOULD be committed: it pins exact provider versions

Did you get this topic?

Test what you just learned

In a Terraform HCL file, you need to reference the id attribute of an aws_instance resource named web from another resource. What is the correct syntax?

Ready to test "HCL Syntax: Blocks, Arguments and Types"?

Put what you learned to the test with original Terraform Associate exam questions.

Start Terraform Associate mock exam

Unlimited mock exams with the Pro plan

Log in to track your progress.