Terraform HCL Guide

ยท

2 min read

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

  1. Use Consistent Formatting

    • Use 2-space indentation

    • Align = signs for readability

    • Group related resources together

  2. Variable Management

    • Use descriptive variable names

    • Always include variable descriptions

    • Set appropriate variable constraints

  3. Resource Organization

    • Use separate files for different components

    • Implement proper state management

    • Use workspaces for environment separation

  4. Documentation

    • Comment complex configurations

    • Use README files

    • Document variables and outputs

ย