1

I have a use case where I wanted to create 2 application load balancer, one will be public another will be private using terraform modules.

I know that we can create 2 directories with the same sort of code for private and public the parameters accordingly but I was trying to create both load balancer by using interpolation of terraform as mentioned below.

ALB Module:

resource "aws_alb" "default" {
  name            = "${var.name}-${var.env_name}-${var.internal == "false" ? "public" : "private" }"
  internal        = "${var.internal == "false" ? "false" : "true" }"
  security_groups = ["${var.internal == "false" ? var.sg80 : var.instance_in_all }"]
  subnets         = ["${var.internal == "false" ? var.public_subnets : var.private_subnets }"]
}

main.tf from where I am calling alb module.

module "public-alb" {
      source         = "../../modules/alb"
      name           = "example"
      internal       = "false"                         #internal: Give it false for public load balancer.
      env_name       = "production"
      vpc_id         = "${module.vpc.vpc_id}"
      public_subnets = "${module.vpc.public_subnets}"
      private_subnets = "${module.vpc.public_subnets}" #This does not matter here because check condition in internal file.
      sg80           = "${module.security-group.sg80}"
      instance_in_all = "${module.security-group.instance_in_all}" #This does not matter here because check condition in internal file.
    }

module "private-alb" {
      source         = "../../modules/alb"
      name           = "example"
      internal       = "true"                          #internal: Give it false for public load balancer.
      env_name       = "production"
      vpc_id         = "${module.vpc.vpc_id}"
      private_subnets = "${module.vpc.public_subnets}"
      public_subnets = "${module.vpc.public_subnets}" #This does not matter here because check condition in internal file.
      sg80           = "${module.security-group.sg80}" #This does not matter here because check condition in internal file.
      instance_in_all = "${module.security-group.instance_in_all}" 
    }

So for the public load balancer, I have to pass private subnets and internal security group same with private load balancer I have to pass public subnets and external security group because I am passing these variables from variables.tf(mentioned below) which is not necessary.

variable "vpc_id" {}

#variable "private_subnets" {   type        = "list"}
variable "sg80" {}

variable "public_subnets" {
  type = "list"
}

variable "name" {}

variable "internal" {}

variable "env_name" {}

variable "private_subnets" {
    type  = "list"
}

variable "instance_in_all" {}

I wanted to know is it the right way to do it or the separate directory is the only workaround as of now.

mohit
  • 2,325
  • 23
  • 48

1 Answers1

1

A couple of possible scenario's:

1) Maximum configurability: I would not expose both variables required for public and private use. Just one var called "subnets" and assign a value to the variable from outside the module. Also, when passing both private_subnets and public_subnets, if you were to use the module in an environment that only contains a public load balancer, you'd have to somehow work around passing the private subnets and security groups, hampering reusability.

2) Less boilerplate, which is how I'm interpreting your question: Using data sources inside the modules. If you want full autonomy (e.g. only pass internal = true/false), and you have fixed subnets and security groups for these scenario's, you can fetch them using data sources, where the query depends on wether var.internal is true or false.

Example:

data "aws_security_groups" "private_lb" {
  tags {
    SomeTag = "something_that_accurately selects my private security groups"
  }
}

data "aws_security_groups" "public_lb" {
  tags {
    SomeTag = "something_that_accurately selects my public security groups"
  }
}

resource "aws_alb" "default" {
  name = "${var.name}-${var.env_name}-${var.internal == "false" ? "public" : "private" }"
  internal = "${var.internal == "false" ? "false" : "true" }"

  security_groups = ["${var.internal == "false" ? data.aws_security_groups.public_lb.ids : data.aws_security_groups.private_lb.ids }"]

  etc...
}

Of course you could also put the conditional part inside the data source itself, e.g. change the filters based on var.internal.

A third option could be to create a dedicated security group inside your module, and assign default ingress/egress rules based on var.internal, exposing the ID of that group in your output so that you can add additional rules to it from outside the module.

Tiemen
  • 816
  • 5
  • 9
  • Yes, I thought about both of these solutions but these have their merits and demerits for example in second solution the resource doesn't exist as of now so when it will look for data then it will fail. Also, as of now the condition support is not available. So, I decided to make different folders for both of them unfortunately. – mohit Dec 04 '18 at 07:32
  • I have had similar scenario's where I created generic modules. I solved it by implementing a count parameter magic on the data sources. With a count of 0, the query won't be executed. Terraform might fail because one of the data sources returns an empty list in that case, which is why I used some ```coalescelist``` interpolation to prevent these errors. – Tiemen Dec 04 '18 at 10:11