Search code examples
terraformterraform0.12+terraform-provider-kubernetes

Argument must not be null, inside dynamic block


I am using kubernetes_network_policy resource. I have around ten network_poilicyand each of them are different. One policy has only ingress, another one has only egrees, few of them have both ingress and egress. I am getting below error when I have a null value in the dynamic block, is there a way to overcome this error. like only execute the dynamic block if the variable(ingress_number) has some value?

 Error: Invalid function argument
│
│   on main.tf line 16, in resource "kubernetes_network_policy" "example-policy":
│   16:       for_each = range(length(each.value.ingress_number))
│     ├────────────────
│     │ each.value.ingress_number is null
│
│ Invalid value for "value" parameter: argument must not be null.

My Resource


resource "kubernetes_network_policy" "example-policy" {
  for_each = var.inputs
  metadata {
    name      = each.value.name
    namespace = each.value.namespace
  }
  spec {
    pod_selector {
      match_labels = {
        app = each.value.selector
      }
    }
    policy_types = each.value.policy
    dynamic "ingress" {
        
        for_each = range(length(each.value.ingress_number))
        
        content {
            ports {
                port     = each.value.ingress_number[ingress.value]
                protocol = each.value.ingress_protocol[ingress.value]
            }
            
            to {
                namespace_selector {
                    match_labels = {
                        app = each.value.ingress_label
                    }
                }
           } 
      }       
    }      
    dynamic "egress" {
        
        for_each = range(length(each.value.egress_number))
        
        content {
            ports {
                port     = each.value.egress_number[egress.value]
                protocol = each.value.egress_protocol[egress.value]
            }
            
            to {
                namespace_selector {
                    match_labels = {
                        app = each.value.egress_label
                    }
                }
           } 
      }       
    }    
  }
}

My varibale.tf

variable "inputs" {
  type = map(object({
    name            = string
    namespace       = string
    selector        = string
    policy          = list(string)
    ingress_number   = optional(list(string))
    ingress_protocol = optional(list(string))
    ingress_label    = optional(string)
    egress_number   = optional(list(string))
    egress_protocol = optional(list(string))
    egress_label    = optional(string)
  }))
  default = {}
}

My tfvars

  inputs = {

    app1 = {
      name           = "apache"
      namespace       = "default"
      selector        = "apache-app"
      policy          = ["ingrees", "Egress"]
      egress_label    = "example"
      egress_number   = ["443", "8080"]
      egress_protocol = ["TCP", "TCP"]
    }
    app2 = {
      name           = "nignx"
      namespace       = "default"
      selector        = "nignix-app"
      policy          = ["ingrees", "Egress"]
      ingress_label    = "play"
      ingress_number   = ["9080", "9320"]
      ingress_protocol = ["TCP", "TCP"]
      egress_label    = "example"
      egress_number   = ["443", "8080"]
      egress_protocol = ["TCP", "TCP"]
    }
  }

Solution

  • Yes, you should be able to put condition on your for_each:

    for_each = each.value.a == null ? [] : range(length(each.value.a))
    

    or a bit shorter, but more hackish:

    for_each = try(range(length(each.value.a)), [])
    

    You could do other things to check that as well.