Required attributes in nested object

Hi,

I’m currently using the Terraform 0.13.5 and fairly new to the product.

// variables.tf in a module
variable "foo" = {
  description = "Demo"
  type = object({
     foo1 = string
     foo2 = object({
         foo2_1 = string                                 // Optional
         foo2_2 = bool                                   // Optional
     })

    foo3 = object({
         foo3_1 = number                             // Required value
         foo3_2 = string                                 // Optional
     })
}

Suppose I wrap the file above and the others in a module, namely foobar", and try to use the module as follows:

// test script (main.tf)
module "foobar" {
  source = "path/to/foobar/"
  
  foo1 = "bar1"
  foo3 = {
      foo3_1= 1
  }
}

The expected output would be that TF accepts the test script, but TF complains that :

The given value is not suitable for child module variable "foo" (...) attribute "foo2" is required.

// (After inserting the foo2 block with null values in the test script)

The given value is not suitable for child module variable "foo" (...) attribute "foo3": attribute "foo3_2" is required.

One way of avoiding this type of error is that assigning a value to every attribute.

// test script (main.tf)
module "foobar" {
  source = "path/to/foobar/"
  
  foo1 = "bar1"
  foo2 = {
      foo2_1 = null                  // or ""
      foo2_2 = false
  }

  foo3 = {
      foo3_1 = 1
      foo3_2 = null                   // or ""
  }
}

This solution seems kind of tiresome and confusing (especially the one that suffices to supply values on either one of the nested blocks or leave them blank). I learnt the optional could be a way of doing this, but this feature is not available in TF 0.13. How should I overcome this?

As you hinted at, this is exactly the use case for optional object attributes and the defaults function added in Terraform 0.15. It might be worth the upgrade!

Here’s what the change to your module would look like:

terraform {
  experiments = [module_variable_optional_attrs]
}

variable "foo" {
  description = "Demo"
  type = object({
    foo1 = string
    foo2 = optional(object({
      foo2_1 = string=
      foo2_2 = bool
    }))

    foo3 = object({
      foo3_1 = number
      foo3_2 = optional(string)
    })
  })
}

With your original test configuration, this would result in a value like:

  foo = {
      foo1 = "bar1"
      foo2 = null
      foo3 = {
          foo3_1 = 1
          foo3_2 = null
      }
  }

If you choose to do so, upgrading from Terraform 0.13 to 0.15 should be straightforward—much easier than 0.12 to 0.13.

Without upgrading, the only other advice I can offer here is to use null as a replacement value for an entirely optional object (i.e. foo2), rather than specifying an object with two null attribute values.