HCL
HashiCorp Configuration Language - the declarative syntax powering Terraform, Packer, Vault, and Consul.
Block Structure
resource "proxmox_vm_qemu" "web" {
name = "web-01"
target_node = "pve"
cores = 2
memory = 4096
}
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-*-amd64-server-*"]
}
}
module "network" {
source = "./modules/network"
vpc_cidr = "10.0.0.0/16"
environment = var.environment
}
STRUCTURE: HCL is blocks all the way down: type "label" "name" { body }.
Types & Variables
variable "name" {
type = string
default = "web-01"
}
variable "count" {
type = number
default = 3
}
variable "enabled" {
type = bool
default = true
}
variable "tags" {
type = map(string)
default = {
Environment = "dev"
Team = "infra"
}
}
variable "ports" {
type = list(number)
default = [80, 443, 8080]
}
variable "servers" {
type = list(object({
name = string
cores = number
memory = number
}))
}
variable "cidrs" {
type = set(string)
default = ["10.0.1.0/24", "10.0.2.0/24"]
}
variable "environment" {
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Must be dev, staging, or prod."
}
}
variable "disk_size" {
type = number
validation {
condition = var.disk_size >= 10 && var.disk_size <= 500
error_message = "Disk size must be between 10 and 500 GB."
}
}
Expressions
name = "${var.prefix}-${var.environment}-web"
description = <<-EOT
This is a multi-line
description that gets
dedented automatically.
EOT
instance_type = var.environment == "prod" ? "t3.large" : "t3.micro"
# List comprehension
upper_names = [for s in var.names : upper(s)]
# Filtered list
prod_servers = [for s in var.servers : s.name if s.env == "prod"]
# Map comprehension
name_to_ip = { for s in var.servers : s.name => s.ip }
# Map with grouping
by_env = { for s in var.servers : s.env => s.name... }
# Equivalent to [for o in var.list : o.id]
ids = var.list[*].id
Built-in Functions
upper("hello") # "HELLO"
lower("HELLO") # "hello"
title("hello world") # "Hello World"
trimspace(" hello ") # "hello"
replace("hello", "l", "L") # "heLLo"
substr("hello", 0, 3) # "hel"
join("-", ["a", "b", "c"]) # "a-b-c"
split(",", "a,b,c") # ["a", "b", "c"]
format("Hello, %s!", "world") # "Hello, world!"
length(var.list)
contains(var.list, "value")
concat(list1, list2)
flatten([["a"], ["b", "c"]]) # ["a", "b", "c"]
keys(var.map)
values(var.map)
merge(map1, map2) # map2 wins on conflict
lookup(var.map, "key", "default")
element(var.list, 0)
slice(var.list, 0, 3)
sort(var.list)
distinct(var.list)
min(5, 12, 3) # 3
max(5, 12, 3) # 12
ceil(4.1) # 5
floor(4.9) # 4
abs(-5) # 5
coalesce("", "fallback") # "fallback"
try(var.optional, "default") # Safe access
file("${path.module}/script.sh")
filebase64("${path.module}/binary")
templatefile("${path.module}/tmpl.tftpl", { name = "web" })
jsonencode({ name = "web", port = 80 })
yamlencode({ name = "web" })
base64encode("hello")
base64decode("aGVsbG8=")
cidrsubnet("10.0.0.0/16", 8, 1) # "10.0.1.0/24"
cidrhost("10.0.1.0/24", 5) # "10.0.1.5"
cidrnetmask("10.0.1.0/24") # "255.255.255.0"
Locals & Patterns
locals {
environment = terraform.workspace
name_prefix = "${var.project}-${local.environment}"
common_tags = {
Project = var.project
Environment = local.environment
ManagedBy = "terraform"
}
# Merge base tags with resource-specific tags
all_tags = merge(local.common_tags, var.extra_tags)
}
resource "aws_instance" "web" {
tags = local.all_tags
}
resource "aws_security_group" "web" {
name = "${local.name_prefix}-web-sg"
dynamic "ingress" {
for_each = var.allowed_ports
content {
from_port = ingress.value
to_port = ingress.value
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
}
PATTERN: locals for computed values and DRY tags. variables for user input.
Quick Reference
| Pattern | Description |
|---|---|
|
Declare input variable |
|
Define local values |
|
Expose output value |
|
Iterate over set of strings |
|
Conditional resource creation |
|
Generate repeated nested blocks |
|
String interpolation |
|
List comprehension (for expression) |
|
Map comprehension |
|
Safe access with fallback |