skip to Main Content

Im facing a problem where i can access my two ec2 instances from the public subnet just fine individually by their IPs, but not from behind the loadbalancer, where i get a 504. Both the ec2 instances and the loadbalancer use the same security group – which allow a single ip on for ingress

resource "aws_security_group" "security_group" {
  name   = "${var.name}_security_group"
  vpc_id = var.vpc_id
    
  ingress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = var.sg_allowable_ips #for now this is a list of a single IP
  }
    
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}


resource "aws_instance" "instance" {
  for_each               = var.instance_specs
  instance_type          = each.value.instance_type
  ami                    = each.value.ami
  key_name               = aws_key_pair.auth_key.id
  vpc_security_group_ids = [aws_security_group.security_group.id]
  subnet_id = var.subnets[each.value.subnet_key].id
}

The issue is that security group. If in AWS console i add the loadbalancer ip to the security group’s allowable ingress ips, im then able to get to my instances from the loadbalancer just fine.
So my question is: using terraform, how can i allow the loadbalancer to access these instances if i dont yet know it’s ip and it is the last thing created in my terraform script?

UPDATE:: the order of how im creating the resources. notice the circular dependency between ec2 and alb modules…the ec2 module create and exposes the security group, which alb also uses. given that my security group has a hardcoded ingress ip and is created before the alb, i have no opportunity to dynamically add the alb to the security group ingress:

module "vpc" {
  source          = "./modules/vpc"
  name_prefix     = var.name_prefix
  tags            = var.tags
  vpc_cidr        = var.vpc_cidr
  public_subnets  = var.public_subnets
  private_subnets = var.private_subnets
}
    
module "ec2" {
  source           = "./modules/ec2"
  name_prefix      = var.name_prefix
  tags             = var.tags
  dev_host_os      = var.dev_host_os
  vpc_id           = module.vpc.vpc_id
  auth_key_file    = var.auth_key_file
  subnets          = module.vpc.public_subnets
  instance_specs   = local.instance_specs
  sg_allowable_ips = var.sg_allowable_ips
}
    
module "alb" {
  source             = "./modules/alb"
  name_prefix        = var.name_prefix
  tags               = var.tags
  security_group_ids = module.ec2.security_group_ids
  subnet_ids         = values(module.vpc.public_subnets)[*].id
  vpc_id             = module.vpc.vpc_id
  instances          = module.ec2.instances
  #alb_certificate_arn =
}

2

Answers


  1. Chosen as BEST ANSWER

    the solution here was to create a security group for the load balancer which restricts the ip and port however i desire. Then create a separate security group for my instances which accept only port 80 from the alb using an aws_security_group_rule. in this way the relationship is setup among security groups, not actual resource ids or ips - resolving the chicken and egg issues with resource's dynamic ips.

    @marko-e: i think some of this you were alluding to, it just wasnt clicking together. i thank you for iterating over this solution w/ me

    resource "aws_security_group" "ec2_security_group" {
      name   = "${var.name}_security_group"
      vpc_id = var.vpc_id
    
      ingress {
        from_port   = 0
        to_port     = 0
        protocol    = "-1"
        cidr_blocks = ["0.0.0.0/0"]
      }
    
      egress {
        from_port   = 0
        to_port     = 0
        protocol    = "-1"
        cidr_blocks = ["0.0.0.0/0"] 
      }
    }
    
    resource "aws_security_group_rule" "allow_lb_only" {
      type                     = "ingress"
      from_port                = 80
      to_port                  = 80
      protocol                 = "tcp"
      source_security_group_id = "${var.lb_security_group_id}"  #security group that handles the load balancer
      security_group_id        = "${aws_security_group.ec2_security_group.id}" #ec2 instance security group
    }
    

  2. What I would do is use the reference to the ALB security group instead of using the IP address range, since it can change. So that would mean changing the ingress part of the code to look something like:

    resource "aws_security_group" "security_group" {
      name   = "${var.name}_security_group"
      vpc_id = var.vpc_id
        
      ingress {
        from_port       = 0
        to_port         = 0
        protocol        = "-1"
        security_groups = [ aws_security_group.alb.id ] # or however many SGs there are
      }
        
      egress {
        from_port   = 0
        to_port     = 0
        protocol    = "-1"
        cidr_blocks = ["0.0.0.0/0"]
      }
    }
    

    Note that it also might be better to use a separate security group rule resource for defining ingress and egress rules.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search