Repository: cloudflare/cf-terraforming

Issue:
CF-TERRAFORMING for CLOUPDFLARE_RULESET resource not terraforming the RULES within ruleset
### Confirmation

- [x] My issue isn't already found on the issue tracker.
- [x] I have replicated my issue using the latest version of the library and it is still present.

### cf-terraforming version

cf-terraforming 0.23.3

### Expected outcome

When using cf-terraforming to terraform the CLOudFLARE_RULESET it should not only bring the basic setting of the ruleset but also the RULES within it, as it did before.

### Actual outcome

The output shows the RULESETS basic config  within the ZONE, but their RULES
```
resource "cloudflare_ruleset" "terraform_managed_resource_<id_redacted>" {
  description = "Created by the Cloudflare security team, this ruleset provides normalization on the URL path"
  kind        = "managed"
  name        = "Cloudflare Normalization Ruleset"
  phase       = "http_request_sanitize"
  zone_id     = "<redacted>"
}

resource "cloudflare_ruleset" "terraform_managed_resource_<id_redacted>" {
  kind    = "managed"
  name    = "Bot Fight Mode for Likely Bots"
  phase   = "http_request_sbfm"
  zone_id = "<redacted>"
}

resource "cloudflare_ruleset" "terraform_managed_resource_<id_redacted>" {
  kind    = "managed"
  name    = "Bot Fight Mode for Definite Bots"
  phase   = "http_request_sbfm"
  zone_id = "<redacted>"
}

resource "cloudflare_ruleset" "terraform_managed_resource_<id_redacted>" {
  description = "Created by the Cloudflare security team, this ruleset is designed to provide fast and effective protection for all your applications. It is frequently updated to cover new vulnerabilities and reduce false positives."
  kind        = "managed"
  name        = "Cloudflare Managed Ruleset"
  phase       = "http_request_firewall_managed"
  zone_id     = "<redacted>"
}

resource "cloudflare_ruleset" "terraform_managed_resource_<id_redacted>" {
  description = "Cloudflare's implementation of the Open Web Application Security Project (OWASP) ModSecurity Core Rule Set. We routinely monitor for updates from OWASP based on the latest version available from the official code repository"
  kind        = "managed"
  name        = "Cloudflare OWASP Core Ruleset"
  phase       = "http_request_firewall_managed"
  zone_id     = "<redacted>"
}

resource "cloudflare_ruleset" "terraform_managed_resource_<id_redacted>" {
  description = "Exposed credentials check rules"
  kind        = "managed"
  name        = "Cloudflare Exposed Credentials Check Ruleset"
  phase       = "http_request_firewall_managed"
  zone_id     = "<redacted>"
}

resource "cloudflare_ruleset" "terraform_managed_resource_<id_redacted>" {
  description = "Automatic mitigation of HTTP-based DDoS attacks. Cloudflare routinely adds signatures to address new attack vectors. Additional configuration allows you to customize the sensitivity of each rule and the performed mitigation action."
  kind        = "managed"
  name        = "DDoS L7 ruleset"
  phase       = "ddos_l7"
  zone_id     = "<redacted>"
}

resource "cloudflare_ruleset" "terraform_managed_resource_<id_redacted>" {
  description = "Created by the Cloudflare security team, this ruleset is designed to provide protection for free zones"
  kind        = "managed"
  name        = "Cloudflare Managed Free Ruleset"
  phase       = "http_request_firewall_managed"
  zone_id     = "<redacted>"
}

resource "cloudflare_ruleset" "terraform_managed_resource_<id_redacted>" {
  kind    = "zone"
  name    = "default"
  phase   = "http_request_firewall_managed"
  zone_id = "<redacted>"
}

resource "cloudflare_ruleset" "terraform_managed_resource_<id_redacted>" {
  description = "Rules maintained by bots team that can run for all plans"
  kind        = "managed"
  name        = "Cloudflare Bot Management rules for all plans"
  phase       = "http_request_sbfm"
  zone_id     = "<redacted>"
}

resource "cloudflare_ruleset" "terraform_managed_resource_<id_redacted>" {
  kind    = "zone"
  name    = "default"
  phase   = "http_request_firewall_custom"
  zone_id = "<redacted>"
}

```

### Steps to reproduce

#Execute the following
```cf-terraforming generate --email myemail@email.com --token <my-CF-token-here> -z <ZONE-ID for which I want the rulesets> --resource-type cloudflare_ruleset > importing-example1.tf```

### References

I'm checking directly on the cf-terraforming repo >> https://github.com/cloudflare/cf-terraforming
And for the supported resources I'm checking the list on your repo: https://github.com/cloudflare/cf-terraforming#:~:text=provider%20%22cloudflare/cloudflare%22-,Supported%20Resources,-v5

Discussion:
@kaincosta: Another important comment:
I checked via CLOUDFLARE API and when doing a curl against: 

```https://api.cloudflare.com/client/v4/zones/<zone-id>/rulesets```

it bring the same information, only the basic settings of the RULESET 
and not the RULES within the rulesets.

 the OLD endpoint`"https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/firewall/rules"` did return that information, but this resource is suppose to be deprecated.

Curl command:
```
❯ curl -X  GET "https://api.cloudflare.com/client/v4/zones/<redacted-zone-id>/rulesets" \
        -H "Authorization: Bearer <redacted-token>" \
        -H "Content-Type: application/json" | jq .
```


OUTPUT:
```
{
  "result": [
    {
      "description": "Created by the Cloudflare security team, this ruleset provides normalization on the URL path",
      "id": "<redacted-zone-id>",
      "kind": "managed",
      "last_updated": "2024-08-01T17:37:11.538019Z",
      "name": "Cloudflare Normalization Ruleset",
      "phase": "http_request_sanitize",
      "version": "6"
    },
    {
      "description": "",
      "id": "<redacted-zone-id>",
      "kind": "managed",
      "last_updated": "2024-12-18T17:13:02.123258Z",
      "name": "Bot Fight Mode for Likely Bots",
      "phase": "http_request_sbfm",
      "source": "firewall_managed",
      "version": "8"
    },
    {
      "description": "",
      "id": "<redacted-zone-id>",
      "kind": "managed",
      "last_updated": "2024-12-18T17:13:03.199618Z",
      "name": "Bot Fight Mode for Definite Bots",
      "phase": "http_request_sbfm",
      "source": "firewall_managed",
      "version": "8"
    },
    {
      "description": "Created by the Cloudflare security team, this ruleset is designed to provide fast and effective protection for all your applications. It is frequently updated to cover new vulnerabilities and reduce false positives.",
      "id": "<redacted-zone-id>",
      "kind": "managed",
      "last_updated": "2025-02-18T17:35:15.131581Z",
      "name": "Cloudflare Managed Ruleset",
      "phase": "http_request_firewall_managed",
      "source": "firewall_managed",
      "version": "232"
    },
    {
      "description": "Cloudflare's implementation of the Open Web Application Security Project (OWASP) ModSecurity Core Rule Set. We routinely monitor for updates from OWASP based on the latest version available from the official code repository",
      "id": "<redacted-zone-id>",
      "kind": "managed",
      "last_updated": "2024-08-19T17:26:58.593021Z",
      "name": "Cloudflare OWASP Core Ruleset",
      "phase": "http_request_firewall_managed",
      "source": "firewall_managed",
      "version": "87"
    },
    {
      "description": "Exposed credentials check rules",
      "id": "<redacted-zone-id>",
      "kind": "managed",
      "last_updated": "2024-08-01T17:37:12.291474Z",
      "name": "Cloudflare Exposed Credentials Check Ruleset",
      "phase": "http_request_firewall_managed",
      "source": "firewall_managed",
      "version": "89"
    },
    {
      "description": "Automatic mitigation of HTTP-based DDoS attacks. Cloudflare routinely adds signatures to address new attack vectors. Additional configuration allows you to customize the sensitivity of each rule and the performed mitigation action.",
      "id": "<redacted-zone-id>",
      "kind": "managed",
      "last_updated": "2025-02-19T21:08:00.815518Z",
      "name": "DDoS L7 ruleset",
      "phase": "ddos_l7",
      "version": "2718"
    },
    {
      "description": "Created by the Cloudflare security team, this ruleset is designed to provide protection for free zones",
      "id": "<redacted-zone-id>",
      "kind": "managed",
      "last_updated": "2024-08-01T17:37:10.825759Z",
      "name": "Cloudflare Managed Free Ruleset",
      "phase": "http_request_firewall_managed",
      "source": "firewall_managed",
      "version": "57"
    },
    {
      "description": "",
      "id": "<redacted-zone-id>",
      "kind": "zone",
      "last_updated": "2023-09-06T21:04:30.352935Z",
      "name": "default",
      "phase": "http_request_firewall_managed",
      "source": "firewall_managed",
      "version": "2"
    },
    {
      "description": "Rules maintained by bots team that can run for all plans",
      "id": "<redacted-zone-id>",
      "kind": "managed",
      "last_updated": "2025-02-12T23:39:38.344875Z",
      "name": "Cloudflare Bot Management rules for all plans",
      "phase": "http_request_sbfm",
      "source": "firewall_managed",
      "version": "10"
    },
    {
      "description": "",
      "id": "<redacted-zone-id>",
      "kind": "zone",
      "last_updated": "2025-02-20T11:04:18.084537Z",
      "name": "default",
      "phase": "http_request_firewall_custom",
      "source": "firewall_custom",
      "version": "50"
    }
  ],
  "success": true,
  "errors": [],
  "messages": []
}


```@kaincosta: Another question, would this have any impact on how terraform is able to track the rules within a ruleset?@kaincosta: I just tried with a new endpoint from the API: `curl -X GET https://api.cloudflare.com/client/v4/zones/<zone-id>/rulesets/<ruleset-id>`, which returned the expected output, I'm wondering inthe CF-TERRAFORMING, what resource would bring the same output as this?@cheewai-bash: Using cf-terraform v0.23.3 with the latest cloudflare/cloudflare provisioner v.5.1.0, I encounter exactly the same issue. `cf-terraforming generate --resource-type cloudflare_ruleset` output is missing `rules` attribute.@cheewai-bash: > Using cf-terraform v0.23.3 with the latest cloudflare/cloudflare provisioner v.5.1.0, I encounter exactly the same issue. `cf-terraforming generate --resource-type cloudflare_ruleset` output is missing `rules` attribute.

`--account` and `--zone` arguments are mutually-exclusive.

When run with `--zone` argument

```text
DEBU[0000] initializing cloudflare-go with API Token     account_Id= zone_id=XXX-REDACTED-XXX
DEBU[0001] initializing Terraform in .                  
DEBU[0003] detected provider                             version=5.1.0
DEBU[0003] reading Terraform schema for Cloudflare provider 
DEBU[0004] reading and building resource                 resource=cloudflare_ruleset
DEBU[0005] generating resource output                    count=20 resource=cloudflare_ruleset
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key account_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
resource "cloudflare_ruleset" "terraform_managed_resource_70339d97bdb34195bbf054b1ebe81f76" {
  description = "Created by the Cloudflare security team, this ruleset provides normalization on the URL path"
  kind        = "managed"
  name        = "Cloudflare Normalization Ruleset"
  phase       = "http_request_sanitize"
  zone_id     = "XXX-REDACTED-XXX"
}
```

When run with `--account` argument

```text
DEBU[0001] initializing Terraform in .                  
DEBU[0002] detected provider                             version=5.1.0
DEBU[0002] reading Terraform schema for Cloudflare provider 
DEBU[0004] reading and building resource                 resource=cloudflare_ruleset
DEBU[0005] generating resource output                    count=3 resource=cloudflare_ruleset
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key zone_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key zone_id, value <nil>, value type <nil> 
DEBU[0005] attribute "rules" has not been generated     
DEBU[0005] got unknown attribute configuration: key zone_id, value <nil>, value type <nil> 
resource "cloudflare_ruleset" "terraform_managed_resource_4d21379b4f9f4bb088e0729962c8b3cf" {
  account_id  = "XXX-REDACTED-XXX"
  description = "Automatic mitigation of HTTP-based DDoS attacks. Cloudflare routinely adds signatures to address new attack vectors. Additional configuration allows you to customize the sensitivity of each rule and the performed mitigation action."
  kind        = "managed"
  name        = "DDoS L7 ruleset"
  phase       = "ddos_l7"
}
```@stboissdev: +1@nhanvqit: +1 same issue@vhgbao01: same problem +1@aimuz: I encountered the same problem, and it seems to have lasted for a month@aimuz: I'm glad I've made progress on this issue. I solved this problem in the following ways.

The main reason for this problem is that the list method does not list the details, while the get method does. Therefore, this problem can be solved by importing the status. 


```bash
cf-terraforming generate --email myemail@email.com --token <my-CF-token-here> -z <ZONE-ID for which I want the rulesets> --resource-type cloudflare_ruleset > importing-example1.tf

cf-terraforming import --email myemail@email.com --token <my-CF-token-here> -z <ZONE-ID for which I want the rulesets> --resource-type cloudflare_ruleset > 1.sh

bash 1.sh

terraform show > importing-example1.tf
```@gui-wf: @aimuz That worked and helped me get on the right track! Thank you!

So, if am understanding correctly, what you did:
1. generate *ruleset* resources -> Gets us each ruleset basic metadata, but not all the rules in it. Before doing this,
2. generate the Terraform imports for all *rulesets* -> Makes Terraform create state of those *rulesets*. This state is created based on whats in the Infrastructure, Cloudflare side.
3. After having the necessary Terraform metadata, having the state in sync with the Infrastructure, we can finally generate the HCL that respects the state.

Am I thinking right?

### Some Testing I did
Following your steps:
1. the `cf-terraforming generate`, 
2. followed by `cf-terraforming import ... > tf-import-commands.sh`,
3. followed by `bash tf-import-commands.sh`, 
4. and finally `terraform show > generated.tf`. 
- If I then try to Terraform Plan, it errors saying that I can't define the *id* attributes as they are read-only. To appease my mind a bit, I would like to know if you hit this same error @aimuz?
  > **NOTE:** I fixed this error by deleting all definitions of *id*'s. 
  > **Another NOTE** I want to make for anyone like me who has beginner/-ish knowledge of Terraform: (Please correct me if I am wrong. This is what I am gathering from my tests) 
  >    - The state will have the ids since that is what represents the real deployed infrastructure, which in our case is the Cloudflare side. The resource definitions themselves should not have the *ids* since this is something that Cloudflare generates, not our Terraform instances.

Based on your method, I then continued on what I was doing, which is trying to create a full representation of the Cloudflare configurations but in HCL/Terrafrom.

If I  create the (modern) import blocks with `cf-terraforming import --modern-import-block ...`, and then try doing `terraform plan`, Terraform invites me to use `-generate-config-out=generated.tf` like so:

```bash
terraform plan -generate-config-out=generated.tf
```

That gives me a 100% valid HCL configuration - `terraform plan`-ning does not error.

### Idk what this issue is/where it comes from...

However, something weird happens. Comparing the values the Cloudflare REST API gets me and what this process gave me:
 
- I have ruleset rules that have the **attribute *categories*** and others that do not. For the ones that don't have this attribute *categories*, on `terraform plan`, Terraform wants to do **in-place changes of** these resources, telling me that it will add this **non-existent *categories* attribute** with (known after apply).

    ![Image](https://github.com/user-attachments/assets/5980ba4f-8ea6-457d-8318-f1e3a9cbab59)
    
    ... when my Terraform HCL does not have this attribute set, not even set as `= null`.@jhutchings1: Thanks all, this is one we're aware of. The LIST endpoint on the rulesets API doesn't return the details of the ruleset itself. As a result, cf_terraforming exports only the metadata which was in that API call. @musa-cf do we have a plan to remedy this one? @madorb: Any idea when the next release is planned for? this issue is sort of a major showstopper for v5 usage...@jhutchings1: @musa-cf has been working on the cf-terraforming updates, and we're in validation right now. We should be ready to go in the next week or so assuming we don't find any unexpected issues. @madorb: thanks! 🤞 

I attempted to build the latest from `master` locally to unblock myself, but that appears to not work either - even worse, there's zero output! 

```
❯ ../../exploratory/cf-terraforming/cf-terraforming generate --resource-type cloudflare_ruleset --zone <myzone>
❯ 
```@elliotdobson: > I attempted to build the latest from master locally to unblock myself, but that appears to not work either - even worse, there's zero output!

+1 same here (using 40e0f0b).

Running with `--verbose` flag shows the successful HTTP requests to the Cloudflare API but `cf-terraforming` is not outputting any Terraform resources.@jhutchings1: > I attempted to build the latest from `master` locally to unblock myself, but that appears to not work either - even worse, there's zero output!

@musa-cf @vaishakdinesh can you have a look? 



@vaishakdinesh: > > I attempted to build the latest from `master` locally to unblock myself, but that appears to not work either - even worse, there's zero output!
> 
> [@musa-cf](https://github.com/musa-cf) [@vaishakdinesh](https://github.com/vaishakdinesh) can you have a look?

Taking a look! 

Solution patch:
diff --git a/README.md b/README.md
index 7f56c96..a65fb60 100644
--- a/README.md
+++ b/README.md
@@ -117,6 +117,15 @@ resource "cloudflare_record" "terraform_managed_resource" {
 }
 ```
 
+Some resource require an ID to be passed in to be able to either generate the hcl block or import command. The resources
+which require an id are listed in the table below for the v5 provider. Example usage:
+
+```bash
+cf-terraforming generate \
+  --zone $CLOUDFLARE_ZONE_ID \
+  --resource-type "cloudflare_hostname_tls_setting" \
+  --resource-id "cloudflare_hostname_tls_setting=ciphers"
+```
 ## Prerequisites
 
 - A Cloudflare account with resources defined (e.g. a few zones, some load
diff --git a/internal/app/cf-terraforming/cmd/custom_processing.go b/internal/app/cf-terraforming/cmd/custom_processing.go
index 56c90ea..7974f0e 100644
--- a/internal/app/cf-terraforming/cmd/custom_processing.go
+++ b/internal/app/cf-terraforming/cmd/custom_processing.go
@@ -302,6 +302,88 @@ func processCustomCasesV5(response *[]interface{}, resourceType string, pathPara
 		}
 	case "cloudflare_magic_wan_static_route":
 		denestResponses(response, resourceCount, "routes")
+	case "cloudflare_ruleset":
+		ruleHeaders := map[string][]map[string]interface{}{}
+		for i, ruleset := range *response {
+			if ruleset.(map[string]interface{})["rules"] != nil {
+				for j, rule := range ruleset.(map[string]interface{})["rules"].([]interface{}) {
+					ID := rule.(map[string]interface{})["id"]
+					if ID != nil {
+						headers, exists := ruleHeaders[ID.(string)]
+						if exists {
+							(*response)[i].(map[string]interface{})["rules"].([]interface{})[j].(map[string]interface{})["action_parameters"].(map[string]interface{})["headers"] = headers
+						}
+					}
+				}
+			}
+		}
+
+		// log custom fields specific transformation fields
+		logCustomFieldsTransform := []string{"cookie_fields", "request_fields", "response_fields"}
+
+		for i := 0; i < resourceCount; i++ {
+			rules := (*response)[i].(map[string]interface{})["rules"]
+			if rules != nil {
+				for ruleCounter := range rules.([]interface{}) {
+					// should the `ref` be the default `id`, don't output it
+					// as we don't need to track a computed default.
+					id := rules.([]interface{})[ruleCounter].(map[string]interface{})["id"]
+					ref := rules.([]interface{})[ruleCounter].(map[string]interface{})["ref"]
+					if id == ref {
+						rules.([]interface{})[ruleCounter].(map[string]interface{})["ref"] = nil
+					}
+
+					actionParams := rules.([]interface{})[ruleCounter].(map[string]interface{})["action_parameters"]
+					if actionParams != nil {
+						// check for log custom fields that need to be transformed
+						for _, logCustomFields := range logCustomFieldsTransform {
+							// check if the field exists and make sure it has at least one element
+							if actionParams.(map[string]interface{})[logCustomFields] != nil && len(actionParams.(map[string]interface{})[logCustomFields].([]interface{})) > 0 {
+								// Create a new list to store the data in.
+								var newLogCustomFields []interface{}
+								// iterate over each of the keys and add them to a generic list
+								for logCustomFieldsCounter := range actionParams.(map[string]interface{})[logCustomFields].([]interface{}) {
+									newLogCustomFields = append(newLogCustomFields, map[string]interface{}{"name": actionParams.(map[string]interface{})[logCustomFields].([]interface{})[logCustomFieldsCounter].(map[string]interface{})["name"]})
+								}
+								actionParams.(map[string]interface{})[logCustomFields] = newLogCustomFields
+							}
+						}
+
+						// check if our ruleset is of action 'skip'
+						if rules.([]interface{})[ruleCounter].(map[string]interface{})["action"] == "skip" && (*response)[i].(map[string]interface{})["phase"] != "http_request_firewall_managed" {
+							for rule := range actionParams.(map[string]interface{}) {
+								// "rules" is the only map[string][]string we need to remap. The others are all []string and are handled naturally.
+								if rule == "rules" {
+									for key, value := range actionParams.(map[string]interface{})[rule].(map[string]interface{}) {
+										var rulesList []string
+										for _, val := range value.([]interface{}) {
+											rulesList = append(rulesList, val.(string))
+										}
+										actionParams.(map[string]interface{})[rule].(map[string]interface{})[key] = strings.Join(rulesList, ",")
+									}
+								}
+							}
+						}
+
+						// Cache Rules transformation
+						if (*response)[i].(map[string]interface{})["phase"] == "http_request_cache_settings" {
+							if ck, ok := rules.([]interface{})[ruleCounter].(map[string]interface{})["action_parameters"].(map[string]interface{})["cache_key"]; ok {
+								if c, cok := ck.(map[string]interface{})["custom_key"]; cok {
+									if qs, qok := c.(map[string]interface{})["query_string"]; qok {
+										if s, sok := qs.(map[string]interface{})["include"]; sok && s == "*" {
+											rules.([]interface{})[ruleCounter].(map[string]interface{})["action_parameters"].(map[string]interface{})["cache_key"].(map[string]interface{})["custom_key"].(map[string]interface{})["query_string"].(map[string]interface{})["include"] = map[string]interface{}{"list": []string{"*"}}
+										}
+										if s, sok := qs.(map[string]interface{})["exclude"]; sok && s == "*" {
+											rules.([]interface{})[ruleCounter].(map[string]interface{})["action_parameters"].(map[string]interface{})["cache_key"].(map[string]interface{})["custom_key"].(map[string]interface{})["query_string"].(map[string]interface{})["exclude"] = map[string]interface{}{"list": []string{"*"}}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		}
 	}
 }
 
diff --git a/internal/app/cf-terraforming/cmd/generate.go b/internal/app/cf-terraforming/cmd/generate.go
index 1dccf9f..c34ec6f 100644
--- a/internal/app/cf-terraforming/cmd/generate.go
+++ b/internal/app/cf-terraforming/cmd/generate.go
@@ -496,7 +496,6 @@ func generateResources() func(cmd *cobra.Command, args []string) {
 					}
 					jsonStructData = newJsonStructData
 					resourceCount = len(jsonStructData)
-
 				case "cloudflare_custom_hostname_fallback_origin":
 					var jsonPayload []cfv0.CustomHostnameFallbackOrigin
 					apiCall, err := apiV0.CustomHostnameFallbackOrigin(context.Background(), zoneID)
@@ -714,7 +713,6 @@ func generateResources() func(cmd *cobra.Command, args []string) {
 							}
 						}
 					}
-
 				case "cloudflare_load_balancer_pool":
 					jsonPayload, err := apiV0.ListLoadBalancerPools(context.Background(), identifier, cfv0.ListLoadBalancerPoolParams{})
 					if err != nil {
@@ -884,7 +882,6 @@ func generateResources() func(cmd *cobra.Command, args []string) {
 						// Remap match.response.status to match.response.statuses
 						jsonStructData[i].(map[string]interface{})["match"].(map[string]interface{})["response"].(map[string]interface{})["statuses"] = jsonStructData[i].(map[string]interface{})["match"].(map[string]interface{})["response"].(map[string]interface{})["status"]
 					}
-
 				case "cloudflare_record":
 					jsonPayload, _, err := apiV0.ListDNSRecords(context.Background(), identifier, cfv0.ListDNSRecordsParams{})
 					if err != nil {
@@ -978,7 +975,8 @@ func generateResources() func(cmd *cobra.Command, args []string) {
 								}
 							}
 						}
-						continue
+						processCustomCasesV5(&jsonStructData, resourceType, "")
+						goto GEN_HCL
 					}
 
 					// Make the rules have the correct header structure
@@ -1401,7 +1399,6 @@ func generateResources() func(cmd *cobra.Command, args []string) {
 					if err != nil {
 						log.Fatal(err)
 					}
-
 				case "cloudflare_zone_settings_override":
 					jsonPayload, err := apiV0.ZoneSettings(context.Background(), zoneID)
 					if err != nil {
@@ -1468,12 +1465,12 @@ func generateResources() func(cmd *cobra.Command, args []string) {
 					return
 				}
 			}
-
 			log.WithFields(logrus.Fields{
 				"count":    resourceCount,
 				"resource": resourceType,
 			}).Debug("generating resource output")
 
+		GEN_HCL:
 			// If we don't have any resources to generate, just bail out early.
 			if resourceCount == 0 {
 				fmt.Fprintf(cmd.OutOrStderr(), "no resources of type %q found to generate", resourceType)



Test patch:
diff --git a/internal/app/cf-terraforming/cmd/generate_test.go b/internal/app/cf-terraforming/cmd/generate_test.go
index e497213..f19e149 100644
--- a/internal/app/cf-terraforming/cmd/generate_test.go
+++ b/internal/app/cf-terraforming/cmd/generate_test.go
@@ -225,25 +225,17 @@ func TestResourceGenerationV4(t *testing.T) {
 				return nil
 			})
 
+			apiV0, _ = cfv0.New(viper.GetString("key"), viper.GetString("email"), cfv0.HTTPClient(
+				&http.Client{
+					Transport: r,
+				},
+			))
 			output := ""
-
 			if tc.identiferType == "account" {
 				viper.Set("account", cloudflareTestAccountID)
-				apiV0, _ = cfv0.New(viper.GetString("key"), viper.GetString("email"), cfv0.HTTPClient(
-					&http.Client{
-						Transport: r,
-					},
-				))
-
 				output, _ = executeCommandC(rootCmd, "generate", "--resource-type", tc.resourceType, "--account", cloudflareTestAccountID)
 			} else {
 				viper.Set("zone", cloudflareTestZoneID)
-				apiV0, _ = cfv0.New(viper.GetString("key"), viper.GetString("email"), cfv0.HTTPClient(
-					&http.Client{
-						Transport: r,
-					},
-				))
-
 				output, _ = executeCommandC(rootCmd, "generate", "--resource-type", tc.resourceType, "--zone", cloudflareTestZoneID)
 			}
 
diff --git a/testdata/terraform/v5/cloudflare_ruleset/test.tf b/testdata/terraform/v5/cloudflare_ruleset/test.tf
index e69de29..6daf7a6 100644
--- a/testdata/terraform/v5/cloudflare_ruleset/test.tf
+++ b/testdata/terraform/v5/cloudflare_ruleset/test.tf
@@ -0,0 +1,42 @@
+resource "cloudflare_ruleset" "terraform_managed_resource" {
+  kind    = "zone"
+  name    = "default"
+  phase   = "http_request_dynamic_redirect"
+  zone_id = "0da42c8d2132a9ddaf714f9e7c920711"
+  rules = [{
+    action = "redirect"
+    action_parameters = {
+      from_value = {
+        preserve_query_string = true
+        status_code           = 301
+        target_url = {
+          value = "https://example.com/foo"
+        }
+      }
+    }
+    enabled      = true
+    expression   = "true"
+    id           = null
+    last_updated = "2025-03-25T22:38:12.845519Z"
+    ref          = "jacob1"
+    version      = "2"
+    }, {
+    action = "redirect"
+    action_parameters = {
+      from_value = {
+        preserve_query_string = true
+        status_code           = 301
+        target_url = {
+          value = "https://example.com/example1"
+        }
+      }
+    }
+    enabled      = true
+    expression   = "true"
+    id           = null
+    last_updated = "2025-03-25T22:38:12.845519Z"
+    ref          = "jacob2"
+    version      = "3"
+  }]
+}
+
diff --git a/testdata/terraform/v5/cloudflare_ruleset_http_request_cache_settings/test.tf b/testdata/terraform/v5/cloudflare_ruleset_http_request_cache_settings/test.tf
index e69de29..15ef521 100644
--- a/testdata/terraform/v5/cloudflare_ruleset_http_request_cache_settings/test.tf
+++ b/testdata/terraform/v5/cloudflare_ruleset_http_request_cache_settings/test.tf
@@ -0,0 +1,79 @@
+resource "cloudflare_ruleset" "terraform_managed_resource" {
+  kind    = "zone"
+  name    = "default"
+  phase   = "http_request_cache_settings"
+  zone_id = "0da42c8d2132a9ddaf714f9e7c920711"
+  rules = [{
+    action = "set_cache_settings"
+    action_parameters = {
+      browser_ttl = {
+        mode = "respect_origin"
+      }
+      cache = true
+      cache_key = {
+        cache_by_device_type  = true
+        cache_deception_armor = true
+        custom_key = {
+          host = {
+            resolved = false
+          }
+          query_string = {
+            exclude = {
+              list = ["*"]
+            }
+          }
+        }
+        ignore_query_strings_order = false
+      }
+      edge_ttl = {
+        default = 30
+        mode    = "override_origin"
+        status_code_ttl = [{
+          status_code = 100
+          value       = 30
+          }, {
+          status_code_range = {
+            from = 100
+            to   = 106
+          }
+          value = 5
+          }, {
+          status_code_range = {
+            from = 130
+            to   = 162
+          }
+          value = 31536000
+        }]
+      }
+      origin_error_page_passthru = true
+      respect_strong_etags       = true
+      serve_stale = {
+        disable_stale_while_updating = true
+      }
+    }
+    description  = "test cache rule"
+    enabled      = false
+    expression   = "(http.host eq \"example.com\")"
+    id           = null
+    last_updated = "2022-09-28T17:21:21.510301Z"
+    ref          = "0f24aab3002347a9a4ac01520e6893d0"
+    version      = "3"
+    }, {
+    action = "set_cache_settings"
+    action_parameters = {
+      cache = false
+      edge_ttl = {
+        default = 60
+        mode    = "override_origin"
+      }
+    }
+    description  = "/status/202"
+    enabled      = true
+    expression   = "(http.host eq \"example.com\")"
+    id           = null
+    last_updated = "2022-09-21T16:36:00.999083Z"
+    ref          = "e5f1bd1386b4464aa8d726ba1e0d51ad"
+    version      = "2"
+  }]
+}
+
diff --git a/testdata/terraform/v5/cloudflare_ruleset_override_remapping_disabled/test.tf b/testdata/terraform/v5/cloudflare_ruleset_override_remapping_disabled/test.tf
index e69de29..89f736c 100644
--- a/testdata/terraform/v5/cloudflare_ruleset_override_remapping_disabled/test.tf
+++ b/testdata/terraform/v5/cloudflare_ruleset_override_remapping_disabled/test.tf
@@ -0,0 +1,32 @@
+resource "cloudflare_ruleset" "terraform_managed_resource" {
+  kind    = "zone"
+  name    = "zone"
+  phase   = "http_request_firewall_managed"
+  zone_id = "0da42c8d2132a9ddaf714f9e7c920711"
+  rules = [{
+    action = "execute"
+    action_parameters = {
+      id = "efb7b8c949ac4650a09736fc376e9aee"
+      overrides = {
+        action = "log"
+        categories = [{
+          category = "paranoia-level-2"
+          enabled  = false
+        }]
+        enabled = false
+        rules = [{
+          enabled = false
+          id      = "6179ae15870a4bb7b2d480d4843b323c"
+        }]
+      }
+      version = "latest"
+    }
+    description  = "zone"
+    enabled      = false
+    expression   = "(http.cookie eq \"jb_testing=true\")"
+    id           = null
+    last_updated = "2021-09-03T06:42:41.341405Z"
+    ref          = null
+  }]
+}
+
diff --git a/testdata/terraform/v5/cloudflare_ruleset_override_remapping_enabled/test.tf b/testdata/terraform/v5/cloudflare_ruleset_override_remapping_enabled/test.tf
index e69de29..6f052b7 100644
--- a/testdata/terraform/v5/cloudflare_ruleset_override_remapping_enabled/test.tf
+++ b/testdata/terraform/v5/cloudflare_ruleset_override_remapping_enabled/test.tf
@@ -0,0 +1,32 @@
+resource "cloudflare_ruleset" "terraform_managed_resource" {
+  kind    = "zone"
+  name    = "zone"
+  phase   = "http_request_firewall_managed"
+  zone_id = "0da42c8d2132a9ddaf714f9e7c920711"
+  rules = [{
+    action = "execute"
+    action_parameters = {
+      id = "efb7b8c949ac4650a09736fc376e9aee"
+      overrides = {
+        action = "log"
+        categories = [{
+          category = "paranoia-level-2"
+          enabled  = true
+        }]
+        enabled = true
+        rules = [{
+          enabled = true
+          id      = "6179ae15870a4bb7b2d480d4843b323c"
+        }]
+      }
+      version = "latest"
+    }
+    description  = "zone"
+    enabled      = false
+    expression   = "(http.cookie eq \"jb_testing=true\")"
+    id           = null
+    last_updated = "2021-09-03T06:42:41.341405Z"
+    ref          = null
+  }]
+}
+
diff --git a/testdata/terraform/v5/cloudflare_ruleset_zone_ddos_l7/test.tf b/testdata/terraform/v5/cloudflare_ruleset_zone_ddos_l7/test.tf
index e69de29..12d8124 100644
--- a/testdata/terraform/v5/cloudflare_ruleset_zone_ddos_l7/test.tf
+++ b/testdata/terraform/v5/cloudflare_ruleset_zone_ddos_l7/test.tf
@@ -0,0 +1,24 @@
+resource "cloudflare_ruleset" "terraform_managed_resource" {
+  kind    = "zone"
+  name    = "zone"
+  phase   = "ddos_l7"
+  zone_id = "0da42c8d2132a9ddaf714f9e7c920711"
+  rules = [{
+    action = "execute"
+    action_parameters = {
+      id = "4d21379b4f9f4bb088e0729962c8b3cf"
+      overrides = {
+        sensitivity_level = "medium"
+      }
+      version = "latest"
+    }
+    description  = "zone"
+    enabled      = true
+    expression   = "true"
+    id           = null
+    last_updated = "2021-08-30T02:38:50.39057Z"
+    ref          = "c6893ad10fb344e9b8be3c0c3575adc9"
+    version      = "1"
+  }]
+}
+
diff --git a/testdata/terraform/v5/cloudflare_ruleset_zone_http_log_custom_fields/test.tf b/testdata/terraform/v5/cloudflare_ruleset_zone_http_log_custom_fields/test.tf
index e69de29..b05a70e 100644
--- a/testdata/terraform/v5/cloudflare_ruleset_zone_http_log_custom_fields/test.tf
+++ b/testdata/terraform/v5/cloudflare_ruleset_zone_http_log_custom_fields/test.tf
@@ -0,0 +1,34 @@
+resource "cloudflare_ruleset" "terraform_managed_resource" {
+  kind    = "zone"
+  name    = "zone"
+  phase   = "http_log_custom_fields"
+  zone_id = "0da42c8d2132a9ddaf714f9e7c920711"
+  rules = [{
+    action = "log_custom_field"
+    action_parameters = {
+      cookie_fields = [{
+        name = "cookie"
+        }, {
+        name = "fields"
+      }]
+      request_fields = [{
+        name = "request"
+        }, {
+        name = "fields"
+      }]
+      response_fields = [{
+        name = "response"
+        }, {
+        name = "fields"
+      }]
+    }
+    description  = "zone"
+    enabled      = true
+    expression   = "true"
+    id           = null
+    last_updated = "2022-07-22T12:34:45.479429Z"
+    ref          = "17a0d1e23a3444ccbd5e58fc7793649a"
+    version      = "1"
+  }]
+}
+
diff --git a/testdata/terraform/v5/cloudflare_ruleset_zone_http_ratelimit/test.tf b/testdata/terraform/v5/cloudflare_ruleset_zone_http_ratelimit/test.tf
index e69de29..c830dbc 100644
--- a/testdata/terraform/v5/cloudflare_ruleset_zone_http_ratelimit/test.tf
+++ b/testdata/terraform/v5/cloudflare_ruleset_zone_http_ratelimit/test.tf
@@ -0,0 +1,24 @@
+resource "cloudflare_ruleset" "terraform_managed_resource" {
+  kind    = "zone"
+  name    = "default"
+  phase   = "http_ratelimit"
+  zone_id = "0da42c8d2132a9ddaf714f9e7c920711"
+  rules = [{
+    action       = "block"
+    description  = "fwewe"
+    enabled      = false
+    expression   = "(http.cookie eq \"namwe=value\")"
+    id           = null
+    last_updated = "2021-08-29T21:59:21.447624Z"
+    ratelimit = {
+      characteristics     = ["ip.src", "cf.colo.id"]
+      mitigation_timeout  = 30
+      period              = 60
+      requests_per_period = 100
+      requests_to_origin  = true
+    }
+    ref     = "549e64153ff14d2cb5a5ef88c1f5bdbc"
+    version = "1"
+  }]
+}
+
diff --git a/testdata/terraform/v5/cloudflare_ruleset_zone_http_request_firewall_custom/test.tf b/testdata/terraform/v5/cloudflare_ruleset_zone_http_request_firewall_custom/test.tf
index e69de29..29d6448 100644
--- a/testdata/terraform/v5/cloudflare_ruleset_zone_http_request_firewall_custom/test.tf
+++ b/testdata/terraform/v5/cloudflare_ruleset_zone_http_request_firewall_custom/test.tf
@@ -0,0 +1,43 @@
+resource "cloudflare_ruleset" "terraform_managed_resource" {
+  kind    = "zone"
+  name    = "default"
+  phase   = "http_request_firewall_custom"
+  zone_id = "0da42c8d2132a9ddaf714f9e7c920711"
+  rules = [{
+    action = "skip"
+    action_parameters = {
+      phases   = ["http_ratelimit", "http_request_firewall_managed"]
+      products = ["zoneLockdown", "uaBlock", "bic", "hot", "securityLevel", "rateLimit", "waf"]
+      ruleset  = "current"
+    }
+    description  = "test.example.com"
+    enabled      = true
+    expression   = "(http.host eq \"test.example.com\")"
+    id           = null
+    last_updated = "2022-11-24T14:24:14.756247Z"
+    logging = {
+      enabled = true
+    }
+    ref     = "88dcb30401e348ba9e1352c2598f2a4c"
+    version = "2"
+    }, {
+    action       = "challenge"
+    description  = "customRule-test"
+    enabled      = true
+    expression   = "(cf.bot_management.score eq 50 and cf.bot_management.static_resource)"
+    id           = null
+    last_updated = "2022-11-07T19:03:05.198191Z"
+    ref          = "b3cc5e4cc6604f9d90a6a106df867760"
+    version      = "29"
+    }, {
+    action       = "log"
+    description  = "AWAF ML"
+    enabled      = false
+    expression   = "(cf.waf.score le 20)"
+    id           = null
+    last_updated = "2022-12-09T16:53:19.003821Z"
+    ref          = "1ecf73bdf7bd4227969a734412b13ad1"
+    version      = "7"
+  }]
+}
+
diff --git a/testdata/terraform/v5/cloudflare_ruleset_zone_http_request_firewall_managed/test.tf b/testdata/terraform/v5/cloudflare_ruleset_zone_http_request_firewall_managed/test.tf
index e69de29..6889b0b 100644
--- a/testdata/terraform/v5/cloudflare_ruleset_zone_http_request_firewall_managed/test.tf
+++ b/testdata/terraform/v5/cloudflare_ruleset_zone_http_request_firewall_managed/test.tf
@@ -0,0 +1,85 @@
+resource "cloudflare_ruleset" "terraform_managed_resource" {
+  kind    = "zone"
+  name    = "zone"
+  phase   = "http_request_firewall_managed"
+  zone_id = "0da42c8d2132a9ddaf714f9e7c920711"
+  rules = [{
+    action = "execute"
+    action_parameters = {
+      id = "efb7b8c949ac4650a09736fc376e9aee"
+      overrides = {
+        rules = [{
+          action = "block"
+          id     = "5de7edfa648c4d6891dc3e7f84534ffa"
+          }, {
+          action = "block"
+          id     = "d52aa57408a144afa35e0fd96e3897dc"
+          }, {
+          action = "block"
+          id     = "7994335d116849f7a0ab6b771d1d0db7"
+          }, {
+          action = "block"
+          id     = "20e34d3164a340dbb5c5d29203ccff90"
+          }, {
+          action = "block"
+          id     = "8d9f209f35df412ba4bafe5156335ab1"
+          }, {
+          action = "block"
+          id     = "8840c3fa2c7947f6b10176ceb8f65558"
+          }, {
+          action = "block"
+          id     = "48e06376fc6347c0bf08b8ccf82d008b"
+          }, {
+          action = "block"
+          id     = "8ea0937695984040b528c80a4e6df495"
+          }, {
+          action = "block"
+          id     = "b777ce009bb346b39be4886055a71165"
+          }, {
+          action = "block"
+          id     = "cb5b6de178d3488d8649da8608b7b3a2"
+          }, {
+          action = "block"
+          id     = "390b6273c8dc4366b36e52fc6f35c356"
+          }, {
+          action = "block"
+          id     = "8ac6964456494da6b098a93c35f86fc9"
+          }, {
+          action = "block"
+          id     = "5ac122b3972c4247a247f3271045f374"
+          }, {
+          action = "block"
+          id     = "b1efd337665d49f5950f892971120c4b"
+          }, {
+          action = "block"
+          id     = "34158d546873469a8f8ccee19139627b"
+        }]
+      }
+      version = "latest"
+    }
+    description  = "zone"
+    enabled      = false
+    expression   = "(http.cookie eq \"jb_testing=true\")"
+    id           = null
+    last_updated = "2021-09-03T06:42:41.341405Z"
+    ref          = null
+    }, {
+    action = "skip"
+    action_parameters = {
+      rules = {
+        "4814384a9e5d4991b9815dcfc25d2f1f" = ["37da7855d2f94f69865365d894a556a4", "6afe6795ee6a48d6a1dfe59255395a78", "5a6f5a57cde8428ab0668ce17cdec0c8", "5e4903d6afa841c9b88b96203297003f", "2380cd409b604c2a9273042f3eb29c4e", "f5aebedc99a14c8d9e8cfa2ce5f94216", "edf8c37cc81747d382690b3c77e82ce4", "1129dfb383bb42e48466488cf3b37cb1"]
+      }
+    }
+    description  = "Bypass managed OWSAP SQL Injection rules for /api/v1/identity"
+    enabled      = true
+    expression   = "(http.request.method eq \"POST\" and http.request.uri.path eq \"/api/v1/identity\")"
+    id           = null
+    last_updated = "2023-05-12T08:23:03.177093Z"
+    logging = {
+      enabled = true
+    }
+    ref     = "d189267a8dc943769d0000c3dcb400eb"
+    version = "1"
+  }]
+}
+
diff --git a/testdata/terraform/v5/cloudflare_ruleset_zone_http_request_late_transform/test.tf b/testdata/terraform/v5/cloudflare_ruleset_zone_http_request_late_transform/test.tf
index e69de29..6592826 100644
--- a/testdata/terraform/v5/cloudflare_ruleset_zone_http_request_late_transform/test.tf
+++ b/testdata/terraform/v5/cloudflare_ruleset_zone_http_request_late_transform/test.tf
@@ -0,0 +1,69 @@
+resource "cloudflare_ruleset" "terraform_managed_resource" {
+  kind    = "zone"
+  name    = "default"
+  phase   = "http_request_late_transform"
+  zone_id = "0da42c8d2132a9ddaf714f9e7c920711"
+  rules = [{
+    action = "rewrite"
+    action_parameters = {
+      headers = {
+        example-http-header-1 = {
+          operation = "remove"
+        }
+        example-http-header-2 = {
+          operation = "remove"
+        }
+        example-http-header-3 = {
+          expression = "(ip.geoip.continent eq \"pluto\")"
+          operation  = "set"
+        }
+      }
+      uri = {
+        path = {
+          value = "/aquarii_b"
+        }
+      }
+    }
+    description  = "test transform"
+    enabled      = true
+    expression   = "(http.request.uri.path eq \"example.com\")"
+    id           = null
+    last_updated = "2022-02-07T16:58:54.317608Z"
+    ref          = "e5b61605d6cf4ce08f729c17d42d76ef"
+    version      = "1"
+    }, {
+    action = "rewrite"
+    action_parameters = {
+      headers = {
+        example-http-static-header-1 = {
+          operation = "set"
+          value     = "my-http-header-1"
+        }
+      }
+    }
+    description  = "test transform set"
+    enabled      = true
+    expression   = "(http.request.uri.path eq \"example.com\")"
+    id           = null
+    last_updated = "2022-02-07T16:58:54.317608Z"
+    ref          = "8ec764cf386940c89dd83dbab7bb4c16"
+    version      = "1"
+    }, {
+    action = "rewrite"
+    action_parameters = {
+      uri = {
+        path = {
+          value = "/spaceship"
+        }
+      }
+    }
+    description  = "test uri rewrite set"
+    enabled      = false
+    expression   = "(http.request.uri.path eq \"pumpkin.com\")"
+    id           = null
+    last_updated = "2022-05-07T16:58:54.317608Z"
+    ref          = "d0f1b4fdb4234adf9c6de9b614424836"
+    version      = "1"
+  }]
+}
+
diff --git a/testdata/terraform/v5/cloudflare_ruleset_zone_http_request_sanitize/test.tf b/testdata/terraform/v5/cloudflare_ruleset_zone_http_request_sanitize/test.tf
index e69de29..4d3ef99 100644
--- a/testdata/terraform/v5/cloudflare_ruleset_zone_http_request_sanitize/test.tf
+++ b/testdata/terraform/v5/cloudflare_ruleset_zone_http_request_sanitize/test.tf
@@ -0,0 +1,26 @@
+resource "cloudflare_ruleset" "terraform_managed_resource" {
+  kind    = "zone"
+  name    = "Zone sanitize ruleset"
+  phase   = "http_request_sanitize"
+  zone_id = "0da42c8d2132a9ddaf714f9e7c920711"
+  rules = [{
+    action = "execute"
+    action_parameters = {
+      id = "70339d97bdb34195bbf054b1ebe81f76"
+      overrides = {
+        rules = [{
+          enabled = true
+          id      = "78723a9e0c7c4c6dbec5684cb766231d"
+        }]
+      }
+      version = "latest"
+    }
+    enabled      = true
+    expression   = "true"
+    id           = null
+    last_updated = "2021-08-19T23:41:34.985519Z"
+    ref          = "0789dc4343054d1e981f8c44bedc6fbd"
+    version      = "1"
+  }]
+}
+
diff --git a/testdata/terraform/v5/cloudflare_ruleset_zone_no_configuration/test.tf b/testdata/terraform/v5/cloudflare_ruleset_zone_no_configuration/test.tf
index e69de29..156215a 100644
--- a/testdata/terraform/v5/cloudflare_ruleset_zone_no_configuration/test.tf
+++ b/testdata/terraform/v5/cloudflare_ruleset_zone_no_configuration/test.tf
@@ -0,0 +1,9 @@
+resource "cloudflare_ruleset" "terraform_managed_resource" {
+  description = "Some ruleset"
+  kind        = "zone"
+  name        = "default"
+  phase       = "http_request_late_transform"
+  zone_id     = "0da42c8d2132a9ddaf714f9e7c920711"
+  rules       = []
+}
+
diff --git a/testdata/terraform/v5/cloudflare_ruleset_zone_rewrite_to_empty_query_parameter/test.tf b/testdata/terraform/v5/cloudflare_ruleset_zone_rewrite_to_empty_query_parameter/test.tf
index e69de29..c817ccb 100644
--- a/testdata/terraform/v5/cloudflare_ruleset_zone_rewrite_to_empty_query_parameter/test.tf
+++ b/testdata/terraform/v5/cloudflare_ruleset_zone_rewrite_to_empty_query_parameter/test.tf
@@ -0,0 +1,24 @@
+resource "cloudflare_ruleset" "terraform_managed_resource" {
+  kind    = "zone"
+  name    = "default"
+  phase   = "http_request_transform"
+  zone_id = "0da42c8d2132a9ddaf714f9e7c920711"
+  rules = [{
+    action = "rewrite"
+    action_parameters = {
+      uri = {
+        query = {
+          value = ""
+        }
+      }
+    }
+    description  = "rewrite with no query string"
+    enabled      = true
+    expression   = "true"
+    id           = null
+    last_updated = "2023-02-16T00:26:08.978517Z"
+    ref          = "1fb6a3117e864d46bcda192d14a1e1dc"
+    version      = "5"
+  }]
+}
+