Using EIPs for ALB

trying to create a load balancer with EIPs attached to them.

resource "aws_alb" "alb1" {
  name="lb"
  ... (truncated) ...

  subnet_mapping {
    count = length( data.terraform_remote_state.vpc.outputs.subnets_public )
    subnet_id = data.terraform_remote_state.vpc.outputs.subnets_public[ count.index ]
    allocation_id = element( aws_eip.eip.*.id, count.index ) 
}

So this doesnt work, get an error

The "count" object can only be used in "module", "resource", and "data" blocks, and only when the "count" argument is set.

is there a way to be able to do something dynamically? As I’m multi-region and some regions have less or more subnets than others.

Hi @aloftaizzz,

As per the aws_alb you need a subnet_mapping block for each subnet you are associating. And, in your situation, there is the need to have the number of block vary based upon the number of subnets you need to associated for the aws_alb in a given region.

What you need here is a dynamic block

Which will allow you to loop over a map or set of strings using for_each, creating the required number of blocks.

Hope that helps

Happy Terraforming

Thanks! exactly what I need.

Is there a way to use dynamic setting with index.
because ultimately i need to loop through the subnet id and aws_eip with an index

i’m unsure how to do that. do i try to dynamically create a variable with the subnet id and allocation id? when all i need is a index count

In order to use the for_each you need to pass in a map or a set. Using a set by having the following:

for_each = toset(val.listvariable)

Will not provide you with an index of the item in the list (which you need to have in order to reference other attributes of other resources based upon the index of the value in the original list).
Therefore you will have to construct a map which provides you a way of referencing the index of that value as it was in the list.

This can be done as follows:

mymap = { for idx, val in local.mylist : idx => val }

Which given mylist = ["a", "b", "c", "d", "e"] will produce:

mymap = {
  "0" = "a"
  "1" = "b"
  "2" = "c"
  "3" = "d"
  "4" = "e"
}

Note you could reverse the idx => val to val => idx to have the map key be the string

See the below HCL illustration. You should be able to copy/paste this into a .tf file in a separate directory and run terraform init / apply to see what it is doing and to experiment with changes to the code.

locals {
  mylist = ["a", "b", "c", "d", "e"]
  mymap  = { for idx, val in local.mylist : idx => val }

}

resource "null_resource" "pretend_I_am_a_dynamic_block" {
  for_each = local.mymap
  triggers = {
    mylistvalue  = each.value
    mylistindex  = each.key
    somthingelse = "some.resource[${each.key}].some_attribute"
  }
}

output "mymap" {
  value = local.mymap
}

output "null_resources" {
  value = null_resource.pretend_I_am_a_dynamic_block
}

Hope that helps!

Happy Terraforming

thanks for the pointers,

though likely not going to be what I want and more of a limitation of terraform.

i want to do this dynamically as I have 2 regions with a different number of AZs.

But since I can’t use count for this and have to use local vars to accomplish, it’s a not something i want to do. Though doesnt seem like I have a choice.

But thanks again

Sorry, just picking up on this after being a bit swamped.

My use of locals above for the mylist is just for illustration in the code. This local variable could easily be replaced by an input variable to make it dynamic and passed in at plan/apply time.

The mymap would be constructed dynamically based upon the values in the input variable and this would still be used in the dynamic block as you required.

variable "inputlist" {
  type = list(string)
  default  = ["a", "b", "c", "d", "e"] # <--- Note this is again just for illustration
}

locals {
  # original local  variable in this position replaced by above and reference changed in next line.
  mymap  = { for idx, val in var.inputlist : idx => val }
}

## Remainder of code here