Reference: Terraform Up & Running (O’Reilly)
Let’s build an AWS Autoscaling Group (ASG) and front it with an Application Load Balancer (ALB) using Terraform. We’ll also introduce data sources, which fetch attributes from existing resources.
Data Sources
data "aws_vpc" "default" {
default = true
}
data "aws_subnet_ids" "default" {
vpc_id = data.aws_vpc.default.id
}
These data sources retrieve the default VPC and its subnets so we can reuse them without hardcoding IDs.
Launch Configuration
Defines how ASG instances should be created.
resource "aws_launch_configuration" "example" {
image_id = "ami-0ea5eb4b05645aa8a"
instance_type = "t3.nano"
security_groups = [aws_security_group.instance.id]
user_data = <<-EOF
#!/bin/bash
echo "Hello, world" > index.html
nohup busybox httpd -f -p 8080 &
EOF
lifecycle {
create_before_destroy = true
}
}
create_before_destroy prevents Terraform from deleting the old configuration before the new one exists.
Autoscaling Group
resource "aws_autoscaling_group" "example" {
min_size = 2
max_size = 10
launch_configuration = aws_launch_configuration.example.name
vpc_zone_identifier = data.aws_subnet_ids.default.ids
target_group_arns = [aws_lb_target_group.asg.arn]
health_check_type = "ELB"
tag {
key = "Name"
value = "terraform-asg-example"
propagate_at_launch = true
}
}
Ensure the subnets span multiple AZs for high availability.
Application Load Balancer Components
We need four resources:
aws_lbaws_lb_listeneraws_lb_target_groupaws_lb_listener_rule
Load Balancer
resource "aws_lb" "example" {
name = "terraform-asg-example"
load_balancer_type = "application"
subnets = data.aws_subnet_ids.default.ids
security_groups = [aws_security_group.alb.id]
}
Listener
resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.example.arn
port = 80
protocol = "HTTP"
default_action {
type = "fixed-response"
fixed_response {
content_type = "text/plain"
message_body = "404: page not found"
status_code = "404"
}
}
}
Target Group
resource "aws_lb_target_group" "asg" {
name = "terraform-asg-example"
port = var.server_port
protocol = "HTTP"
vpc_id = data.aws_vpc.default.id
health_check {
path = "/"
protocol = "HTTP"
matcher = "200"
interval = 15
timeout = 3
healthy_threshold = 2
unhealthy_threshold = 2
}
}
Listener Rule
resource "aws_lb_listener_rule" "asg" {
listener_arn = aws_lb_listener.http.arn
priority = 100
action {
type = "forward"
target_group_arn = aws_lb_target_group.asg.arn
}
condition {
path_pattern {
values = ["*"]
}
}
}
Security Group for the ALB
resource "aws_security_group" "alb" {
name = "terraform-example-alb"
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
ALBs need outbound access for health checks, so open egress accordingly.
Output
output "alb_dns_name" {
value = aws_lb.example.dns_name
description = "The domain name of the load balancer"
}
Apply everything (increase parallelism for speed):
terraform apply -parallelism=30
Test:
curl http://<alb_dns_name>
When you’re done, destroy the resources to avoid charges:
terraform destroy