Skip to content

In cloudflare_ruleset removing an existing rule causes recreation of later rules #5921

@simonblake-mp

Description

@simonblake-mp

Confirmation

  • This is a bug with an existing resource and is not a feature request or enhancement. Feature requests should be submitted with Cloudflare Support or your account team.
  • I have searched the issue tracker and my issue isn't already found.
  • I have replicated my issue using the latest version of the provider and it is still present.

Terraform and Cloudflare provider version

$ terraform -v
Terraform v1.12.2
on darwin_arm64

provider: v5.8.2

Affected resource(s)

  • cloudflare_ruleset

Terraform configuration files

resource "cloudflare_ruleset" "example_ruleset" {
  zone_id = cloudflare_zone.origin_marketplacer_com.id
  kind    = "zone"
  phase   = "http_ratelimit"
  name    = "default"

  rules = [
    {
      description = "rule a"
      enabled     = true
      expression  = "(http.host in {\"a.com\"})"
      action      = "block"
      ratelimit = {
        characteristics = [
          "ip.src",
          "cf.colo.id",
        ]
        mitigation_timeout  = 0
        period              = 10
        requests_per_period = 200
        requests_to_origin  = true
      }
    },
    {
      description = "rule b"
      enabled     = true
      expression  = "(http.host in {\"b.com\"})"
      action      = "block"
      ratelimit = {
        characteristics = [
          "ip.src",
          "cf.colo.id",
        ]
        mitigation_timeout  = 0
        period              = 10
        requests_per_period = 200
        requests_to_origin  = true
      }
    },
    {
      description = "rule c"
      enabled     = true
      expression  = "(http.host in {\"c.com\"})"
      action      = "block"
      ratelimit = {
        characteristics = [
          "ip.src",
          "cf.colo.id",
        ]
        mitigation_timeout  = 0
        period              = 10
        requests_per_period = 200
        requests_to_origin  = true
      }
    }
  ]
}

Link to debug output

n/a

Panic output

n/a

Expected output

if you apply the above config, it creates three rules, all good. If you then remove 'rule a', then I'd expect rule a to be removed, and 'rule b/c' to be unchanged. If at some later time I add 'rule a' back to the config, I'd expect it to be inserted without change to 'rule b/c'

Actual output

when you plan removing rule a from the config above, terraform shows rules b and c "moving up" - that is rule a is reconfigured to match rule b, rule b is reconfigured to match rule c, and then the old rule c is deleted:

          ~ {
              ~ description = "rule a" -> "rule b"
              ~ id          = "78e2c032fa934fca87b78ec51a8aa876" -> (known after apply)
              ~ ref         = "78e2c032fa934fca87b78ec51a8aa876" -> (known after apply)
                # (4 unchanged attributes hidden)
            },
          ~ {
              ~ description = "rule b" -> "rule c"
              ~ id          = "2113c87e80ca491981fd326330c413f2" -> (known after apply)
              ~ ref         = "2113c87e80ca491981fd326330c413f2" -> (known after apply)
                # (4 unchanged attributes hidden)
            },
          - {
              - action      = "block" -> null
              - description = "rule c" -> null
              - enabled     = true -> null
              - expression  = "(http.host in {\"b.com\"})" -> null
              - id          = "801316ad90c04acdba36148b31838ceb" -> null
              - ratelimit   = {
                  - characteristics     = [
                      - "ip.src",
                      - "cf.colo.id",
                    ] -> null
                  - mitigation_timeout  = 0 -> null
                  - period              = 10 -> null
                  - requests_per_period = 200 -> null
                  - requests_to_origin  = true -> null
                } -> null
              - ref         = "801316ad90c04acdba36148b31838ceb" -> null
            },

behind the scenes, though, that doesn't appear to be what happens - instead, what appears to happen is that all three rules are deleted, and then rule b and rule c are recreated as entirely new rules - so before the plan above I had:

rule a id: 78e2c032fa934fca87b78ec51a8aa876
rule b id: 2113c87e80ca491981fd326330c413f2
rule c id :801316ad90c04acdba36148b31838ceb

then after the apply I have:

rule b id: 4303ec36d8084c5b82414ddca552e3ed
rule c id: 885170548236493ea560afd88dc63117

when you add rule a back to the console, the plan shows the reverse

          ~ {
              ~ description = "rule b" -> "rule a"
              ~ id          = "4303ec36d8084c5b82414ddca552e3ed" -> (known after apply)
              ~ ref         = "4303ec36d8084c5b82414ddca552e3ed" -> (known after apply)
                # (4 unchanged attributes hidden)
            },
          ~ {
              ~ description = "rule c" -> "rule b"
              ~ id          = "885170548236493ea560afd88dc63117" -> (known after apply)
              ~ ref         = "885170548236493ea560afd88dc63117" -> (known after apply)
                # (4 unchanged attributes hidden)
            },
          + {
              + action      = "block"
              + description = "rule c"
              + enabled     = true
              + expression  = "(http.host in {\"b.com\"})"
              + id          = (known after apply)
              + ratelimit   = {
                  + characteristics     = [
                      + "ip.src",
                      + "cf.colo.id",
                    ]
                  + mitigation_timeout  = 0
                  + period              = 10
                  + requests_per_period = 200
                  + requests_to_origin  = true
                }
              + ref         = (known after apply)
            },

and when applied, all three rules are recreated

rule a id: 2dfd6e99f53e4e5a9552cbb1badf51ce
rule b id: 8494b9eaa19d41d28994e7227c918d4f
rule c id :0dd37aeeefb7415cad9e4ea9647a5d67

Steps to reproduce

  1. apply the config above
  2. wait some period for the rules to start accruing metrics
  3. remove rule a from the config above, apply
  4. observe in the plan that terraform is going to "move everything up"
  5. observe in the cloudflare console that rules b and c have been recreated, and all history lost

Additional factoids

From a narrow terraform POV, this is fine - you're getting the end result you asked for . However, back in the cloudflare console you lose all the history of rules b/c - 30 days of metrics vanish - that's a pretty major violation of the principle of least surprise. The 30 days of graphs for each rule are important to our use of Cloudflare, we don't like to erase them if we can avoid it, so we've had to resort to adding and removing rules in the console.

The frustrating thing here is that the Cloudflare console handles this beautifully - inserting and deleting rules from the middle of the rule corpus doesn't cause other rules to lose their history.

This is not a new problem, the v4 provider was even worse (in that it would recreate all the rules if there was any change in any rule in the corpus). I was hoping that the move from independent rule blocks with implicit ordering to a single rules list with explicit ordering in v5 would make this issue go away, sadly, it hasn't yet.

References

This is essentially a rehashing of #3357 , but for provider v5

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/bugCategorizes issue or PR as related to a bug.version/5Categorizes issue or PR as related to version 5 of the provider.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions