Terraform: Argument expansion error in output expression during validation walk

Created on 9 Aug 2019  ·  13Comments  ·  Source: hashicorp/terraform

Terraform Version

$ terraform version
Terraform v0.12.7-dev

Terraform Configuration Files

variable "mapping" {
  default = {
    "a" = ["b", "c"]
  }
}

locals {
  mapping = [
    for x, y in var.mapping : [
      for z in y : {
        "${z}" = x
      }
    ]
  ]
}

output "x" {
  value = merge(flatten(local.mapping)...)
}

Debug Output

Crash Output


https://gist.github.com/apparentlymart/e1e86b3299c679df1b66d64e2ee86fb5

Expected Behavior

The value for output x after applying should be:

{
  "b" = "a"
  "c" = "a"
}

Actual Behavior

Terraform produces an error during the validation walk:

Error: Invalid expanding argument value

  on expand-arguments-error.tf line 18, in output "x":
  18:   value = merge(flatten(local.mapping)...)

The expanding argument (indicated by ...) must be of a tuple, list, or set
type.

Steps to Reproduce

Using the above configuration, run terraform apply

Additional Context

Interestingly, evaluating the same expression in terraform console works as expected:

$ terraform console
> merge(flatten(local.mapping)...)
{
  "b" = "a"
  "c" = "a"
}

The console is evaluating expressions in a different mode than in the validate walk, so perhaps there's something unusual about the validate walk that is making this fail.

This bug is likely to be upstream in the HCL repository, e.g. in the FunctionCallExpr evaluation logic. It looks like this isn't correctly handling the case where the expression to expand is cty.DynamicVal, so perhaps that's the root cause; if so, adding cty.DynamicPseudoType to the set of allowed types is probably enough to fix it, because there's already a test in there for if the value is unknown. If this theory does turn out to be true then this'll be a PR in the HCL repository first, and then a vendor update PR in here.

This value would be unknown here during the validation walk because it's derived from a variable, and variables don't get concrete values until the plan walk.

References

This was originally reported in a community forum topic.

bug config confirmed v0.12

Most helpful comment

Bumped into this. Currently I'm working around this by...

merge(flatten([local.mapping])...)

or in this case maybe a double flatten? but a single flatten is actually sufficient for me.

All 13 comments

Bumped into this. Currently I'm working around this by...

merge(flatten([local.mapping])...)

or in this case maybe a double flatten? but a single flatten is actually sufficient for me.

Bug is still present in

$ terraform version
Terraform v0.12.16

Error still present in v0.12.17, can anyone provide a solution or workaround ?

locals {
outputvnets = "${flatten(data.azurerm_virtual_network.testvnet[*].address_space)}"
}
variable "SUBNET_MASK" {
description = "address prefix for the vnet to be created. Examples: 17,18,19,20,21,22,23,24,25,26,27,28,29"
#type = number
default = 23
}
locals {
nexthop = "${var.SUBNET_MASK - 16}"
}
locals {
subnetlist = [for s in range(1,pow(2,local.nexthop)) : contains(local.outputvnets,cidrsubnet("10.1.0.0/16",local.nexthop,s)) ? "" : cidrsubnet("10.1.0.0/16",local.nexthop,s)]
}

Error: Invalid expanding argument value
on outputs.tf line 30, in output "myvnets":
30: value = coalesce("${local.subnetlist}"...)
The expanding argument (indicated by ...) must be of a tuple, list, or set
type.

PS C:\Users\vn50est\Source\Reposterraform-azurerm-vnet> terraform version
Terraform v0.12.17

  • provider.azurerm v1.37.0

Seeing something similar in 0.12.18. As far as I can tell, the only difference between the two examples is whether the follow data structure comes from a local or a variable

target_groups = {
  tg1 = ["api", "www"]
}

Working

locals {
  target_groups = {
    tg1 = ["api", "www"]
  }

  tuple_of_maps = [
    for target_group, domains in local.target_groups :
    { for domain in domains : domain => target_group }
  ]

  domains = merge(local.tuple_of_maps...)
}

resource "null_resource" "this" {
  for_each = local.domains
}

output "resources" {
  value = [for nr in null_resource.this : nr.id]
}

output:

null_resource.this["www"]: Creating...
null_resource.this["api"]: Creating...
null_resource.this["api"]: Creation complete after 0s [id=3280095703150378859]
null_resource.this["www"]: Creation complete after 0s [id=6840764206472798878]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

resources = [
  "3280095703150378859",
  "6840764206472798878",
]

Not Working

variable "target_groups" {
  default = {
    tg1 = ["api", "www"]
  }
  type = map(list(string))
}

locals {
  tuple_of_maps = [
    for target_group, domains in var.target_groups :
    { for domain in domains : domain => target_group }
  ]

  domains = merge(local.tuple_of_maps...)
}

resource "null_resource" "this" {
  for_each = local.domains
}

output "resources" {
  value = [for nr in null_resource.this : nr.id]
}

output:

Error: Invalid expanding argument value

  on test.tf line 20, in locals:
  20:   domains = merge(local.tuple_of_maps...)

The expanding argument (indicated by ...) must be of a tuple, list, or set
type.

@leoskyrocker's workaround appears to fix my contrived example.

Huh, looks like I was really tired yesterday when I effectively reposted the original example without recognizing I'd done so 🤦‍♂ .

Could this bug be related to https://github.com/hashicorp/terraform/issues/22576?

Bumped into this. Currently I'm working around this by...

merge(flatten([local.mapping])...)

or in this case maybe a double flatten? but a single flatten is actually sufficient for me.

@leoskyrocker You are a savior. I just spent hours trying to figure out was wrong with my expressions. Can confirm this workaround works and saved my sanity.

terraform 0.12.20 - still not works. The workaround still works (the formula above)

I believe this should be fixed by hashicorp/hcl#386 once Terraform's HCL dependency is upgraded to a version including that change.

I'm on version v0.12.26.

I don't think the workaround is working for me :'(

  splunk_vault_secrets_in = flatten(flatten(flatten(flatten([
    for i, splunk in local.splunk :
    {
      "splunk_${i}_token" = splunk.token
    }
  ]))))
  splunk_vault_secrets = merge(flatten(flatten(local.splunk_vault_secrets_in))...)
Error: Invalid expanding argument value

  on ../../modules/role-kube/main.tf line 212, in locals:
 212:   splunk_vault_secrets = merge(flatten(flatten(local.splunk_vault_secrets_in))...)

The expanding argument (indicated by ...) must be of a tuple, list, or set
type.

Passing the list through flatten() does not resolve the issue for me, but passing it through a utility module that calls merge does. I called it merge_list.

 variable data {
    type = list(any)
  }

 locals {
   result = merge(var.data...)
 }

 output result {
   value = local.result
 }

module merge_list {
  source = ".../merge_list"

  data = local.my_list_to_merge
}

locals {
  my_merged_list = module.merge_list.result
}

@web-vertalo @iridian-ks I think, because this is a weird syntax-parsing error, part of the key to the workaround is wrapping your value in a list literal before flattening and merging: flatten([this]), not flatten(this). Maybe you've tried that, or maybe I'm wrong. But I noticed at leasts @iridian-ks's example was missing that.

Ran in to this with Terraform 0.12.28 on Linux x86_64 with the following snippet:

locals {
  azs           = length(data.aws_availability_zones.azs.names)
  total_subnets = (var.private_subnets + var.public_subnets)
  newbits       = [for i in range(local.total_subnets) : ceil(log(local.total_subnets, 2))]
  subnet_cidrs = cidrsubnets(var.cidr_block, local.newbits...)
}

Changing the last line to

  subnet_cidrs = cidrsubnets(var.cidr_block, flatten([local.newbits])...)

fixes the issue.

Also the original code (without flatten([])) works in v0.13.0-beta2.
Thanks to @leoskyrocker for the workaround, and to all Terraform devs for continued awesome work!

I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues.

If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

Was this page helpful?
0 / 5 - 0 ratings