1

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"
  }
}
Jacob
  • 53
  • 5
  • All your subnets are public ones? – Marcin Jun 28 '21 at 21:18
  • All subnets are public and follow the structure I've added to the post, with the necessary parts changed – Jacob Jun 28 '21 at 21:19
  • Then what are your security groups. Also why don't you use load balancer? That's the proper way to access website on instances in ASG. – Marcin Jun 28 '21 at 21:23
  • I'm using a load balancer which I've now also added. It's my first attempt at doing so, so maybe I need to have a look at it a bit closer. Also added the security group but it's fairly standard and works fine with the standalone instance, so I'm pretty confident about it. – Jacob Jun 28 '21 at 21:28
  • So acces through ALB works, but not directly to instances? Your post is not clear, as its missing a lot of info. Can you explain in details your architecture? – Marcin Jun 28 '21 at 21:30
  • I've updated with the whole script, as I realise this might be easier! Truthfully I'm not entirely certain if my load balancer is working, this may be something I need to look into further. I think at this point everything is pointing to an error there. – Jacob Jun 28 '21 at 21:38

1 Answers1

0

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
  }
}

Marcin
  • 215,873
  • 14
  • 235
  • 294