0

I am trying to achieve setup shown in AWS doc (excluding Elastic IP because I am a bit scared to miss conditions described here to avoid possible charges in my Free Tier account) but so far I am only getting 502 Bad Gateway and not sure what I am missing/doing wrong. Although both instance check are successful ("System reachability check passed" and "Instance reachability check passed"), related Target Groups are "Unhealthy". Any is much appreciated.

Note: I am not experienced in AWS infra setup (nor terraform) but this is best I could produce after going through resources online.

terraform {
  required_version = "~> 1.4.4"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.30.0"
    }
  }
}

provider "aws" {
  profile = "development"
  region  = local.region
}

# -- VARS ----------------------------------------------------------------------

locals {
  project = "app"

  region = "eu-west-1"

  vpc_cidr = "10.0.0.0/16"

  public_subnets = {
    "a1" = {
      az   = "${local.region}a"
      cidr = "10.0.10.0/24"
    },
    "b1" = {
      az   = "${local.region}b"
      cidr = "10.0.20.0/24"
    }
  }

  private_subnets = {
    "a1" = {
      az   = "${local.region}a"
      cidr = "10.0.135.0/24"
    },
    "b1" = {
      az   = "${local.region}b"
      cidr = "10.0.145.0/24"
    }
  }

  ec2_ami           = "ami-0fb2f0b847d44d4f0" // Amazon Linux 2023 AMI 2023.1.20230705.0 x86_64 HVM kernel-6.1
  ec2_instance_type = "t2.micro"
}

# -- VPC -----------------------------------------------------------------------

resource "aws_vpc" "vpc" {
  cidr_block           = local.vpc_cidr
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = local.project
  }
}

# -- ROUTE TABLE ---------------------------------------------------------------

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.vpc.id

  tags = {
    Name = "${local.project}-public"
  }
}

resource "aws_route_table" "private" {
  vpc_id = aws_vpc.vpc.id

  tags = {
    Name = "${local.project}-private"
  }
}

# -- INTERNET GATEWAY ----------------------------------------------------------

resource "aws_internet_gateway" "public" {
  vpc_id = aws_vpc.vpc.id

  tags = {
    Name = local.project
  }
}

resource "aws_route" "public" {
  destination_cidr_block = "0.0.0.0/0"
  route_table_id         = aws_route_table.public.id
  gateway_id             = aws_internet_gateway.public.id
}

# -- SUBNET --------------------------------------------------------------------

resource "aws_subnet" "public" {
  for_each = local.public_subnets

  vpc_id            = aws_vpc.vpc.id
  cidr_block        = each.value.cidr
  availability_zone = each.value.az

  tags = {
    Name = "${local.project}-public-${each.key}"
  }
}

resource "aws_subnet" "private" {
  for_each = local.private_subnets

  vpc_id            = aws_vpc.vpc.id
  cidr_block        = each.value.cidr
  availability_zone = each.value.az

  tags = {
    Name = "${local.project}-private-${each.key}"
  }
}

resource "aws_route_table_association" "public" {
  for_each = local.public_subnets

  subnet_id      = aws_subnet.public[each.key].id
  route_table_id = aws_route_table.public.id
}

resource "aws_route_table_association" "private" {
  for_each = local.private_subnets

  subnet_id      = aws_subnet.private[each.key].id
  route_table_id = aws_route_table.private.id
}

# -- SECURITY GROUP ------------------------------------------------------------

resource "aws_security_group" "elb" {
  name   = "${local.project}-elb"
  vpc_id = aws_vpc.vpc.id

  ingress {
    description = "From Internet to ELB"
    from_port   = "80"
    to_port     = "80"
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    description = "From ELB to Internet"
    from_port   = "0"
    to_port     = "0"
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_security_group" "ec2" {
  name   = "${local.project}-ec2"
  vpc_id = aws_vpc.vpc.id

  ingress {
    description     = "From ELB to EC2"
    from_port       = "80"
    to_port         = "80"
    protocol        = "tcp"
    security_groups = [aws_security_group.elb.id]
  }

  egress {
    description = "From EC2 to ELB"
    from_port   = "0"
    to_port     = "0"
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# -- EC2 -----------------------------------------------------------------------

resource "aws_instance" "ec2" {
  for_each = local.private_subnets

  ami                         = local.ec2_ami
  instance_type               = local.ec2_instance_type
  subnet_id                   = aws_subnet.private[each.key].id
  vpc_security_group_ids      = [aws_security_group.ec2.id]
  associate_public_ip_address = true

  tags = {
    Name = "${local.project}-ec2-private-subnet-${each.key}"
  }
}

# -- LOAD BALANCER -------------------------------------------------------------

resource "aws_lb" "application" {
  name = "${local.project}-application-load-balancer"

  load_balancer_type = "application"
  security_groups    = [aws_security_group.elb.id]
  subnets            = [for sub in aws_subnet.public : sub.id]
}

resource "aws_lb_target_group" "application" {
  name = "${local.project}-http-application"

  port                          = 80
  protocol                      = "HTTP"
  vpc_id                        = aws_vpc.vpc.id
  load_balancing_algorithm_type = "least_outstanding_requests"

  health_check {
    protocol            = "HTTP"
    port                = 80
    path                = "/"
    healthy_threshold   = 2
    unhealthy_threshold = 2
    timeout             = 10
    interval            = 30
    matcher             = 200
  }
}

resource "aws_lb_target_group_attachment" "ec2" {
  for_each = aws_instance.ec2

  port             = 80
  target_id        = each.value.id
  target_group_arn = aws_lb_target_group.application.arn
}

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

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

  tags = {
    Name = "${local.project}-application-load-listener"
  }
}
BentCoder
  • 12,257
  • 22
  • 93
  • 165

1 Answers1

0

Port 80 is going to be blocked by Amazon Linux by default. You'll need to add this commend to the EC2 instance's user-data script to open port 80 in the Linux firewall:

iptables -I INPUT -p tcp -m tcp --dport 80 -j ACCEPT
Mark B
  • 183,023
  • 24
  • 297
  • 295
  • Thank you Mark. I've added it to script and rebuild all from scratch but still same problem. Given 80s are all over the place, would you mind pointing out which lines I should change from 80 to 8000 instead please? I tried myself afterwards but I think I messed things up. – BentCoder Jul 08 '23 at 22:54
  • On a side note, I have a smaller setup version where ELB is in public subnet with two EC2 instances and all using port 80 which works fine by default. Based on that I am not sure if this is really port issue. I feel like I am wiring/configuring things up wrong above with this new setup. If this makes sense. – BentCoder Jul 08 '23 at 22:58
  • If you want to change the port to `8000` then change it everywhere except in the load balancer listener. As for your other service that works without issue, I can't speak to that as I can't see all the configuration for that. Obviously there is something different about that environment and the one in your question, or you wouldn't be having an issue. – Mark B Jul 09 '23 at 13:55
  • Still not working. I am definitely either wiring resources wrong or missing a configuration. – BentCoder Jul 09 '23 at 16:18
  • I know you said in another question you were able to access the EC2 instances directly, bypassing the load balancer, at one point. When you had it configured that way, were you able to directly access the website at the EC2 instance's address? You'll need to do some debugging like that to determine what the actual issue is. – Mark B Jul 09 '23 at 16:20
  • Yes, in that other case I could access EC2 directly and also SSH into it. In this case I cannot even SSH into EC2 in the private subnets to debug why they are unhealthy. I am not even sure where else to look for debugging to be fair. I have been trying last two days but no luck. – BentCoder Jul 09 '23 at 16:44
  • For an EC2 instance in a private subnet, instead of using SSH you should looking into using SSM connect https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/session-manager.html – Mark B Jul 09 '23 at 17:11
  • I'm noticing you don't seem to have a NAT Gateway in the Terraform code. Your instances in the private subnet probably can't run the `yum` commands successfully in that case. – Mark B Jul 09 '23 at 17:12
  • I removed `user_data` to avoid `yum` commands. I think SSM will confuse me even more by adding extra complexity. I literally applied [this](https://www.youtube.com/watch?v=pfWd-XNRY7c) and [this](https://www.youtube.com/watch?v=vx6xLxqsDek) manually in AWS Console but interestingly I am still having same problem as Terraform code gives me. What I noticed is that the AWS doc link I referenced in question has only one route table but I have two. Also not sure what that "AWS internal routing node" thing is. – BentCoder Jul 09 '23 at 17:58
  • Sorry, I'm not going to watch a bunch of Youtube videos to help you. If you want to show specific things you did you need to add those details to your question. If you can't get into your servers via `ssh` or `ssm` or something, then you can't debug your instances, and you should focus on being able to debug before doing anything else. If you removed the `user_data` then you don't have a web server installed or running on the EC2 instances, so of course things are going to fail now, and they were probably failing during the install before, which you need to be able to debug. – Mark B Jul 09 '23 at 19:31