For_each loop only with attributes present

Hi Forum,

I like it when I can declare maps in tfvars, and get resource creation ignored when an attribute is absent. Gives a real sense of convenience for the end user, which if often myself.

This relates to the for_each loop and condition based on the network_rules attribute being present

So with this:

storage_account = {
  fddfdfdfdfdfggg = {
    resource_group_key = "aa"
    name               = "fddfdfdfdfdfggg"
    network_rules = {
      default_action = "Deny"
      ip_rules = [
        "2.2.2.2"
      ]
    }
    tags = {
      environment = "experimental"
      owner       = "bar"
    }
  }
  asdsadasdasd = {
    resource_group_key = "aa"
    name               = "asdsadasdasd"
    tags = {
      environment = "experimental"
      owner       = "foo"
    }
  }
}

So I have done this:

resource "azurerm_storage_account_network_rules" "this" {
  for_each = {
    for k, v in var.storage_account : k => v
    if try(v.network_rules, null) != null
  }
  storage_account_id         = azurerm_storage_account.this[each.key].id
  default_action             = try(each.value.network_rules.default_action, null)
  ip_rules                   = try(each.value.network_rules.ip_rules, [])
  virtual_network_subnet_ids = try(each.value.network_rules.virtual_network_subnet_ids, [])
  bypass                     = try(each.value.network_rules.bypass, [])
}

It works, really well actually: but it seems ugly and does not take advantage of existing functions - anyone know something more “terraform - centric”?

Thanks all in advance

Hi @niksheridan1!

The data structure you’ve shown here seems like it’s logically a map of objects rather than a map of maps, because the top level keys are chosen by the caller but the second level attributes are a fixed part of your module’s interface. This distinction is similar to the difference between map keys and class attributes in some other languages.

Given that, I would typically define this variable using an explicit type constraint which specifies exactly which attributes are expected and what types they ought to have, which would mean that the attributes will always be present because otherwise there would be a type mismatch error assigning the value.

In current Terraform (v1.2) there is no way to declare that an attribute is optional, but in the forthcoming v1.3 it will be possible to declare a particular attribute as optional and also optionally specify a default value to use when the caller doesn’t set it. If you don’t specify a default then the default value will be null so you can test it without using error handling functions like try.

v1.3.0-beta1 is currently available for testing in case you’d like to try it. I wouldn’t suggest using the beta version in production, but if you write your module with an exact type constraint today then you can add the optional attribute annotations later once you are ready to use the new version, without the need for any other modifications at that time (since your uses of the variable will already treat null as meaning that the attribute isn’t set.)

1 Like

Hi @apparentlymart !
Thanks so much for the response - I will revisit the docs and I appreciate the hints, I think know where going with this regarding the declaring the variable type as an object, but - no -offence - I do find this a bit awkward to work with sometimes. Any idea when 1.3 might be out as a general release?
nik

For the evaluation you can use if can(v[”network_rules”])

2 Likes