Terraform Associate
Deep Dive
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.
Contents
Everything in Terraform is defined inside blocks. Each block has a type, optional labels, and a body of arguments between braces.
| Block | Labels | Purpose |
|---|---|---|
| terraform {} | none | Configuration of Terraform itself (required version, providers, backend) |
| provider "aws" {} | "aws" | Configures a provider (credentials, region, alias) |
| resource "aws_vpc" "main" {} | "aws_vpc" "main" | Defines a managed resource: type + local name |
| data "aws_ami" "ubuntu" {} | "aws_ami" "ubuntu" | Reads data from existing infrastructure without creating it |
| variable "env" {} | "env" | Declares an input parameter for the module |
| output "vpc_id" {} | "vpc_id" | Exposes a value from the module outward |
| locals {} | none | Defines reusable intermediate values without exposing them as a variable |
| module "vpc" {} | "vpc" | Calls a child module (local or remote) |
# 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
}Arguments inside blocks have types. Terraform validates them at plan time.
Primitive types
Text. With interpolation: "vpc-${var.env}"
Integer or decimal: 3, 3.14
Boolean. true or false (without quotes)
Complex types
Ordered list. Access by index: var.list[0]
Set with no duplicates and no guaranteed order
Key-value map. Access: var.map["key"]
Structure with specific typed keys
List with heterogeneous types in fixed positions
References connect blocks to each other. Terraform uses them to build the dependency graph automatically.
| Reference | Example | Description |
|---|---|---|
| <type>.<name>.<attribute> | aws_vpc.main.id | Attribute of a resource |
| var.<name> | var.aws_region | Value of an input variable |
| local.<name> | local.vpc_name | Value of a computed local |
| data.<type>.<name>.<attrib> | data.aws_ami.ubuntu.id | Attribute of a data source |
| module.<name>.<output> | module.vpc.vpc_id | Output of a child module |
| path.module | path.module | Path of the current module directory |
| terraform.workspace | terraform.workspace | Name of the active 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.
# 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
#, //, and /* */. The exam may ask which ones are valid.terraform fmt uses 2-space indentation and aligns = in blocks. Never use tabs manually..tf extension (or .tf.json for native JSON).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 versionsDid 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?
Put what you learned to the test with original Terraform Associate exam questions.
Start Terraform Associate mock examUnlimited mock exams with the Pro plan
Log in to track your progress.