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:
- Use workspaces for environment separation
- Leverage count and for_each for resource replication
- Implement dynamic blocks for repeated configurations
- Utilize advanced variable validations
- Apply proper lifecycle rules
IaC
DevOps
Advanced