Allowing load-balanced autoscaled instances to connect to the internet - AWS / Terraform

145 Views Asked by At

I'm using Terraform and I'm having a tricky time with connecting my autoscaled AWS EC2 instances to the internet. I can launch a standalone EC2 that connects with no difficulty, but when I visit the public IP addresses of my instances created with an autoscaling group I get "This site can’t be reached xxx.xx.xxx.xxx unexpectedly closed the connection."

The main difference I'm seeing is that I can specify a network interface with an EC2, but I'm not sure how this would work with my launch template. My instances launch into different subnets in different availability zones, and the template is as follows:

provider "aws" {
  region     = "us-east-1"
  access_key = "xxxxx"
  secret_key = "xxxxx"
}

data "template_file" "testfile" {
  template = <<EOF
                #!/bin/bash
                sudo apt update -y
                sudo apt install apache2 -y
                sudo systemct1 start apache2
                sudo bash -c 'echo hello, world! > var/www/html/index.html'
                EOF
}

resource "aws_vpc" "first_vpc" {
  cidr_block = "10.0.0.0/16"
  tags = {
    Name = "prod-vpc"
  }
}

resource "aws_internet_gateway" "gw" {
  vpc_id = aws_vpc.first_vpc.id

  tags = {
    Name = "prod-igw"
  }
}

resource "aws_route_table" "prod_route_table" {
  vpc_id = aws_vpc.first_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.gw.id
  }

  route {
    ipv6_cidr_block = "::/0"
    gateway_id      = aws_internet_gateway.gw.id
  }

  tags = {
    Name = "prod-rt"
  }
}

resource "aws_subnet" "subnet_1" {
  vpc_id            = aws_vpc.first_vpc.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "us-east-1a"
  map_public_ip_on_launch = true

  tags = {
    Name = "prod-subnet-1"
    Tier = "public"
  }
}

resource "aws_subnet" "subnet_2" {
  vpc_id            = aws_vpc.first_vpc.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "us-east-1b"
  map_public_ip_on_launch = true

  tags = {
    Name = "prod-subnet-2"
    Tier = "public"
  }
}

resource "aws_subnet" "subnet_3" {
  vpc_id            = aws_vpc.first_vpc.id
  cidr_block        = "10.0.3.0/24"
  availability_zone = "us-east-1c"
  map_public_ip_on_launch = true

  tags = {
    Name = "prod-subnet-3"
    Tier = "public"
  }
}

resource "aws_route_table_association" "a" {
  subnet_id      = aws_subnet.subnet_1.id
  route_table_id = aws_route_table.prod_route_table.id
}

resource "aws_route_table_association" "b" {
  subnet_id      = aws_subnet.subnet_2.id
  route_table_id = aws_route_table.prod_route_table.id
}

resource "aws_route_table_association" "c" {
  subnet_id      = aws_subnet.subnet_3.id
  route_table_id = aws_route_table.prod_route_table.id
}

resource "aws_security_group" "allow_web" {
  name        = "allow_web"
  description = "Allow web inbound traffic"
  vpc_id      = aws_vpc.first_vpc.id

  ingress {
    description = "HTTPS from VPC"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "HTTP from VPC"
    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"]
    ipv6_cidr_blocks = ["::/0"]
  }

  tags = {
    Name = "allow_tls"
  }
}

resource "aws_launch_template" "frontend" {
  name                   = "frontend"
  image_id               = "ami-0ee02acd56a52998e"
  instance_type          = "t2.micro"
  vpc_security_group_ids = [aws_security_group.allow_web.id]

  network_interfaces {
    device_index = 0
    associate_public_ip_address = true
  }

  user_data = base64encode(data.template_file.testfile.rendered)
}

resource "aws_lb" "loadbalancer" {
  name               = "loadbalancer"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.allow_web.id]
  subnets            = [aws_subnet.subnet_1.id, aws_subnet.subnet_2.id, aws_subnet.subnet_3.id]

  tags = {
    Environment = "production"
  }
}

resource "aws_autoscaling_group" "as_group_1" {
  vpc_zone_identifier = [aws_subnet.subnet_1.id, aws_subnet.subnet_2.id, aws_subnet.subnet_3.id]
  desired_capacity    = 3
  max_size            = 5
  min_size            = 2
  target_group_arns   = [aws_lb_target_group.frontendhttp.arn]

  launch_template {
    id      = aws_launch_template.frontend.id
    version = "$Latest"
  }
}

resource "aws_lb_target_group" "frontendhttp" {
  name     = "frontendhttp"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.first_vpc.id
}

resource "aws_lb_listener" "frontendhttp" {
  load_balancer_arn = aws_lb.loadbalancer.arn
  port              = "80"
  protocol          = "HTTP"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.frontendhttp.arn
  }
}


#Test standalone instance

resource "aws_network_interface" "web_server_1" {
  subnet_id       = aws_subnet.subnet_1.id
  private_ips     = ["10.0.1.50"]
  security_groups = [aws_security_group.allow_web.id]
}

resource "aws_instance" "ubuntu-1" {
  ami               = "ami-0ee02acd56a52998e"
  instance_type     = "t2.micro"
  availability_zone = "us-east-1a" #hardcoded to ensure that subnet and instance are in same availability availability zone 

  network_interface {
    device_index         = 0
    network_interface_id = aws_network_interface.web_server_1.id
  }
  user_data = <<-EOF
                #!/bin/bash
                sudo apt update -y
                sudo apt install apache2 -y
                sudo systemct1 start apache2
                sudo bash -c 'echo hello! > var/www/html/index.html'
                EOF
  tags = {
    Name = "web-server"
  }
}
1

There are 1 best solutions below

1
On BEST ANSWER

I modified a bit your template (user data, its indentation and aws_launch_template), and now it works now. It will work only over HTTP, as you don't have HTTPS setup, so don't need SG rules for HTTPS.




data "template_file" "testfile" {
  template = <<EOF
#!/bin/bash
apt update -y
apt install apache2 -y
systemct1 start apache2
echo "hello, world!" > var/www/html/index.html
EOF
}

resource "aws_vpc" "first_vpc" {
  cidr_block = "10.0.0.0/16"
  tags = {
    Name = "prod-vpc"
  }
}

resource "aws_internet_gateway" "gw" {
  vpc_id = aws_vpc.first_vpc.id

  tags = {
    Name = "prod-igw"
  }
}

resource "aws_route_table" "prod_route_table" {
  vpc_id = aws_vpc.first_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.gw.id
  }

  route {
    ipv6_cidr_block = "::/0"
    gateway_id      = aws_internet_gateway.gw.id
  }

  tags = {
    Name = "prod-rt"
  }
}

resource "aws_subnet" "subnet_1" {
  vpc_id            = aws_vpc.first_vpc.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "us-east-1a"
  map_public_ip_on_launch = true

  tags = {
    Name = "prod-subnet-1"
    Tier = "public"
  }
}

resource "aws_subnet" "subnet_2" {
  vpc_id            = aws_vpc.first_vpc.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "us-east-1b"
  map_public_ip_on_launch = true

  tags = {
    Name = "prod-subnet-2"
    Tier = "public"
  }
}

resource "aws_subnet" "subnet_3" {
  vpc_id            = aws_vpc.first_vpc.id
  cidr_block        = "10.0.3.0/24"
  availability_zone = "us-east-1c"
  map_public_ip_on_launch = true

  tags = {
    Name = "prod-subnet-3"
    Tier = "public"
  }
}

resource "aws_route_table_association" "a" {
  subnet_id      = aws_subnet.subnet_1.id
  route_table_id = aws_route_table.prod_route_table.id
}

resource "aws_route_table_association" "b" {
  subnet_id      = aws_subnet.subnet_2.id
  route_table_id = aws_route_table.prod_route_table.id
}

resource "aws_route_table_association" "c" {
  subnet_id      = aws_subnet.subnet_3.id
  route_table_id = aws_route_table.prod_route_table.id
}

resource "aws_security_group" "allow_web" {
  name        = "allow_web"
  description = "Allow web inbound traffic"
  vpc_id      = aws_vpc.first_vpc.id

  ingress {
    description = "HTTP from VPC"
    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"]
    ipv6_cidr_blocks = ["::/0"]
  }

  tags = {
    Name = "allow_http"
  }
}

resource "aws_launch_template" "frontend" {
  name                   = "frontend"
  image_id               = "ami-0ee02acd56a52998e"
  instance_type          = "t2.micro"
  vpc_security_group_ids = [aws_security_group.allow_web.id]

# DONT NEED THIS
#   network_interfaces {
#     device_index = 0
#     associate_public_ip_address = true
#   }

  user_data = base64encode(data.template_file.testfile.rendered)
}

resource "aws_lb" "loadbalancer" {
  name               = "loadbalancer"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.allow_web.id]
  subnets            = [aws_subnet.subnet_1.id, aws_subnet.subnet_2.id, aws_subnet.subnet_3.id]

  tags = {
    Environment = "production"
  }
}

resource "aws_autoscaling_group" "as_group_1" {
  vpc_zone_identifier = [aws_subnet.subnet_1.id, aws_subnet.subnet_2.id, aws_subnet.subnet_3.id]
  desired_capacity    = 3
  max_size            = 5
  min_size            = 2
  target_group_arns   = [aws_lb_target_group.frontendhttp.arn]

  launch_template {
    id      = aws_launch_template.frontend.id
    version = "$Latest"
  }
}

resource "aws_lb_target_group" "frontendhttp" {
  name     = "frontendhttp"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.first_vpc.id
}

resource "aws_lb_listener" "frontendhttp" {
  load_balancer_arn = aws_lb.loadbalancer.arn
  port              = "80"
  protocol          = "HTTP"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.frontendhttp.arn
  }
}