terraformcloudflare

Terraform Module for multiple rules in Cloudflare ruleset


I am trying to make a module for a ruleset in cloudflare with multiple rules. When I pass in a variable with the rules, it tries to create two rulesets, not a single ruleset with multiple rules in it.

This the terraform file that calls the module:

module "cloudflare-single-redirects-cf-domain-com" {
  source          = "./single-redirects"
  single_redirect = var.single-redirects-cf-domain-com
  zone_id         = "8451218541616132151"
  providers = {
    cloudflare = cloudflare.provider
  }
}

variable "single-redirects-cf-domain-com" {
  type = map(object({
    target_url            = string
    preserve_query_string = bool
    expression            = string
    description           = string
    enabled               = bool
  }))

  default = {
    alerts-redirect-otherdomain = {
      target_url            = "https://www.otherdomain.com"
      preserve_query_string = false
      expression            = "(starts_with(http.request.uri.path, \"/test\"))"
      description           = "Redirect users to otherdomain"
      enabled               = true
    }
    jaymz-redirect-otherdomain = {
      target_url            = "https://www.otherdomain.com"
      preserve_query_string = false
      expression            = "(starts_with(http.request.uri.path, \"/testing\"))"
      description           = "Redirect users to otherdomain"
      enabled               = true
    }
  }
}

This is the module file

resource "cloudflare_ruleset" "single_redirect" {
  zone_id     = var.zone_id
  name        = "http_request_dynamic_redirect"
  description = "http_request_dynamic_redirect"
  kind        = "zone"
  phase       = "http_request_dynamic_redirect"

  for_each    = var.single_redirect
  rules {
    action = "redirect"
    action_parameters {
      from_value {
        status_code = 301
        target_url {
          value = each.value["target_url"]
        }
        preserve_query_string = each.value["preserve_query_string"]
      }
    }
    expression  = each.value["expression"]
    description = each.value["description"]
    enabled     = each.value["enabled"]
  }
}

If I remove the second rule in the variable, it creates a ruleset fine.

When I tried to move the for_each into the rules section but get "Error: each.value cannot be used in this context"


Solution

  • Don't use a foreach meta, that is telling Terraform you want to create the resource for each item. Instead you want to create a rule for each item. I have not used CloudFlare or the Terraform provider for it, but normally in this situation you want to use Terraform dynamic block https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks.

    A dynamic block acts much like a for expression, but produces nested blocks instead of a complex typed value. It iterates over a given complex value, and generates a nested block for each element of that complex value.

    Try the below, I can't say it will work for sure since I don't know the CF provider but it's the format I would expect to work:

    resource "cloudflare_ruleset" "single_redirect" {
      zone_id     = var.zone_id
      name        = "http_request_dynamic_redirect"
      description = "http_request_dynamic_redirect"
      kind        = "zone"
      phase       = "http_request_dynamic_redirect"
    
      dynamic "rules" {
        for_each = var.single-redirects-cf-domain-com
        content {
          action = "redirect"
          action_parameters {
            from_value {
              status_code           = 301
              target_url {
                value = rules.value["target_url"]
              }
              preserve_query_string = rules.value["preserve_query_string"]
            }
          }
    
          expression  = rules.value["expression"]
          description = rules.value["description"]
          enabled     = rules.value["enabled"]
        }
    
      }
    }