Advanced Terraform Features
Terraform

Advanced Terraform Features

Master advanced Terraform features to write more efficient and maintainable infrastructure code.

January 20, 2024
DevHub Team
5 min read

Advanced Terraform Features

Learn how to leverage advanced Terraform features to write more efficient, maintainable, and scalable infrastructure code.

Workspaces

Workspaces allow you to manage multiple states for the same configuration.

Managing Workspaces

# List workspaces terraform workspace list # Create new workspace terraform workspace new dev terraform workspace new prod # Switch workspace terraform workspace select dev

Workspace-Aware Configuration

# Use workspace name in configuration resource "aws_instance" "example" { instance_type = terraform.workspace == "prod" ? "t3.medium" : "t2.micro" tags = { Environment = terraform.workspace } } # Workspace-specific variables locals { environment_config = { dev = { instance_count = 1 instance_type = "t2.micro" } prod = { instance_count = 3 instance_type = "t3.medium" } } config = local.environment_config[terraform.workspace] }

Count and For Each

Count for Resource Replication

# Basic count usage resource "aws_instance" "server" { count = 3 ami = "ami-0c55b159cbfafe1f0" instance_type = "t2.micro" tags = { Name = "server-${count.index + 1}" } } # Conditional creation resource "aws_eip" "lb" { count = var.environment == "prod" ? 1 : 0 instance = aws_instance.server[count.index].id }

For Each for Map and Set Iteration

# Using for_each with a map variable "instances" { type = map(object({ instance_type = string ami = string })) default = { web = { instance_type = "t2.micro" ami = "ami-123" } app = { instance_type = "t3.small" ami = "ami-456" } } } resource "aws_instance" "server" { for_each = var.instances ami = each.value.ami instance_type = each.value.instance_type tags = { Name = "server-${each.key}" } } # Using for_each with a set resource "aws_subnet" "example" { for_each = toset(["us-west-2a", "us-west-2b", "us-west-2c"]) vpc_id = aws_vpc.main.id availability_zone = each.key cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 8, index(tolist(toset(["us-west-2a", "us-west-2b", "us-west-2c"])), each.key)) }

Dynamic Blocks

Basic Dynamic Block Usage

variable "ingress_rules" { type = list(object({ port = number protocol = string cidr_blocks = list(string) })) default = [ { 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" "example" { name = "example" dynamic "ingress" { for_each = var.ingress_rules content { from_port = ingress.value.port to_port = ingress.value.port protocol = ingress.value.protocol cidr_blocks = ingress.value.cidr_blocks } } }

Nested Dynamic Blocks

variable "load_balancer_config" { type = object({ listeners = list(object({ port = number protocol = string rules = list(object({ priority = number actions = list(map(string)) conditions = list(map(string)) })) })) }) } resource "aws_lb" "example" { name = "example" dynamic "listener" { for_each = var.load_balancer_config.listeners content { port = listener.value.port protocol = listener.value.protocol dynamic "default_action" { for_each = listener.value.rules content { type = "forward" target_group_arn = aws_lb_target_group.example[default_action.key].arn dynamic "condition" { for_each = default_action.value.conditions content { host_header { values = condition.value.values } } } } } } } }

Advanced Variable Usage

Variable Validation

variable "environment" { type = string description = "Environment name" validation { condition = contains(["dev", "staging", "prod"], var.environment) error_message = "Environment must be dev, staging, or prod." } } variable "instance_type" { type = string description = "EC2 instance type" validation { condition = can(regex("^t[23].", var.instance_type)) error_message = "Instance type must be t2 or t3 series." } }

Complex Type Constraints

variable "vpc_config" { type = object({ cidr_block = string subnets = map(object({ cidr_block = string public = bool tags = map(string) })) enable_nat_gateway = bool }) default = { cidr_block = "10.0.0.0/16" subnets = { public-1 = { cidr_block = "10.0.1.0/24" public = true tags = { Type = "Public" } } private-1 = { cidr_block = "10.0.2.0/24" public = false tags = { Type = "Private" } } } enable_nat_gateway = true } }

Advanced Expressions

Splat Expressions

# Get all instance IDs output "instance_ids" { value = aws_instance.server[*].id } # Complex splat with for expressions output "private_ips" { value = [for i in aws_instance.server : i.private_ip if i.associate_public_ip_address] }

For Expressions

locals { # Map transformation instance_types = { for k, v in var.instances : k => v.instance_type } # Filtering with for expressions public_subnets = { for k, v in var.vpc_config.subnets : k => v if v.public } # Complex transformations subnet_config = { for az, config in var.vpc_config.subnets : az => { cidr = config.cidr_block tags = merge(config.tags, { AZ = az Environment = var.environment }) } } }

Lifecycle Rules

Advanced Lifecycle Management

resource "aws_instance" "example" { # ... other configuration ... lifecycle { create_before_destroy = true prevent_destroy = true ignore_changes = [ tags, volume_tags, ] precondition { condition = var.environment != "prod" || var.instance_type != "t2.micro" error_message = "Production environment cannot use t2.micro instances." } postcondition { condition = self.associate_public_ip_address == false || var.environment != "prod" error_message = "Production instances cannot have public IPs." } } }

Data Sources and Dependencies

Advanced Data Source Usage

# Dynamic data source queries data "aws_ami" "example" { most_recent = true dynamic "filter" { for_each = var.ami_filters content { name = filter.value.name values = filter.value.values } } } # Depends on with count resource "aws_instance" "example" { count = length(var.subnet_ids) ami = data.aws_ami.example.id instance_type = var.instance_type subnet_id = var.subnet_ids[count.index] depends_on = [ aws_nat_gateway.example, aws_route_table_association.private ] }

Conclusion

Advanced Terraform features enable:

  • Better environment management
  • More efficient resource creation
  • Dynamic and flexible configurations
  • Complex infrastructure patterns
  • Improved code maintainability

Remember to:

  1. Use workspaces for environment separation
  2. Leverage count and for_each for resource replication
  3. Implement dynamic blocks for repeated configurations
  4. Utilize advanced variable validations
  5. Apply proper lifecycle rules
IaC
DevOps
Advanced