1
votes

I would like to use Terraform to combine a list and a map into a set of resource arguments for an AWS security group. For each port in the list of ports, and for each key in the map of people I would like to have an Ingress rule in the security group. So for example I have a (simplified) .tf like this (question marks where I don't know):

variable "IP_Mapping" {
  type = "map"
  default = {
    "bob"          = "1.1.1.1/32"
    "alice"        = "2.2.2.2/32"
  }
}

variable "ingress_ports" {
  type        = list(number)
  description = "list of ingress ports"
  default     = [80, 443]
}

resource "aws_security_group" "sg-vpc" {
  name        = "sd-ocp-vpc_sg"
  description = "Default security group"
  vpc_id      = "${aws_vpc.vpc.id}"

    ingress {
      from_port   = ?
      to_port     = ?
      protocol    = "tcp"
      cidr_blocks = ?
      description = ?
    }
}

And I would like a resulting static resource like this:

resource "aws_security_group" "sg-vpc" {
  name        = "sd-ocp-vpc_sg"
  description = "Default security group"
  vpc_id      = "${aws_vpc.vpc.id}"

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["1.1.1.1/32"]
    description = "bob"
  }
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["1.1.1.1/32"]
    description = "bob"
  }
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["2.2.2.2/32"]
    description = "alice"
  }
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["2.2.2.2/32"]
    description = "alice"
  }
}

I realize that the order of the ingress blocks doesn't matter. I'm also not overly attached to the map/list data structure if there's a better way I'm not seeing, as long as I don't have to list each port for each user (there may be many ports, but they'll always be the same for all users).

I have gotten as far as using dynamic blocks for the ingress rule, and can figure out how to iterate over the map in the dynamic block, but can't for the life of my figure out how to get a nested for loop in a dynamic resource argument to work.

1

1 Answers

1
votes

You can use setproduct and create a helper_list first, before you construct your dynamic block.


locals {

  helper_list = setproduct(
                  var.ingress_ports,
                  [for name, cidr in var.IP_Mapping: [name, cidr]])           
}

local.helper_list should be in the for form of:

[
  [
    80,
    [
      "alice",
      "2.2.2.2/32",
    ],
  ],
  [
    80,
    [
      "bob",
      "1.1.1.1/32",
    ],
  ],
  [
    443,
    [
      "alice",
      "2.2.2.2/32",
    ],
  ],
  [
    443,
    [
      "bob",
      "1.1.1.1/32",
    ],
  ],
]

Then for your dynamic block:

resource "aws_security_group" "sg-vpc" {
  name        = "sd-ocp-vpc_sg"
  description = "Default security group"
  vpc_id      = "${aws_vpc.vpc.id}"

  dynamic "ingress" {

      for_each = {for idx, item in local.helper_list: idx=>item}

      content {
        from_port   = ingress.value[0]
        to_port     = ingress.value[0]
        protocol    = "tcp"
        cidr_blocks = [ingress.value[1][1]]
        description = ingress.value[1][0]
      }    
   }
}