skip to Main Content

Below is the locals sg_rules output I am getting check the value for cidr_blocks and security_group_id variables. At a time either of the values will be

"security_group_id" = tostring(null)

or

"cidr blocks" = tolist([])

Locals Outputs :

sg_rules = {
    "testsg_1-ingress-1521-tcp-10.80.0.10/32" => {
    "cidr blocks" = tolist(["10.80.0.10/32",]) 
    "description" = "1521 tcp ingress" 
    "from_port" = 1521 
    "protocol" = "tcp" 
    "security_group_id" = tostring(null) 
    "sg_name" = "testsg_1"
    "to_port" = 1521 
    "type" = "ingress" 
  },
    "testsg_2-ingress-1524-tcp-sg-23423439" => {
    "cidr blocks" = tolist([]) 
    "description" = "1524 tcp ingress" 
    "from_port" = 1524 
    "protocol" = "tcp" 
    "security_group_id" = "sg-23423439"
    "sg_name" = "testsg_2"
    "to_port" = 1524 
    "type" = "ingress" 
  }
}

using the above local in the aws_security_group_rule resource like below getting conflicting error, that both cidr_blocks and source_security_group_id should not exist at the same time.

resource "aws_security_group_rule" "tcp_cidr_blocks" {
  for_each  = { for key, sg_rule in local.sg_rules : key => sg_rule }
  type      = each.value.type
  from_port = each.value.from_port
  to_port   = each.value.to_port
  cidr_blocks              = each.value.cidr_blocks
  source_security_group_id = each.value.security_group_id
  protocol                 = each.value.protocol
  security_group_id        = aws_security_group.security_groups.id
}

what I am expecting as either of inputs are null, at a time, so it shouldn’t conflict and use either cidr_blocks or source_security_group_id at a time.

Error:

Error: Conflicting configuration argumentsnn with 
module.sg.aws_security_group_ru1e.tcp_cidr_blocks["testsg_2-ingress-1524-tcp-sg-23423439"],n on sg/main.tf line 30, in resource 
"aws_security_group_rule" "tcp_cidr_blocks" : n 30: 
source_security_group_id 
= each.value.security_group_idnn"security_group_id": 
conflicts with cidr_blocksn", 

3

Answers


  1. The second object in your example has:

    • "cidr blocks" = tolist([])
    • "security_group_id" = "sg-23423439"

    An empty list is not null, so for this object both of the arguments are set at once.

    If you set the CIDR blocks attribute to tolist(null) instead then you will have a null value for that argument, which may allow this validation rule to pass.

    This validation rule is implemented by the hashicorp/aws provider in its aws_security_group_rule implementation and so exactly how it will interpret these cases is decided by how the logic in the provider is written — the provider could potentially choose to treat an empty list the same as unset — but a null value for a resource argument is always exactly equivalent to leaving it unset and so making sure at least one of them is really null is the most robust way to configure this, which should work regardless of how the provider plugin is implemented.

    Login or Signup to reply.
  2. There are a couple of issues with the current code, one being the => which is not a valid syntax in Terraform:

    enter image description here

    The second is explicitly casting to types, e.g., tolist(["10.80.0.10/32",]) and tostring(null). If the local variable sg_rules is fixed to look like this:

    sg_rules = {
        "testsg_1-ingress-1521-tcp-10.80.0.10/32" = {
          "cidr_blocks"       = ["10.80.0.10/32", ] # <---- list instead of type casting
          "description"       = "1521 tcp ingress"
          "from_port"         = 1521
          "protocol"          = "tcp"
          "security_group_id" = "" # <---- empty string instead of type casting
          "sg_name"           = "testsg_1"
          "to_port"           = 1521
          "type"              = "ingress"
        },
        "testsg_2-ingress-1524-tcp-sg-23423439" = {
          "cidr_blocks"       = [""] # <---- empty list of strings instead of type casting
          "description"       = "1524 tcp ingress"
          "from_port"         = 1524
          "protocol"          = "tcp"
          "security_group_id" = "sg-23423439"
          "sg_name"           = "testsg_2"
          "to_port"           = 1524
          "type"              = "ingress"
        }
      }
    

    Following that change, using the ternary operator on your code will result in terraform plan working:

    resource "aws_security_group_rule" "tcp_cidr_blocks" {
      for_each                 = local.sg_rules
      type                     = each.value.type
      from_port                = each.value.from_port
      to_port                  = each.value.to_port
      cidr_blocks              = each.value.cidr_blocks != [""] ? each.value.cidr_blocks : null
      source_security_group_id = each.value.security_group_id != "" ? each.value.security_group_id : null
      protocol                 = each.value.protocol
      security_group_id        = aws_security_group.security_groups.id
    }
    

    Setting cidr_blocks or source_security_group_id to null will tell terraform to treat it is an absence of the argument which means it will not complain about conflicting arguments.

    Login or Signup to reply.
  3. I will leave my answer here just as another example of how to work with JSON files (I imported the security group rules first as I was working with existing resources):

    [
     {
        "description": "Remove me",
        "from_port": 500,
        "to_port": 500,
        "protocol": "udp",
        "source_security_group_id": "",
        "cidr_blocks": ["xxx.xx.xx.xxx/xx"]
     },
     {
        "description": "Remove this rule",
        "from_port": 5000,
        "to_port": 7000,
        "protocol": "tcp",
        "source_security_group_id": "sg-xxxx8f180c16xxxx",
        "cidr_blocks": [""]
     }
    ]
    
    locals {
      ingress_security_groups = jsondecode(file("${path.module}/components/ingress_security_groups.json"))
    }
    
    resource "aws_security_group_rule" "ingress_rule" {
      count                    = length(local.ingress_security_groups)
      type                     = "ingress"
      description              = local.ingress_security_groups[count.index].description
      from_port                = local.ingress_security_groups[count.index].from_port
      to_port                  = local.ingress_security_groups[count.index].to_port
      protocol                 = local.ingress_security_groups[count.index].protocol
      source_security_group_id = local.ingress_security_groups[count.index].source_security_group_id != "" ? local.ingress_security_groups[count.index].source_security_group_id : null
      cidr_blocks              = local.ingress_security_groups[count.index].cidr_blocks != [""] ? local.ingress_security_groups[count.index].cidr_blocks : null
      security_group_id        = aws_security_group.this.id
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search