amazon-web-servicesterraformterraform-provider-awscloudposse

"Invalid value for module argument" with list of CIDR blocks


I’m trying to add multiple rules to a cloudposse security group. Here is the relevant code:

module "subnets" {
  source = "cloudposse/dynamic-subnets/aws"
  version = "0.39.8"

  vpc_id              = module.vpc.vpc_id
  igw_id              = module.vpc.igw_id
  cidr_block          = module.vpc.vpc_cidr_block
  availability_zones  = local.az_names
  # nat_gateway_enabled = true

  context = module.this.context
}

module "sg" {
  source = "cloudposse/security-group/aws"
  version = "0.4.3"

  attributes = ["primary"]

  rules = [
    {
      key         = "HTTP"
      type        = "ingress"
      from_port   = 80
      to_port     = 80
      protocol    = "tcp"
      cidr_blocks = module.subnets.public_subnet_cidrs
      self        = null
      description = "Allow HTTP from IPs in our public subnets (which includes the ALB)"
    },
    {
      key         = "SSH"
      type        = "ingress"
      from_port   = 22
      to_port     = 22
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
      self        = null
      description = "Allow SSH from all IPs"
    }
  ]

  vpc_id  = module.vpc.vpc_id
  context = module.this.context
}

This is failing with:

Error: Invalid value for module argument The given value is not suitable for child module variable “rules” defined at .terraform/modules/project_module.sg/variables.tf:60,1-17: element types must all match for conversion to list.

And the problem is the cidr_blocks. If I replace the first one with ["0.0.0.0/0"] it works. I see that the output from the aws-dynamic-subnets module is aws_subnet.public.*.cidr_block. The current value of the cidr_blocks variable in the resource is ["172.16.96.0/19", "172.16.128.0/19"], which sure looks like a list of strings to me. When I open terraform console and ask for the type of public_subnet_cidrs, I just get dynamic. I’ve tried wrapping the output in tolist() and adding an empty string to the cidr_blocks array (to create lists of the same length) in the second ingress rule, but neither changes the error.

I have successfully worked around this issue by using a rule_matrix for the HTTP rule and then also defining rules with a single rule dictionary for the SSH rule, but this feels rather hacky.

What am I doing wrong?


Solution

  • You can use rule_maps, instead of rules:

    module "sg" {
      source = "cloudposse/security-group/aws"
      version = "0.4.3"
    
      attributes = ["primary"]
    
      rules_map = {
            "HTTP" = [{
              key         = "HTTP"
              type        = "ingress"
              from_port   = 80
              to_port     = 80
              protocol    = "tcp"
              cidr_blocks = module.subnets.public_subnet_cidrs
              self        = null
              description = "Allow HTTP from IPs in our public subnets (which includes the ALB)"
            }],
           "SSH" = [{
              key         = "SSH"
              type        = "ingress"
              from_port   = 22
              to_port     = 22
              protocol    = "tcp"
              cidr_blocks = ["0.0.0.0/0"]
              self        = null
              description = "Allow SSH from all IPs"
            }
          ]
        }
    
      vpc_id  = module.vpc.vpc_id
      context = module.this.context
    }
    

    This is a little bit cleaner then using both rules and rule_matrix. Also I'm not sure why just using rules does not work. I guess it does some inner processing of cidr_blocks, and expects them to be exactly same type (list with 3 non-empty string elements).