-
Notifications
You must be signed in to change notification settings - Fork 101
Description
Module version
v1.15.0
Use-cases
The cloudflare_ruleset
resource takes an ordered list of rules as an attribute:
resource "cloudflare_ruleset" "my_ruleset" {
zone_id = var.zone_id
name = "My Ruleset"
phase = "http_request_firewall_custom"
kind = "zone"
rules = [
{
expression = "ip.src eq 1.1.1.1"
action = "block"
ref = "one"
},
{
expression = "ip.src eq 2.2.2.2"
action = "block"
ref = "two"
}
]
}
Often, users want to insert or delete a rule midway through the list:
resource "cloudflare_ruleset" "my_ruleset" {
zone_id = var.zone_id
name = "My Ruleset"
phase = "http_request_firewall_custom"
kind = "zone"
rules = [
{
expression = "ip.src eq 2.2.2.2"
action = "block"
ref = "two"
}
]
}
However, Terraform cannot "match" the rules in the state to those in the plan, so shows a complex diff where many rules are shifted up/down (this gets worse the more rules there are in the ruleset):
# cloudflare_ruleset.my_ruleset will be updated in-place
~ resource "cloudflare_ruleset" "my_ruleset" {
id = "d94e34a9782e4156a58473bb13070748"
~ last_updated = "2025-08-28T18:55:06Z" -> (known after apply)
name = "My Ruleset"
~ rules = [
~ {
~ expression = "ip.src eq 1.1.1.1" -> "ip.src eq 2.2.2.2"
~ id = "5663e5c68fd64d5cbbd055820cf836a4" -> "eaefa038f18d422aab1f9fc5183f6ae8"
~ ref = "one" -> "two"
# (4 unchanged attributes hidden)
},
- {
- action = "block" -> null
- action_parameters = {} -> null
- enabled = true -> null
- expression = "ip.src eq 2.2.2.2" -> null
- id = "eaefa038f18d422aab1f9fc5183f6ae8" -> null
- ref = "two" -> null
# (1 unchanged attribute hidden)
},
]
~ version = "1" -> (known after apply)
# (4 unchanged attributes hidden)
}
In this case though, the provider has knowledge of how the rules in the state map to the rules in the plan, by using the ref
attribute of each rule.
Therefore, it would be ideal for the provider to have a way to pass this information to Terraform, so it can show a better diff:
# cloudflare_ruleset.my_ruleset will be updated in-place
~ resource "cloudflare_ruleset" "my_ruleset" {
id = "d94e34a9782e4156a58473bb13070748"
~ last_updated = "2025-08-28T18:55:06Z" -> (known after apply)
name = "My Ruleset"
~ rules = [
- {
- action = "block" -> null
- action_parameters = {} -> null
- enabled = true -> null
- expression = "ip.src eq 1.1.1.1" -> null
- id = "5663e5c68fd64d5cbbd055820cf836a4" -> null
- ref = "one" -> null
# (1 unchanged attribute hidden)
},
# (1 unchanged element hidden)
]
~ version = "1" -> (known after apply)
# (4 unchanged attributes hidden)
}
Attempted Solutions
I am not aware of any ways to achieve this today, unfortunately.
Proposal
I think this is something that could potentially be customized in a ModifyPlan
function. In the ModifyPlanResponse.Plan
, perhaps each list element could take an optional parameter with the index of the element in the state that it corresponds to.
Alternatively, perhaps schema.ListNestedAttribute
could take an optional parameter which specifies how to match elements in the state and the plan.
References
There are several related issues around diffing of lists (but not about allowing providers to customize this AFAICT):
- Terraform changes a lot of resources when removing an element from the middle of a list terraform#14275
- CLI plan: Use individual-element diffs when diffing two lists of the same length terraform#33779
This has also come up as an issue for the cloudflare_ruleset
resource: