terraform icon indicating copy to clipboard operation
terraform copied to clipboard

Misleading error message for an incorrect dynamic block iterator

Open acdha opened this issue 2 years ago • 3 comments

Terraform Version

Terraform v1.6.2
on darwin_arm64
+ provider registry.terraform.io/cloudflare/cloudflare v4.8.0

Terraform Configuration Files

terraform {
  required_version = ">= 1.5.0"

  required_providers {
    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = "~> 4.0"
    }
  }
}

data "cloudflare_zone" "example" {
  name = "example.org"
}

locals {
  example1_ip_ranges = [
    "1.2.3.4/32",
    "1.2.3.4/32",
  ]

  example2_ip_ranges = [
    "1.2.3.4/32",
    "1.2.3.4/32",
  ]
}

resource "cloudflare_zone_lockdown" "example" {
  zone_id = data.cloudflare_zone.example.zone_id

  urls = ["https://*.example.org"]

  dynamic "configurations" {
    for_each = concat(local.example1_ip_ranges, local.example2_ip_ranges)
    iterator = "client"

    content {
      target = "ip_range"
      value  = client.value
    }
  }
}

Debug Output

n/a

Expected Behavior

It should have told me that "client" was an invalid value for the iterator argument. As soon as I de-quoted it, everything works as expected.

Actual Behavior

╷
│ Error: Insufficient configurations blocks
│ 
│   on zonelockdown.tf line 13, in resource "cloudflare_zone_lockdown" "example":
│   13: resource "cloudflare_zone_lockdown" "example" {
│ 
│ At least 1 "configurations" blocks are required.
╵
╷
│ Error: Unknown variable
│ 
│   on zonelockdown.tf line 19, in resource "cloudflare_zone_lockdown" "example":
│   19:     for_each = concat(local.example1_ip_ranges, local.example2_ip_ranges)
│ 
│ There is no variable named "local".
╵
╷
│ Error: Unknown variable
│ 
│   on zonelockdown.tf line 19, in resource "cloudflare_zone_lockdown" "example":
│   19:     for_each = concat(local.example1_ip_ranges, local.example2_ip_ranges)
│ 
│ There is no variable named "local".

Steps to Reproduce

terraform validate

Additional Context

No response

References

No response

acdha avatar Oct 24 '23 19:10 acdha

Thanks for reporting this, @acdha!

Unfortunately the error message you included has a code snippet that doesn't match the source code you shared. I assume that's because you've modified whatever you actually tried in order to share it. So we can make sure that we are reproducing what you intended to report, can you share an example that reproduces the problem without needing any modifications first, and the error message you saw when you tried exactly that code?

Thanks again!

apparentlymart avatar Oct 24 '23 20:10 apparentlymart

@apparentlymart I updated the example and response with a more carefully redacted version. Now it's a single file including the data lookup I was using and the provider versions.

acdha avatar Oct 25 '23 00:10 acdha

Thanks, @acdha!

Using the configuration you shared I reproduced exactly the behavior you observed, without any modifications. (The fact that this is visible using terraform validate means that a functioning CloudFlare account isn't required to reproduce the problem.)

This bug actually seems to belong to the upstream HCL library rather than to Terraform itself, since that's where this idea of dynamic blocks is implemented. However, from reading the relevant HCL source code I've not yet completely determined why this occurs, so further debugging is needed here.

The relevant parts I've reviewed so far:

  • Interpreting the iterator argument as a "traversal" (a reference expression) and returning early with errors if that interpretation fails.

    Specifically, I would expect this to produce the detail message "A single static variable reference is required: only attribute access and indexing with constant keys. No calculations, function calls, template expressions, etc are allowed here.". (Relevant error handling)

  • The caller of decodeSpec should notice the returned error, append it to the growing diags sequence, and then continue processing any other dynamic blocks. There are no other blocks in this example, so iteration should end here.

I have a hunch that Terraform's way of using this HCL feature might be contributing. Terraform decodes resource blocks in two phases:

  1. PartialContent to find the various "meta-arguments" that are handled by Terraform Core itself, such as count, for_each, depends_on, etc.
  2. Much later on, Content to collect up the rest of the content that's ultimately interpreted by the associated provider rather than by Terraform Core.

If I recall correctly, the "dynamic block" expansion happens just before the second evaluation, in which case the first evaluation would not be exercising this "dynamic block" code in HCL at all, and the expansion would instead be happening on the remnants of the original body that weren't consumed by PartialContent.

Therefore it isn't clear to me exactly why that two-step decoding process should cause a problem here, but it could potentially be.

I hope the above is a useful starting point for a future person who is working on debugging this further! For now, I'm going to add the labels to indicate that this is a confirmed bug that has not yet been explained, which means this issue is now at step 3 of the bug process.

apparentlymart avatar Oct 25 '23 17:10 apparentlymart

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.

github-actions[bot] avatar Mar 31 '24 02:03 github-actions[bot]