Reference to "each" in context without for_each

Hello, i am migrating the code from classic waf to wafv2, in one of my data modules I am using the for each loop to retrieve the list of IP addresses and block those IP’s by using the waf_acl rule.
when i am run the terraform plan getting the below error:

Error: Reference to "each" in context without for_each
│
│   on global-main.tf line 52, in resource "aws_wafv2_web_acl" "dev":
│   52:         arn = data.aws_wafv2_ip_set.whitelists[each.key].arn
│
│ The "each" object can be used only in "module" or "resource" blocks, and only when the "for_each" argument is set.

Below is my sample code:

data "aws_wafv2_ip_set" "whitelists" {
  for_each = { for v in var.whitelist_ip_sets : v => v }
  name = each.value
  scope = "CLOUDFRONT"
}
resource "aws_wafv2_web_acl" "test" {
  name        = "testWebAcl-${var.environment}"
  scope = "CLOUDFRONT"

  default_action {
    allow {}
  }

  rule {
    name        = "ADN whitelist"
    priority = 10

    action {
      block {}
    }

    statement {
      ip_set_reference_statement {
        arn = data.aws_wafv2_ip_set.whitelists[each.key].arn
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = false
      metric_name                = "testWebAcl${var.environment}"
      sampled_requests_enabled   = false
      }
    }
}

So, please suggest if any mistake in my code.
Thanks in advance.

Hi @sadik13

Your aws_wafv2_web_acl resource is using each.key in the statement block, but that resource is not using for_each, so there is no each value to reference there.

HI @jbardin

In one of my repo WAFV2 code migrations, I need to retrieve the data block for_each value in my resource “aws_wafv2_web_acl” statement block. So please suggest how to achieve this one in WAFV2.

Below is my sample code snippet:

main.tf:=

data "aws_wafv2_ip_set" "whitelists" {
  for_each = { for v in var.whitelist_ip_sets : v => v }
  name     = each.key
  scope    = var.scope
}
resource "aws_wafv2_web_acl" "dev" {
  name  = "testWebAcl-${var.environment}"
  scope = "CLOUDFRONT"

  default_action {
    allow {}
  }

  rule {
    name     = "Test-whitelist"
    priority = 10

    action {
      block {}
    }

    statement {

      and_statement {

        statement {

          ip_set_reference_statement {

            arn = data.aws_wafv2_ip_set.whitelists[each.key].arn

          }
        }
 }
    }

    visibility_config {
      cloudwatch_metrics_enabled = false
      metric_name                = "testWebAcl${var.environment}"
      sampled_requests_enabled   = false
    }
  }
visibility_config {
    cloudwatch_metrics_enabled = false
    metric_name                = "testWebAcl${var.environment}"
    sampled_requests_enabled   = false
  }
}
variable.tf:-
variable "test_ip_sets" {
 type = list(string)
  default = {
    test_public_nats1,
    test_function,
    testjar,
  }
}

If i am run the Terraform plan(V1.0.0) by using the above code getting the below error:

Error: Reference to "each" in context without for_each
│
│   on global-main.tf line 59, in resource "aws_wafv2_web_acl" "dev":
│   59:             arn = data.aws_wafv2_ip_set.whitelists[each.key].arn
│
│ The "each" object can be used only in "module" or "resource" blocks, and only when the "for_each"
│ argument is set

Thanks in Advance!!!

Hi @sadik13,

Is the requirement to repeat one of the nested blocks inside resource "aws_wafv2_web_acl" "dev" for each element in data.aws_wafv2_ip_set.whitelists? For example, do you need to have a separate rule block for each distinct IP set, or perhaps to generate an or_statement block with one nested statement block per IP set?

In all of these cases you can get the intended result using a dynamic block to generate multiple blocks of the same type with for_each = data.aws_wafv2_ip_set.whitelists, but the details will depend on exactly which of the block types you intend to dynamically generate.

If you’re not sure, it can help to write out manually the generated result you’d want for the three values you included in the default value for var.test_ip_sets, and then share that here and then we can show ways to get the same result dynamically in terms of var.test_ip_sets.