HCL is a structured configuration language designed by HashiCorp specifically for their tools, with Terraform being the most prominent user. It combines the human-friendliness of JSON with additional features that make it ideal for infrastructure configuration.
Blocks and Arguments
The basic building blocks of HCL are blocks and arguments. Here's a simple example:
# Block declaration
resource "aws_instance" "web_server" {
# Arguments
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
# Nested block
tags = {
Name = "WebServer"
Environment = "Production"
}
}
Variables and Data Types
HCL supports various data types and variable declarations:
# String variable
variable "region" {
type = string
default = "us-west-2"
description = "AWS region for resources"
}
# Number variable
variable "instance_count" {
type = number
default = 2
description = "Number of EC2 instances"
}
# List variable
variable "availability_zones" {
type = list(string)
default = ["us-west-2a", "us-west-2b"]
}
# Map variable
variable "instance_tags" {
type = map(string)
default = {
Environment = "Production"
Team = "DevOps"
}
}
Dynamic Blocks
Dynamic blocks allow you to create repeated nested blocks based on complex data structures:
locals {
ingress_rules = [
{
port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
{
port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
]
}
resource "aws_security_group" "web" {
name = "web-security-group"
dynamic "ingress" {
for_each = local.ingress_rules
content {
from_port = ingress.value.port
to_port = ingress.value.port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
}
Conditional Expressions
HCL supports conditional expressions for dynamic resource configuration:
resource "aws_instance" "server" {
instance_type = var.environment == "production" ? "t2.medium" : "t2.micro"
tags = {
Environment = var.environment
Name = var.environment == "production" ? "prod-server" : "dev-server"
}
}
Count and For Each
For creating multiple similar resources:
# Using count
resource "aws_instance" "server" {
count = var.instance_count
ami = var.ami_id
instance_type = "t2.micro"
tags = {
Name = "server-${count.index + 1}"
}
}
# Using for_each with a map
resource "aws_instance" "servers" {
for_each = {
web = "t2.micro"
app = "t2.small"
db = "t2.medium"
}
ami = var.ami_id
instance_type = each.value
tags = {
Name = "server-${each.key}"
Role = each.key
}
}
Best Practices
Use Consistent Formatting
Use 2-space indentation
Align = signs for readability
Group related resources together
Variable Management
Use descriptive variable names
Always include variable descriptions
Set appropriate variable constraints
Resource Organization
Use separate files for different components
Implement proper state management
Use workspaces for environment separation
Documentation
Comment complex configurations
Use README files
Document variables and outputs