Search code examples
terraform

Conditionally add property to object


I wish to add ingress rules to a security group, and I need to allow users to do this through variables while specifying only the name, description and either a cidr_block or a security_group_id.

I have added a variable, set a value for that variable, and am attempting to create a local variable that I can then use in my (specifically within an EKS module definition).

However, I'm encountering an error Null values are not allowed for this attribute value. when the aforementioned local variable is created, which I believe is because variable in within the EKS module aren't nullable.

So given the following code, how can I amend my local variable to totally ignore either cidr_block or security_group_id if they are not specified, rather than setting the value to null?


From variables.tf

variable "ingress_rules" {
  description = "Ingress rules to add to the application security group."
  type = set(object({
    name                     = string
    description              = string
    cidr_block               = optional(string)
    source_security_group_id = optional(string)
  }))
  default = []
}

From tfvars file

ingress_rules = [
  {
    name        = "dev_vpc"
    description = "Dev VPC"
    cidr_block  = "99.99.99.99/28"
  },
  {
    name                     = "cicd_sg"
    description              = "CI/CD server security group"
    source_security_group_id = "sg-00000000000000000"
  }
]

From main.tf

locals {
  ingress_rules = {
    for ingress in var.ingress_rules : ingress.name => {
      description              = ingress.description
      type                     = "ingress"
      protocol                 = "tcp"
      from_port                = 443
      to_port                  = 443
      cidr_blocks              = [lookup(ingress, "cidr_block", null)]
      source_security_group_id = [lookup(ingress, "security_group_id", null)]
    }
  }
}

Full error message

╷
│ Error: Null value found in list
│ 
│   with module.eks[0].aws_security_group_rule.cluster["cicd_sg"],
│   on .terraform/modules/eks/main.tf line 210, in resource "aws_security_group_rule" "cluster":
│  210:   cidr_blocks              = lookup(each.value, "cidr_blocks", null)
│ 
│ Null values are not allowed for this attribute value.

Solution

  • In general the value you want for unset complex types would be an empty constructor e.g. [] or {}. Expanding on the suggestion by MarkoE arrives at the following solution:

    cidr_blocks              = ingress.cidr_block == null ? [] : [ingress.cidr_block]
    source_security_group_id = ingress.source_security_group_id == null ? [] : [ingress.source_security_group_id]
    

    Alternatively your original type is optional(string) and therefore a list type in your locals would be unnecessary, and you can remove the constructor:

    cidr_blocks              = lookup(ingress, "cidr_block", null)
    source_security_group_id = lookup(ingress, "security_group_id", null)
    

    However, while the usage of these values is unspecified in the question, I would imagine these are for inputs to the terraform-aws-eks module requiring either list(string or set(string) types for the values. In that case the above suggestion would be invalid. In that situation you could also:

    cidr_blocks              = ingress.cidr_block == null ? null : [ingress.cidr_block]
    source_security_group_id = ingress.source_security_group_id == null ? null : [ingress.source_security_group_id]
    

    Note the coalesce function is somewhat relevant here, but I believe cannot improve the code. I would be super interested in seeing a clever solution with it though.