Terraform: Misleading "value is not suitable" with complex Object types

Created on 5 Aug 2019  路  12Comments  路  Source: hashicorp/terraform

Terraform Version

Terraform v0.12.6

Terraform Configuration Files

main.tf:

locals {
  t = {
    not_a_list = ["l", "l"]
    num        = 2
    boolean    = true
  }
}

module "test" {
  source = "./test"
  t      = local.t
}

module/variables.tf:

variable "t" {
  type = object({
    not_a_list = string
    num        = number
    boolean    = bool
  })
} 

Debug Output

https://gist.github.com/dpiddockcmp/ef8b66638498105144f346a99afb4b0b

Expected Behavior

Expecting the error:
The given value is not suitable for child module variable "t" defined at test/variables.tf:1,1-13: attribute "not_a_list": string required.

Actual Behavior

The error message is non-deterministic. Sometimes it correctly complains about not_a_list but it will also randomly complain that num is not a number or boolean is not a bool. When, as far as the user can see, they are.

Steps to Reproduce

  1. terraform init
  2. terraform apply

Additional Context

The error message is highly confusing.

I had messed up by setting an object key to a list(string) when the variable was expecting it to be a string. Terraform was complaining about a different key not being a number when it was.

Doesn't help that repeated runs will randomly pick a key to complain about being the wrong type.

bug cli v0.12

Most helpful comment

However it errors on valid fields when a different field in the object is invalid. There is a bug here.

All 12 comments

Thanks for reporting this unexpected behavior, @dpiddockcmp. As you've observed, terraform validate aborts after the first module variable type mismatch it sees, instead of validating all values (the ordering is, and will likely continue to be, non-deterministic). There are other places where terraform will gather and report _all_ configuration errors before exiting, and it is reasonable to expect the same here.

However it errors on valid fields when a different field in the object is invalid. There is a bug here.

Also faced this issue today banging my head against a wall for about 10 minutes until I realized I had a key mismatch for the complex object I was trying to create.

Word of advice to anyone who will face this issue: double check every key and every value regardless of what the error message says.

Adding @bclodius, make sure all variable exist and no typo, because no variables is optional. you can use 0 for number and "" for string, and check the resulting variable in the plan

This is definitely still happening in 0.12.24. In my case it was an incorrect nested object resource attribute refference.

variable "example_config_map" {
    type = map(object({
        toggle_attribute: bool
        config_a = {
            a = string
            b = string
            c = string
            d = string
        }
        config_b = {
            a = string
            b = string
            c = string
            d = string
        }
    }))
}

Module invoked like this:

module "module-invocation-example" {
    source = "./modules/example"
    example_config_map = {
        first-env-env: {
            toggle_attribute = false
            config_a = {
              a = "one"
              b = "two"
              c = "three"
              d = "${random_string.test-config_a-secret}"
            }
            config_b = {
              a = "one"
              b = "two"
              c = "three"
              d = "${random_string.test-config_b-secret}"
            }
        }
    }
}

d was a reference to a random string created elewhere in terraform. "${random_string.test-config_a-secret}" which turned out to be incorrect and needed to be "${random_string.test-config_a-secret.result}" but the error messages were _completely_ misleading and frustrating as they all took place at the top level of the object hierarchy, about toggle_attribute not being a boolean, and others caused by my deliberately manipulating the structure to try and debug the issue.

Still happening in 0.12.29.
I worked around it by using the items in the exact same order. Nevertheless I think the order shouldn't matter?

I'm getting similar issue in Terraform v0.12.19, am i missing something. I'm writing a modules list entities and each entity associated with a list of aliases

locals {
  # flatten ensures that this local value is a flat list of objects, rather
  # than a list of lists of objects.
  list_aliases = flatten([
    for entity_key, entity in var.entities : [
      for alias_key, alias in entity.aliases : {
        name = alias.name
      }
    ]
  ])
}
resource "vault_auth_backend" "b" {
  provider = vault.this

  for_each = {
    for alias in local.list_aliases :
    alias.name => alias
  }

  type = each.value.type
  path = each.value.auth_path
}



md5-ab501d436c358ca265b2a2e4052aba09



resource "vault_identity_entity_alias" "alias" {
  provider = vault.this

  for_each = {
    for alias in local.list_aliases :
    alias.name => alias
  }

  name           = each.key
  mount_accessor = lookup(vault_auth_backend.b[each.key], "accessor", null)
  canonical_id   = vault_identity_entity.entity[each.value.entity].id
}



md5-f5bc55b4cd79e638a462b611ed1ba838



variable "entities" {
  description = "A collection of entities where each entity is associated with a list aliases "
  type = list(object({
    name     = string
    policies = list(string)
    metadata = map(string)
    aliases = map(object({
      name      = string
      entity    = string
      auth_path = string
      type      = string
    }))
  }))
}



md5-e287ac033d1f56cad3941e4785bdd5e2



Error: Invalid value for module argument

  on identity.tf line 7, in module "vault_foo_entity":
   7:   entities = [
   8:     {
   9:       name     = "vault-foo",
  10:       policies = ["filtered_policy"]
  11:       metadata = {
  12:         "metadata-1" = "value-1"
  13:         "metadata-2" = "value-2"
  14:       }
  16:       aliases = [
  17:         {
  18:           "name"      = "aws-foo"
  19:           "entity"    = "vault-foo"
  20:           "auth_path" = "aws"
  21:           "type"      = "aws"
  22:         },
  23:         {
  24:           "name"      = "ldap-foo"
  25:           "entity"    = "vault-foo"
  26:           "auth_path" = "ldap"
  27:           "type"      = "ldap"
  28:         }
  29:       ]
  30:     }
  31:   ]

The given value is not suitable for child module variable "entities" defined
at .terraform/modules/vault_dba_entity/variables.tf:1,1-20: element 0:
attribute "aliases": map of object required.

In your case I think the error message is correct. You have defined the aliases variable as a map(object(...)) and then passed in a list(object(...))

You have a mismatch in your logic. Do you want a list or a map?

Using:
Terraform v0.12.29

  • provider.aws v3.2.0
    Terraform Enterprise Cloud

I am seeing this trying to read a variable that is a map of list of object. I've tried the syntax in different ways and all have similar errors.

From variables.tf:

variable buckets_read {
  description = "List of bucket ARNs to which the glue job has read access."
  default = {
    prd = []
    pre = []
    sit = []
    dev = []
  }
  type = map(list(object({
    arn     = string,
    key_arn = string
  })))
}

I have 3 variables with this setup, and they all fail. when attempting to use them in a module.

From main.tf:

module prd {
  source             = "./glue"
  atlas              = var.atlas
  buckets_read       = var.buckets_read["prd"]
  buckets_read_write = var.buckets_read_write["prd"]
  runtime            = "prd"
  database_name      = var.database
  role_name          = var.role
  assume_ssm         = var.assume_ssm
  ssm_arns           = var.ssm_arns["prd"]

  providers = {
    aws            = aws.acct
    aws.replicated = aws.acct_replicated
  }
}

An attempted plan with this syntax gives this error:

Error: Invalid value for module argument

  on main.tf line 17, in module "prd":
  17:   buckets_read       = var.buckets_read["prd"]

The given value is not suitable for child module variable "buckets_read"
defined at glue/variables.tf:16,1-22: map of list of object required.

An attempted plan with a lookup gives a different error:

Error: Invalid function argument

on main.tf line 17, in module "prd":
  17:   buckets_read      = lookup(var.buckets_read, ["prd"])

Invalid value for "key" parameter: string required.

And not using [] around your list name gives this:

Error: Invalid value for module argument

  on main.tf line 17, in module "prd":
  17:   buckets_read      = lookup(var.buckets_read, "prd")

The given value is not suitable for child module variable "buckets_read"
defined at glue/variables.tf:16,1-22: map of list of object required

How am I supposed to access the map of list of object?

Using:
Terraform v0.12.29

  • provider.aws v3.2.0
    Terraform Enterprise Cloud
    How am I supposed to access the map of list of object?

This was fixed. The glue module only wants a list of object, the prd module wants the map of list of object.

@jslovato-nutrien Its not entirely clear from your example if you have the same issue here, or just a misconfiguration. You didn't include a complete example, as it looks like you (sensibly) omitted some sensitive data, but that makes it harder to tell if your comment is the same issue or not. Also this issue is about terraform _warning about the wrong thing_, ( To use my own example, the mistake was inside config_a and config_b but terraform said the issue was with toggle_attribute. ) and your second comment makes it sound like your issue was a misconfiguration where terraform gave you the warning/error about the correct variable. Can you clarify?

Also this issue is about terraform _warning about the wrong thing_, ( To use my own example, the mistake was inside config_a and config_b but terraform said the issue was with toggle_attribute. ) and your second comment makes it sound like your issue was a misconfiguration where terraform gave you the warning/error about the correct variable. Can you clarify?

Terraform was very much looking for a map of lists of objects. My maps bucket_read and buckets_read_write had two lists, but had multiple items within each list, as it is a list.

So map buckets_read with key of "prd" gave us single value of arn which is a list, but seen as a single object.

arn = "[arn1, arn2, arn3, etc...]"

AWS now complains that the arn is not valid, because it isn't. It's all the arns mashed together into the single object.

So the TF data structure worked as designed, but my input just needs massaging. I still haven't figured out how to flatten the list that is given, so that it gives me multiple objects, not just one big object.

Inside the data of the aws_iam_policy_document, I have this to read the list:

dynamic statement {
for_each = length(local.buckets_read) == 0 ? [] : [""]

content {
  sid = "S3ObjectRead"
  resources = flatten(concat(
    formatlist("%s/*", [for bucket in local.buckets_read : bucket.arn]),
    [for bucket in local.buckets_read : bucket.key_arn]
  ))

I'm thinking I need to so something like this before I feed it to the module. Also, I didn't create this module, it was handed to me and the original author hasn't had time to look into it.

Hope this explains what is going on over here.

Was this page helpful?
0 / 5 - 0 ratings