diff --git a/cloudflare/resource_cloudflare_ruleset.go b/cloudflare/resource_cloudflare_ruleset.go index e3b1e799ed..a83724e332 100644 --- a/cloudflare/resource_cloudflare_ruleset.go +++ b/cloudflare/resource_cloudflare_ruleset.go @@ -109,11 +109,13 @@ func resourceCloudflareRuleset() *schema.Resource { "uri": { Type: schema.TypeList, Optional: true, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "path": { Type: schema.TypeList, Optional: true, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "value": { @@ -130,6 +132,7 @@ func resourceCloudflareRuleset() *schema.Resource { "query": { Type: schema.TypeList, Optional: true, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "value": { @@ -456,6 +459,29 @@ func buildRulesetRulesFromResource(r interface{}) ([]cloudflare.RulesetRule, err } } + case "uri": + for _, uriValue := range pValue.([]interface{}) { + if val, ok := uriValue.(map[string]interface{})["path"]; ok && len(val.([]interface{})) > 0 { + uriPathConfig := val.([]interface{})[0].(map[string]interface{}) + rule.ActionParameters.URI = &cloudflare.RulesetRuleActionParametersURI{ + Path: &cloudflare.RulesetRuleActionParametersURIPath{ + Value: uriPathConfig["value"].(string), + Expression: uriPathConfig["expression"].(string), + }, + } + } + + if val, ok := uriValue.(map[string]interface{})["query"]; ok && len(val.([]interface{})) > 0 { + uriQueryConfig := val.([]interface{})[0].(map[string]interface{}) + rule.ActionParameters.URI = &cloudflare.RulesetRuleActionParametersURI{ + Query: &cloudflare.RulesetRuleActionParametersURIQuery{ + Value: uriQueryConfig["value"].(string), + Expression: uriQueryConfig["expression"].(string), + }, + } + } + } + default: log.Printf("[DEBUG] unknown key encountered in buildRulesetRulesFromResource for action parameters: %s", pKey) } diff --git a/cloudflare/resource_cloudflare_ruleset_test.go b/cloudflare/resource_cloudflare_ruleset_test.go index d6d8c519d7..96f2225223 100644 --- a/cloudflare/resource_cloudflare_ruleset_test.go +++ b/cloudflare/resource_cloudflare_ruleset_test.go @@ -563,6 +563,104 @@ func TestAccCloudflareRuleset_MagicTransitUpdateWithHigherPriority(t *testing.T) }) } +func TestAccCloudflareRuleset_TransformationRuleURIPath(t *testing.T) { + t.Parallel() + rnd := generateRandomResourceName() + zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") + zoneName := os.Getenv("CLOUDFLARE_DOMAIN") + resourceName := "cloudflare_ruleset." + rnd + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckCloudflareRulesetTransformationRuleURIPath(rnd, "transform rule for URI path", zoneID, zoneName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "transform rule for URI path"), + resource.TestCheckResourceAttr(resourceName, "description", rnd+" ruleset description"), + resource.TestCheckResourceAttr(resourceName, "kind", "zone"), + resource.TestCheckResourceAttr(resourceName, "phase", "http_request_transform"), + + resource.TestCheckResourceAttr(resourceName, "rules.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rules.0.action", "rewrite"), + resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.uri.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.uri.0.path.0.value", "/static-rewrite"), + ), + }, + }, + }) +} + +func TestAccCloudflareRuleset_TransformationRuleURIQuery(t *testing.T) { + t.Parallel() + rnd := generateRandomResourceName() + zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") + zoneName := os.Getenv("CLOUDFLARE_DOMAIN") + resourceName := "cloudflare_ruleset." + rnd + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckCloudflareRulesetTransformationRuleURIQuery(rnd, "transform rule for URI query", zoneID, zoneName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "transform rule for URI query"), + resource.TestCheckResourceAttr(resourceName, "description", rnd+" ruleset description"), + resource.TestCheckResourceAttr(resourceName, "kind", "zone"), + resource.TestCheckResourceAttr(resourceName, "phase", "http_request_transform"), + + resource.TestCheckResourceAttr(resourceName, "rules.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rules.0.action", "rewrite"), + resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.uri.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.uri.0.query.0.value", "a=b"), + ), + }, + }, + }) +} + +func TestAccCloudflareRuleset_TransformationRuleHeaders(t *testing.T) { + t.Parallel() + rnd := generateRandomResourceName() + zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") + zoneName := os.Getenv("CLOUDFLARE_DOMAIN") + resourceName := "cloudflare_ruleset." + rnd + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckCloudflareRulesetTransformationRuleHeaders(rnd, "transform rule for headers", zoneID, zoneName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "transform rule for headers"), + resource.TestCheckResourceAttr(resourceName, "description", rnd+" ruleset description"), + resource.TestCheckResourceAttr(resourceName, "kind", "zone"), + resource.TestCheckResourceAttr(resourceName, "phase", "http_request_late_transform"), + + resource.TestCheckResourceAttr(resourceName, "rules.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rules.0.action", "rewrite"), + + resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.headers.#", "3"), + + resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.headers.0.name", "example1"), + resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.headers.0.value", "my-http-header-value1"), + resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.headers.0.operation", "set"), + + resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.headers.1.name", "example2"), + resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.headers.1.operation", "set"), + resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.headers.1.expression", "cf.zone.name"), + + resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.headers.2.name", "example3"), + resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.headers.2.operation", "remove"), + ), + }, + }, + }) +} + func testAccCheckCloudflareRulesetMagicTransitSingle(rnd, name, accountID string) string { return fmt.Sprintf(` resource "cloudflare_ruleset" "%[1]s" { @@ -978,3 +1076,92 @@ func testAccCheckCloudflareRulesetManagedWAFWithIDBasedOverrides(rnd, name, zone } }`, rnd, name, zoneID, zoneName) } + +func testAccCheckCloudflareRulesetTransformationRuleURIPath(rnd, name, zoneID, zoneName string) string { + return fmt.Sprintf(` + resource "cloudflare_ruleset" "%[1]s" { + zone_id = "%[3]s" + name = "%[2]s" + description = "%[1]s ruleset description" + kind = "zone" + phase = "http_request_transform" + + rules { + action = "rewrite" + action_parameters { + uri { + path { + value = "/static-rewrite" + } + } + } + + expression = "(http.host eq \"%[4]s\")" + description = "URI transformation path example" + enabled = false + } + }`, rnd, name, zoneID, zoneName) +} + +func testAccCheckCloudflareRulesetTransformationRuleURIQuery(rnd, name, zoneID, zoneName string) string { + return fmt.Sprintf(` + resource "cloudflare_ruleset" "%[1]s" { + zone_id = "%[3]s" + name = "%[2]s" + description = "%[1]s ruleset description" + kind = "zone" + phase = "http_request_transform" + + rules { + action = "rewrite" + action_parameters { + uri { + query { + value = "a=b" + } + } + } + + expression = "(http.host eq \"%[4]s\")" + description = "URI transformation query example" + enabled = false + } + }`, rnd, name, zoneID, zoneName) +} + +func testAccCheckCloudflareRulesetTransformationRuleHeaders(rnd, name, zoneID, zoneName string) string { + return fmt.Sprintf(` + resource "cloudflare_ruleset" "%[1]s" { + zone_id = "%[3]s" + name = "%[2]s" + description = "%[1]s ruleset description" + kind = "zone" + phase = "http_request_late_transform" + + rules { + action = "rewrite" + action_parameters { + headers { + name = "example1" + operation = "set" + value = "my-http-header-value1" + } + + headers { + name = "example2" + operation = "set" + expression = "cf.zone.name" + } + + headers { + name = "example3" + operation = "remove" + } + } + + expression = "true" + description = "example header transformation rule" + enabled = false + } + }`, rnd, name, zoneID, zoneName) +} diff --git a/website/docs/r/ruleset.html.markdown b/website/docs/r/ruleset.html.markdown index 5c8b814094..f80252aa15 100644 --- a/website/docs/r/ruleset.html.markdown +++ b/website/docs/r/ruleset.html.markdown @@ -83,6 +83,88 @@ resource "cloudflare_ruleset" "zone_level_managed_waf_with_category_based_overri enabled = false } } + +# Rewrite the URI path component to a static path +resource "cloudflare_ruleset" "transform_uri_rule_path" { + zone_id = "cb029e245cfdd66dc8d2e570d5dd3322" + name = "transformation rule for URI path" + description = "change the URI path to a new static path" + kind = "zone" + phase = "http_request_transform" + + rules { + action = "rewrite" + action_parameters { + uri { + path { + value = "/my-new-route" + } + } + } + + expression = "(http.host eq \"example.com\" and http.uri.path eq \"/old-path\")" + description = "URI transformation path example" + enabled = true + } +} + +# Rewrite the URI query component to a static query +resource "cloudflare_ruleset" "transform_uri_rule_query" { + zone_id = "cb029e245cfdd66dc8d2e570d5dd3322" + name = "transformation rule for URI query parameter" + description = "change the URI query to a new static query" + kind = "zone" + phase = "http_request_transform" + + rules { + action = "rewrite" + action_parameters { + uri { + query { + value = "old=new_again" + } + } + } + + expression = "true" + description = "URI transformation query example" + enabled = true + } +} + +# Rewrite HTTP headers to a modified values +resource "cloudflare_ruleset" "transform_uri_http_headers" { + zone_id = "cb029e245cfdd66dc8d2e570d5dd3322" + name = "transformation rule for HTTP headers" + description = "modify HTTP headers before reaching origin" + kind = "zone" + phase = "http_request_late_transform" + +rules { + action = "rewrite" + action_parameters { + headers { + name = "example-http-header-1" + operation = "set" + value = "my-http-header-value-1" + } + + headers { + name = "example-http-header-2" + operation = "set" + expression = "cf.zone.name" + } + + headers { + name = "example-http-header-3-to-remove" + operation = "remove" + } + + expression = "true" + description = "example header transformation rule" + enabled = false + } +} ``` ## Argument Reference