Skip to content

Defaults bug when nested type is map(string) #31177

@theherk

Description

@theherk

Terraform Version

Terraform v1.2.1
on darwin_amd64

Terraform Configuration Files

terraform {
  experiments = [module_variable_optional_attrs]
}

variable "things" {
  type = list(object({
    inner = optional(object({
      a = optional(string)
      b = optional(map(string))
    }))
  }))
}

locals {
  things_with_defaults = defaults(var.things, {
    inner = {
      # If this is uncommented: error:
      # Invalid value for "defaults" parameter: .inner.b: invalid default value for string: string required.
      # But the type is a map(string), so {} should be fine, right?
      # b = {}
    }
  })

  inners = [for v in local.things_with_defaults : v.inner]
}

output "inners" {
  value = local.inners
}

output "things" {
  value = [for v in local.inners : v.b != {} ? v.b : { "override" = "map value" }]
}

terraform.tfvars

things = [
  {
    inner = {
      a = "something"
    }
  },
  {
    inner = {
      b = { "want" = "bar" }
    }
  },
]

Debug Output

Expected Behavior

Should have given:

Changes to Outputs:
  + inners = [
      + {
          + a = "something"
          + b = {}
        },
      + {
          + a = null
          + b = {
              + "want" = "bar"
            }
        },
    ]
  + things = [
      + {
          + "override" = "map value"
        },
      + {
          + "want" = "bar"
        },
    ]

Actual Behavior

Actually yields:

Changes to Outputs:
  + inners = [
      + {
          + a = "something"
          + b = {}
        },
      + {
          + a = null
          + b = {
              + "want" = "bar"
            }
        },
    ]
  + things = [
      + {}, # this is the issue
      + {
          + "want" = "bar"
        },
    ]

Steps to Reproduce

terraform init
terraform plan

Additional Context

This example is contrived, and therefore may not seem practical, but the actual use case is more complex. This is just the minimum reproducible example.

I understand this is still being evaluated as noted recently in #19898, but I use it currently and this case I'm not sure the workaround.

You can see in the output inners that b is clearly {}, so why can't we seem to match that?

I even tried setting the inner.b default to {}, but then I get the error Invalid value for "defaults" parameter: .inner.b: invalid default value for string: string. I find this even more baffling. This isn't a string. It is map(string), so {} should be a valid value, right?

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugnewnew issue not yet triaged

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions