Search code examples
amazon-web-servicesterraformterraform-provider-aws

Create waf rule if environment is nonprod Terraform


I'm trying to create an IP whitelist in nonprod for load testing, the WAF is dynamically created in prod and nonprod based on the envname/envtype:

resource "aws_waf_ipset" "pwa_cloudfront_ip_restricted" {
  name = "${var.envname}-pwa-cloudfront-whitelist"
  dynamic "ip_set_descriptors" {
    for_each = var.cloudfront_ip_restricted_waf_cidr_whitelist
    content {
      type  = ip_set_descriptors.value.type
      value = ip_set_descriptors.value.value
    }
  }
}

resource "aws_waf_rule" "pwa_cloudfront_ip_restricted" {
  depends_on  = [aws_waf_ipset.pwa_cloudfront_ip_restricted]
  name        = "${var.envname}-pwa-cloudfront-whitelist"
  metric_name = "${var.envname}PWACloudfrontWhitelist"

  predicates {
    data_id = aws_waf_ipset.pwa_cloudfront_ip_restricted.id
    negated = false
    type    = "IPMatch"
  }
}

resource "aws_waf_ipset" "pwa_cloudfront_ip_restricted_load_testing" {
  name  = "${var.envname}-pwa-cloudfront-whitelist_load_testing"
  count = var.envtype == "nonprod" ? 1 : 0
  dynamic "ip_set_descriptors" {
    for_each = var.cloudfront_ip_restricted_waf_cidr_whitelist_load_testing
    content {
      type  = ip_set_descriptors.value.type
      value = ip_set_descriptors.value.value
    }
  }
}

resource "aws_waf_rule" "pwa_cloudfront_ip_restricted_load_testing" {
  depends_on  = [aws_waf_ipset.pwa_cloudfront_ip_restricted_load_testing]
  count = var.envtype == "nonprod" ? 1 : 0
  name        = "${var.envname}-pwa-cloudfront-whitelist-load_testing"
  metric_name = "${var.envname}PWACloudfrontWhitelistload_testing"

  predicates {
    data_id = aws_waf_ipset.pwa_cloudfront_ip_restricted_load_testing[count.index].id
    negated = false
    type    = "IPMatch"
  }
}

resource "aws_waf_web_acl" "pwa_cloudfront_ip_restricted" {
  name        = "${var.envname}-pwa-cloudfront-whitelist"
  metric_name = "${var.envname}PWACloudfrontWhitelist"

  default_action {
    type = "BLOCK"
  }

  rules {
    action {
      type = "ALLOW"
    }

    priority = 1
    rule_id  = aws_waf_rule.pwa_cloudfront_ip_restricted.id
    type     = "REGULAR"
  }

    rules {
    action {
      type = "ALLOW"
    }

    priority = 2
    rule_id  = aws_waf_rule.pwa_cloudfront_ip_restricted_load_testing.id
    type     = "REGULAR"
  }
}

The second rules block throws and error in the terraform plan:

Error: Missing resource instance key

  on waf.tf line 73, in resource "aws_waf_web_acl" "pwa_cloudfront_ip_restricted":
  73:     rule_id  = aws_waf_rule.pwa_cloudfront_ip_restricted_load_testing.id

Because aws_waf_rule.pwa_cloudfront_ip_restricted_load_testing has "count" set,
its attributes must be accessed on specific instances.

For example, to correlate with indices of a referring resource, use:
    aws_waf_rule.pwa_cloudfront_ip_restricted_load_testing[count.index]

However if I add [count.index] :

Error: Reference to "count" in non-counted context

  on waf.tf line 73, in resource "aws_waf_web_acl" "pwa_cloudfront_ip_restricted":
  73:     rule_id  = aws_waf_rule.pwa_cloudfront_ip_restricted_load_testing[count.index].id

The "count" object can only be used in "module", "resource", and "data"
blocks, and only when the "count" argument is set.

Is there a way to do this that doesn't use the count param? Or am I missing something in the way that I am using it?


Solution

  • Since there is difference between the prod and non-prod environment, the way this should be tackled is by using dynamic [1] and for_each meta-argument [2]:

    resource "aws_waf_web_acl" "pwa_cloudfront_ip_restricted" {
      name        = "${var.envname}-pwa-cloudfront-whitelist"
      metric_name = "${var.envname}PWACloudfrontWhitelist"
    
      default_action {
        type = "BLOCK"
      }
    
      dynamic "rules" {
        for_each = var.envtype == "nonprod" ? [1] : []
        content {
          action {
            type = "ALLOW"
         }
          priority = 1
          rule_id  = aws_waf_rule.pwa_cloudfront_ip_restricted[0].id
          type     = "REGULAR"
        }
      }
    
      dynamic "rules" {
        for_each = var.envtype == "nonprod" ? [1] : []
        content {
          action {
            type = "ALLOW"
          }
          priority = 2
          rule_id  = aws_waf_rule.pwa_cloudfront_ip_restricted_load_testing[0].id
          type     = "REGULAR"
        }
      }
    }
    

    [1] https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks

    [2] https://developer.hashicorp.com/terraform/language/expressions/for