Terraform: Populate an object with reasonable, then partially override?

Viewed 1469

I am looking for a way to have a variable of type "object" (to force a certain structure), and then override certain parts. consider this variable declaration:

variable "prod_vpc" {
  type = object({
        name = string,
        region = string,
        single_nat_gw = bool,
        create = bool,
        supernet = string
    })

  default = {
    name = "PROD"
    region = "eu-central-1"
    single_nat_gw = true
    create = false,
    supernet = "0.0.0.0/0"
  }
}

in this case, i might want to have this in my auto.tfvars:

prod_vpc = {
    create = true,
    supernet = "10.0.8.0/24"
}

because all other values are good as they are. the only way i can think of is to do this is with type = map(string) and conditionals in a locals block, i.e.:

variable "vpc_defaults" {
  type = object({
        name = string,
        region = string,
        single_nat_gw = bool,
        create = bool,
        supernet = string
    })

  default = {
    name = "PROD"
    region = "eu-central-1"
    single_nat_gw = true
    create = false,
    supernet = "0.0.0.0/0"
  }
}


variable "prod_vpc" {
  type = map(string)
  default = {}
}

then

prod_vpc = {
    create = true,
    supernet = "10.0.8.0/24"
}

and then:

locals {
    create = coalesce(var.prod_vpc["create"], vpc_defaults.create)
}

(i haven't tried this, but i think something along these lines should work)

any other ideas for pre-defining defaults and only overriding when needed?

regards

ruben

1 Answers

Even if you had used a plain map rather than object type, it would not have been possible to do this in this way, and the reasoning is set out in the docs here. It is suggested there that you should use locals instead to provide the defaults and then explicitly merge them.

Meanwhile, as noted by James Bardin in this related GitHub issue, the object keys are required in order to set a default. If you want those to be null, then you have to explicitly set them as such.

James added:

You also have the option of setting the default as null (or not having a default, and requiring the caller set a value). In that case however, you need to avoid passing a null object into merge, which you can do using a condition or coalesce:

merged_map_keys = merge(local.default_map_keys, coalesce(var.prod_vpc, {}))

It would be nice if you could do something like this:

variable "prod_vpc" {
  type = object({
    name          = string,
    region        = string,
    single_nat_gw = bool,
    create        = bool,
    supernet      = string
  })
  default = {}
}

locals {
  default_map_keys = {
    name          = "PROD"
    region        = "eu-central-1"
    single_nat_gw = true
    create        = false,
    supernet      = "0.0.0.0/0"
  }
  merged_map_keys = merge(local.default_map_keys, var.prod_vpc)
}

Then you could call it with:

prod_vpc = {
  create   = true,
  supernet = "10.0.8.0/24"
}

There is more info in the open GitHub issue.

Related