From 2ab664116243bea8ca17d913571848d20ca9fddc Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Fri, 6 Dec 2024 13:35:27 +1100 Subject: [PATCH 01/35] remove secondary DNS tests --- .github/workflows/next-acceptance-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/next-acceptance-tests.yml b/.github/workflows/next-acceptance-tests.yml index 39c837c671..fd68938eb5 100644 --- a/.github/workflows/next-acceptance-tests.yml +++ b/.github/workflows/next-acceptance-tests.yml @@ -43,4 +43,4 @@ jobs: id: go - run: go install gotest.tools/gotestsum@latest - run: go get github.com/cloudflare/cloudflare-go/v3@next - - run: TF_ACC=1 gotestsum ./internal/services/{argo_smart_routing,argo_tiered_caching,bot_management,d1_database,dns_firewall,dns_record,healthcheck,list,origin_ca_certificate,queue,r2_bucket,secondary_dns_acl,secondary_dns_peer,secondary_dns_tsig,tiered_cache,total_tls,zone,zone_cache_variants,zone_setting,zone_subscription} -run "^TestAcc" -count 1 -v -timeout 120m + - run: TF_ACC=1 gotestsum ./internal/services/{argo_smart_routing,argo_tiered_caching,bot_management,d1_database,dns_firewall,dns_record,healthcheck,list,origin_ca_certificate,queue,r2_bucket,tiered_cache,total_tls,zone,zone_cache_variants,zone_setting,zone_subscription} -run "^TestAcc" -count 1 -v -timeout 120m From 6ba5e2a3e89aceeba61747d340e18b256e7f13a4 Mon Sep 17 00:00:00 2001 From: Kyle Hiller Date: Thu, 5 Dec 2024 15:53:17 -0500 Subject: [PATCH 02/35] AUTH-6667 document scim_config fields for Access identity providers --- .changelog/4721.txt | 3 ++ ...ema_cloudflare_access_identity_provider.go | 36 +++++++++++-------- 2 files changed, 24 insertions(+), 15 deletions(-) create mode 100644 .changelog/4721.txt diff --git a/.changelog/4721.txt b/.changelog/4721.txt new file mode 100644 index 0000000000..7898c46db0 --- /dev/null +++ b/.changelog/4721.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/access_identity_provider: document scim_config fields +``` diff --git a/internal/sdkv2provider/schema_cloudflare_access_identity_provider.go b/internal/sdkv2provider/schema_cloudflare_access_identity_provider.go index 104cd7bbb4..11e9e6e5e6 100644 --- a/internal/sdkv2provider/schema_cloudflare_access_identity_provider.go +++ b/internal/sdkv2provider/schema_cloudflare_access_identity_provider.go @@ -196,31 +196,37 @@ func resourceCloudflareAccessIdentityProviderSchema() map[string]*schema.Schema Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "enabled": { - Type: schema.TypeBool, - Optional: true, + Type: schema.TypeBool, + Optional: true, + Description: "A flag to enable or disable SCIM for the identity provider.", }, "secret": { - Type: schema.TypeString, - Optional: true, - Computed: true, - Sensitive: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + Sensitive: true, + Description: "A read-only token generated when the SCIM integration is enabled for the first time. It is redacted on subsequent requests. If you lose this you will need to refresh it token at /access/identity_providers/:idpID/refresh_scim_secret.", }, "user_deprovision": { - Type: schema.TypeBool, - Optional: true, + Type: schema.TypeBool, + Optional: true, + Description: "A flag to enable revoking a user's session in Access and Gateway when they have been deprovisioned in the Identity Provider.", }, "seat_deprovision": { - Type: schema.TypeBool, - Optional: true, + Type: schema.TypeBool, + Optional: true, + Description: "A flag to remove a user's seat in Zero Trust when they have been deprovisioned in the Identity Provider. This cannot be enabled unless user_deprovision is also enabled.", }, "group_member_deprovision": { - Type: schema.TypeBool, - Optional: true, + Type: schema.TypeBool, + Optional: true, + Description: "Deprecated. Use `identity_update_behavior`.", }, "identity_update_behavior": { - Type: schema.TypeString, - Optional: true, - Computed: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Indicates how a SCIM event updates a user identity used for policy evaluation. Use \"automatic\" to automatically update a user's identity and augment it with fields from the SCIM user resource. Use \"reauth\" to force re-authentication on group membership updates, user identity update will only occur after successful re-authentication. With \"reauth\" identities will not contain fields from the SCIM user resource. With \"no_action\" identities will not be changed by SCIM updates in any way and users will not be prompted to reauthenticate.", ValidateDiagFunc: func(val interface{}, path cty.Path) diag.Diagnostics { s, ok := val.(string) From 4ebd7d8cf4e66806506576ca70c277f4c3526aaa Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Mon, 9 Dec 2024 11:20:11 +1100 Subject: [PATCH 03/35] `make docs` --- docs/resources/access_identity_provider.md | 12 ++++++------ docs/resources/access_policy.md | 3 +++ .../resources/zero_trust_access_identity_provider.md | 12 ++++++------ docs/resources/zero_trust_access_policy.md | 3 +++ 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/docs/resources/access_identity_provider.md b/docs/resources/access_identity_provider.md index 1665150f55..cec84badcc 100644 --- a/docs/resources/access_identity_provider.md +++ b/docs/resources/access_identity_provider.md @@ -128,12 +128,12 @@ Read-Only: Optional: -- `enabled` (Boolean) -- `group_member_deprovision` (Boolean) -- `identity_update_behavior` (String) -- `seat_deprovision` (Boolean) -- `secret` (String, Sensitive) -- `user_deprovision` (Boolean) +- `enabled` (Boolean) A flag to enable or disable SCIM for the identity provider. +- `group_member_deprovision` (Boolean) Deprecated. Use `identity_update_behavior`. +- `identity_update_behavior` (String) Indicates how a SCIM event updates a user identity used for policy evaluation. Use "automatic" to automatically update a user's identity and augment it with fields from the SCIM user resource. Use "reauth" to force re-authentication on group membership updates, user identity update will only occur after successful re-authentication. With "reauth" identities will not contain fields from the SCIM user resource. With "no_action" identities will not be changed by SCIM updates in any way and users will not be prompted to reauthenticate. +- `seat_deprovision` (Boolean) A flag to remove a user's seat in Zero Trust when they have been deprovisioned in the Identity Provider. This cannot be enabled unless user_deprovision is also enabled. +- `secret` (String, Sensitive) A read-only token generated when the SCIM integration is enabled for the first time. It is redacted on subsequent requests. If you lose this you will need to refresh it token at /access/identity_providers/:idpID/refresh_scim_secret. +- `user_deprovision` (Boolean) A flag to enable revoking a user's session in Access and Gateway when they have been deprovisioned in the Identity Provider. ## Import diff --git a/docs/resources/access_policy.md b/docs/resources/access_policy.md index 44ac5b241f..531387752f 100644 --- a/docs/resources/access_policy.md +++ b/docs/resources/access_policy.md @@ -245,6 +245,9 @@ Required: Required: - `usernames` (List of String) Contains the Unix usernames that may be used when connecting over SSH. + +Optional: + - `allow_email_alias` (Boolean) Allows connecting to Unix username that matches the authenticating email prefix. diff --git a/docs/resources/zero_trust_access_identity_provider.md b/docs/resources/zero_trust_access_identity_provider.md index 53d328b43d..506ea1a68a 100644 --- a/docs/resources/zero_trust_access_identity_provider.md +++ b/docs/resources/zero_trust_access_identity_provider.md @@ -128,12 +128,12 @@ Read-Only: Optional: -- `enabled` (Boolean) -- `group_member_deprovision` (Boolean) -- `identity_update_behavior` (String) -- `seat_deprovision` (Boolean) -- `secret` (String, Sensitive) -- `user_deprovision` (Boolean) +- `enabled` (Boolean) A flag to enable or disable SCIM for the identity provider. +- `group_member_deprovision` (Boolean) Deprecated. Use `identity_update_behavior`. +- `identity_update_behavior` (String) Indicates how a SCIM event updates a user identity used for policy evaluation. Use "automatic" to automatically update a user's identity and augment it with fields from the SCIM user resource. Use "reauth" to force re-authentication on group membership updates, user identity update will only occur after successful re-authentication. With "reauth" identities will not contain fields from the SCIM user resource. With "no_action" identities will not be changed by SCIM updates in any way and users will not be prompted to reauthenticate. +- `seat_deprovision` (Boolean) A flag to remove a user's seat in Zero Trust when they have been deprovisioned in the Identity Provider. This cannot be enabled unless user_deprovision is also enabled. +- `secret` (String, Sensitive) A read-only token generated when the SCIM integration is enabled for the first time. It is redacted on subsequent requests. If you lose this you will need to refresh it token at /access/identity_providers/:idpID/refresh_scim_secret. +- `user_deprovision` (Boolean) A flag to enable revoking a user's session in Access and Gateway when they have been deprovisioned in the Identity Provider. ## Import diff --git a/docs/resources/zero_trust_access_policy.md b/docs/resources/zero_trust_access_policy.md index 6b370b963a..a700d9074c 100644 --- a/docs/resources/zero_trust_access_policy.md +++ b/docs/resources/zero_trust_access_policy.md @@ -206,6 +206,9 @@ Required: Required: - `usernames` (List of String) Contains the Unix usernames that may be used when connecting over SSH. + +Optional: + - `allow_email_alias` (Boolean) Allows connecting to Unix username that matches the authenticating email prefix. From 1c39e979c0c2cefb3aff8006ddb7d60b33baba1b Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Mon, 9 Dec 2024 12:50:37 +1100 Subject: [PATCH 04/35] Update main.go --- tools/cmd/sync-github-issue-to-jira/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cmd/sync-github-issue-to-jira/main.go b/tools/cmd/sync-github-issue-to-jira/main.go index 38f8ec40f7..b9d54a8d88 100644 --- a/tools/cmd/sync-github-issue-to-jira/main.go +++ b/tools/cmd/sync-github-issue-to-jira/main.go @@ -152,7 +152,7 @@ var ( owner: "njones", }, "service/pages": { - teamName: "Cloudflare Pages", + teamName: "Workers Builds & Automation", owner: "nrogers", }, "service/bot_management": { From 064e7f823d800c48f2dc221020b5acc7b63d7672 Mon Sep 17 00:00:00 2001 From: Daniel Walsh Date: Mon, 9 Dec 2024 13:15:03 +0000 Subject: [PATCH 05/35] Update issue sync owners of Pages/Workers --- tools/cmd/sync-github-issue-to-jira/main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/cmd/sync-github-issue-to-jira/main.go b/tools/cmd/sync-github-issue-to-jira/main.go index b9d54a8d88..ecd8dac62c 100644 --- a/tools/cmd/sync-github-issue-to-jira/main.go +++ b/tools/cmd/sync-github-issue-to-jira/main.go @@ -128,7 +128,7 @@ var ( owner: "opayne", }, "service/workers": { - teamName: "Workers Core Platform", + teamName: "Workers Deploy & Config", owner: "laszlo", }, "service/tunnel": { @@ -152,8 +152,8 @@ var ( owner: "njones", }, "service/pages": { - teamName: "Workers Builds & Automation", - owner: "nrogers", + teamName: "Workers Deploy & Config", + owner: "laszlo", }, "service/bot_management": { teamName: "Bot Management", From 895f6c01720460da300e432e92245ae5a8018fac Mon Sep 17 00:00:00 2001 From: Zak Cutner Date: Tue, 3 Dec 2024 10:57:39 -0500 Subject: [PATCH 06/35] Remove logic to preserve ruleset rule refs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This logic tries to detect which rules are the same as before during an update, and preserve the refs (and therefore IDs) of these rules. However, this logic is fallible, since we cannot know the user's true intention, and makes it difficult for us to remove the `version` and `last_updated` fields from rules. If users want to preserve rule refs across updates, they can now actually use the `ref` field directly—and there is no need for this remapping logic anymore. --- .../framework/service/rulesets/resource.go | 159 +--------- .../service/rulesets/resource_test.go | 287 +----------------- 2 files changed, 9 insertions(+), 437 deletions(-) diff --git a/internal/framework/service/rulesets/resource.go b/internal/framework/service/rulesets/resource.go index ab0f93edfa..0357735baa 100644 --- a/internal/framework/service/rulesets/resource.go +++ b/internal/framework/service/rulesets/resource.go @@ -2,7 +2,6 @@ package rulesets import ( "context" - "encoding/json" "errors" "fmt" "reflect" @@ -130,10 +129,9 @@ func (r *RulesetResource) Create(ctx context.Context, req resource.CreateRequest Phase: rulesetPhase, } - rulesetData := data.toRuleset(ctx) - - if len(rulesetData.Rules) > 0 { - rs.Rules = rulesetData.Rules + rulesetRules := data.toRulesetRules(ctx) + if len(rulesetRules) > 0 { + rs.Rules = rulesetRules } ruleset, rulesetCreateErr := r.client.V1.CreateRuleset(ctx, identifier, rs) @@ -146,7 +144,7 @@ func (r *RulesetResource) Create(ctx context.Context, req resource.CreateRequest params := cfv1.UpdateEntrypointRulesetParams{ Phase: rulesetPhase, Description: rulesetDescription, - Rules: rulesetData.Rules, + Rules: rulesetRules, } var err error @@ -225,12 +223,6 @@ func (r *RulesetResource) Update(ctx context.Context, req resource.UpdateRequest accountID := plan.AccountID zoneID := plan.ZoneID.ValueString() - remappedRules, e := remapPreservedRuleRefs(ctx, state, plan) - if e != nil { - resp.Diagnostics.AddError("failed to remap rule IDs from state", e.Error()) - return - } - var identifier *cfv1.ResourceContainer if accountID.ValueString() != "" { identifier = cfv1.AccountIdentifier(accountID.ValueString()) @@ -241,7 +233,7 @@ func (r *RulesetResource) Update(ctx context.Context, req resource.UpdateRequest params := cfv1.UpdateRulesetParams{ ID: state.ID.ValueString(), Description: plan.Description.ValueString(), - Rules: remappedRules, + Rules: plan.toRulesetRules(ctx), } rs, err := r.client.V1.UpdateRuleset(ctx, identifier, params) if err != nil { @@ -790,18 +782,13 @@ func toRulesetResourceModel(ctx context.Context, zoneID, accountID basetypes.Str // // The reverse of this method is `toRulesetResourceModel` which handles building // a state representation using the API response. -func (r *RulesetResourceModel) toRuleset(ctx context.Context) cfv1.Ruleset { - var rs cfv1.Ruleset +func (r *RulesetResourceModel) toRulesetRules(ctx context.Context) []cfv1.RulesetRule { var rules []cfv1.RulesetRule - - rs.ID = r.ID.ValueString() for _, rule := range r.Rules { rules = append(rules, rule.toRulesetRule(ctx)) } - rs.Rules = rules - - return rs + return rules } // toRulesetRule takes a state representation of a Ruleset Rule and transforms @@ -1384,135 +1371,3 @@ func (r *RulesModel) toRulesetRule(ctx context.Context) cfv1.RulesetRule { return rr } - -// ruleRefs is a lookup table for rule IDs with two operations, add and pop. - -// We use add to populate the table from the old value of rules. We use pop to -// look up the ref for the new value of a rule (and remove it from the table). -// -// Internally, both operations serialize the rule to JSON and use the resulting -// string as the lookup key; the ref itself and other computed fields are -// excluded from the JSON. -// -// If a ruleset has multiple copies of the same rule, the copies have a single -// lookup key associated with multiple refs; we preserve order when adding and -// popping the refs. -type ruleRefs struct { - refs map[string][]string -} - -// newRuleRefs creates a new ruleRefs. -func newRuleRefs(rulesetRules []cfv1.RulesetRule, explicitRefs map[string]struct{}) (ruleRefs, error) { - r := ruleRefs{make(map[string][]string)} - for _, rule := range rulesetRules { - if rule.Ref == "" { - // This is unexpected. We only invoke this function for the old - // values of rules, which have their refs populated. - return ruleRefs{}, errors.New("unable to determine ID or ref of existing rule") - } - - if _, ok := explicitRefs[rule.Ref]; ok { - // We should not add explicitly-set refs, to avoid them being - // "stolen" by other rules. - continue - } - - if err := r.add(rule); err != nil { - return ruleRefs{}, err - } - } - - return r, nil -} - -// add stores a ref for the given rule. -func (r *ruleRefs) add(rule cfv1.RulesetRule) error { - key, err := ruleToKey(rule) - if err != nil { - return err - } - - r.refs[key] = append(r.refs[key], rule.Ref) - return nil -} - -// pop removes a ref for the given rule and returns it. If no ref was found for -// the rule, pop returns an empty string. -func (r *ruleRefs) pop(rule cfv1.RulesetRule) (string, error) { - key, err := ruleToKey(rule) - if err != nil { - return "", err - } - - refs := r.refs[key] - if len(refs) == 0 { - return "", nil - } - - ref, refs := refs[0], refs[1:] - r.refs[key] = refs - - return ref, nil -} - -// isEmpty returns true if the store does not contain any rule refs. -func (r *ruleRefs) isEmpty() bool { - return len(r.refs) == 0 -} - -// ruleToKey converts a ruleset rule to a key that can be used to track -// equivalent rules. Internally, it serializes the rule to JSON after removing -// computed fields. -func ruleToKey(rule cfv1.RulesetRule) (string, error) { - // For the purposes of preserving existing rule refs, we don't want to - // include computed fields as a part of the key value. - rule.ID = "" - rule.Ref = "" - rule.Version = nil - rule.LastUpdated = nil - - data, err := json.Marshal(rule) - if err != nil { - return "", err - } - - return string(data), nil -} - -// remapPreservedRuleRefs tries to preserve the refs of rules that have not -// changed in the ruleset, while also allowing users to explicitly set the ref -// if they choose to. -func remapPreservedRuleRefs(ctx context.Context, state, plan *RulesetResourceModel) ([]cfv1.RulesetRule, error) { - currentRuleset := state.toRuleset(ctx) - plannedRuleset := plan.toRuleset(ctx) - - plannedExplicitRefs := make(map[string]struct{}) - for _, rule := range plannedRuleset.Rules { - if rule.Ref != "" { - plannedExplicitRefs[rule.Ref] = struct{}{} - } - } - - refs, err := newRuleRefs(currentRuleset.Rules, plannedExplicitRefs) - if err != nil { - return nil, err - } - - if refs.isEmpty() { - // There are no rule refs when the ruleset is first created. - return plannedRuleset.Rules, nil - } - - for i := range plannedRuleset.Rules { - rule := &plannedRuleset.Rules[i] - - // We should not override refs that have been explicitly set. - if rule.Ref == "" { - if rule.Ref, err = refs.pop(*rule); err != nil { - return nil, err - } - } - } - - return plannedRuleset.Rules, nil -} diff --git a/internal/framework/service/rulesets/resource_test.go b/internal/framework/service/rulesets/resource_test.go index a6646520c3..49ea8be9a2 100644 --- a/internal/framework/service/rulesets/resource_test.go +++ b/internal/framework/service/rulesets/resource_test.go @@ -11,7 +11,6 @@ import ( "github.com/cloudflare/terraform-provider-cloudflare/internal/acctest" "github.com/cloudflare/terraform-provider-cloudflare/internal/utils" "github.com/hashicorp/terraform-plugin-testing/helper/resource" - "github.com/hashicorp/terraform-plugin-testing/terraform" ) func TestMain(m *testing.M) { @@ -851,167 +850,6 @@ func TestAccCloudflareRuleset_RateLimitMitigationTimeoutOfZero(t *testing.T) { }) } -func TestAccCloudflareRuleset_PreserveRuleRefs(t *testing.T) { - // Temporarily unset CLOUDFLARE_API_TOKEN if it is set as the WAF - // service does not yet support the API tokens and it results in - // misleading state error messages. - if os.Getenv("CLOUDFLARE_API_TOKEN") != "" { - t.Setenv("CLOUDFLARE_API_TOKEN", "") - } - - rnd := utils.GenerateRandomResourceName() - zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") - resourceName := "cloudflare_ruleset." + rnd - - var adminRuleRef, loginRuleRef, adminRuleCopyRef, adminRuleExplicitRef string - - resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.TestAccPreCheck(t) }, - ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, - Steps: []resource.TestStep{ - { - // Create a ruleset with two rules (one for /admin, one for - // /login) and get their refs. - Config: testAccCheckCloudflareRulesetTwoCustomRules(rnd, zoneID), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrWith(resourceName, "rules.0.ref", getValue(&adminRuleRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.1.ref", getValue(&loginRuleRef)), - ), - }, - { - // Reverse the order of rules. The refs should remain the same, - // just in reverse order. - Config: testAccCheckCloudflareRulesetTwoCustomRulesReversed(rnd, zoneID), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrWith(resourceName, "rules.0.ref", equalsValue(&loginRuleRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.1.ref", equalsValue(&adminRuleRef)), - ), - }, - { - // Revert to the original version. - Config: testAccCheckCloudflareRulesetTwoCustomRules(rnd, zoneID), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrWith(resourceName, "rules.0.ref", equalsValue(&adminRuleRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.1.ref", equalsValue(&loginRuleRef)), - ), - }, - { - // Append a copy of the admin rule. The first two refs should - // not change. - Config: testAccCheckCloudflareRulesetThreeCustomRules(rnd, zoneID, true), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrWith(resourceName, "rules.0.ref", equalsValue(&adminRuleRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.1.ref", equalsValue(&loginRuleRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.2.ref", notEqualsValue(&adminRuleRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.2.ref", getValue(&adminRuleCopyRef)), - ), - }, - { - // Disable the login rule. Its ref will change, but the admin - // rule refs should remain the same. - Config: testAccCheckCloudflareRulesetThreeCustomRules(rnd, zoneID, false), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrWith(resourceName, "rules.0.ref", equalsValue(&adminRuleRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.1.ref", notEqualsValue(&loginRuleRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.2.ref", equalsValue(&adminRuleCopyRef)), - ), - }, - { - // Revert to the original version. The preserved admin rule ref - // should stay the same, and the login rule ref should change. - Config: testAccCheckCloudflareRulesetTwoCustomRules(rnd, zoneID), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrWith(resourceName, "rules.0.ref", equalsValue(&adminRuleRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.1.ref", notEqualsValue(&loginRuleRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.1.ref", getValue(&loginRuleRef)), - ), - }, - { - // Give the admin rule a ref. - Config: testAccCheckCloudflareRulesetTwoCustomRulesWithRef(rnd, zoneID, true), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(resourceName, "rules.0.ref", "foo"), - resource.TestCheckResourceAttrWith(resourceName, "rules.0.ref", getValue(&adminRuleExplicitRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.1.ref", equalsValue(&loginRuleRef)), - ), - }, - { - // Disable the admin rule. Its ref should stay the same. - Config: testAccCheckCloudflareRulesetTwoCustomRulesWithRef(rnd, zoneID, false), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrWith(resourceName, "rules.0.ref", equalsValue(&adminRuleExplicitRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.1.ref", equalsValue(&loginRuleRef)), - ), - }, - { - // Prepend a copy of the admin rule without an explicit ref. The - // original rule should keep its explicit ref and the new rule - // should get a new ref. - Config: testAccCheckCloudflareRulesetThreeCustomRulesWithRef(rnd, zoneID), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrWith(resourceName, "rules.0.ref", notEqualsValue(&adminRuleRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.0.ref", notEqualsValue(&adminRuleCopyRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.0.ref", notEqualsValue(&adminRuleExplicitRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.1.ref", equalsValue(&adminRuleExplicitRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.2.ref", equalsValue(&loginRuleRef)), - ), - }, - { - // Remove the prepended admin rule and re-enable the original - // admin rule. - Config: testAccCheckCloudflareRulesetTwoCustomRulesWithRef(rnd, zoneID, true), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrWith(resourceName, "rules.0.ref", equalsValue(&adminRuleExplicitRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.1.ref", equalsValue(&loginRuleRef)), - ), - }, - { - // Revert to the original version. The refs should remain - // exactly the same. - Config: testAccCheckCloudflareRulesetTwoCustomRules(rnd, zoneID), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrWith(resourceName, "rules.0.ref", equalsValue(&adminRuleExplicitRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.1.ref", equalsValue(&loginRuleRef)), - ), - }, - { - // Reverse the order of rules. The refs should remain the same, - // just in reverse order. - Config: testAccCheckCloudflareRulesetTwoCustomRulesReversed(rnd, zoneID), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrWith(resourceName, "rules.0.ref", equalsValue(&loginRuleRef)), - resource.TestCheckResourceAttrWith(resourceName, "rules.1.ref", equalsValue(&adminRuleExplicitRef)), - ), - }, - }, - }) -} - -func getValue(result *string) func(string) error { - return func(value string) error { - *result = value - return nil - } -} - -func equalsValue(expected *string) func(string) error { - return func(value string) error { - if value != *expected { - return fmt.Errorf("expected '%s' got '%s'", *expected, value) - } - return nil - } -} - -func notEqualsValue(expected *string) func(string) error { - return func(value string) error { - if value == *expected { - return fmt.Errorf("expected != '%s'", *expected) - } - return nil - } -} - func TestAccCloudflareRuleset_CustomErrors(t *testing.T) { // Temporarily unset CLOUDFLARE_API_TOKEN if it is set as the WAF // service does not yet support the API tokens and it results in @@ -1477,7 +1315,7 @@ func TestAccCloudflareRuleset_ActionParametersHTTPDDoSOverride(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "rules.0.action", "execute"), resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.id", "4d21379b4f9f4bb088e0729962c8b3cf"), - resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.overrides.0.rules.0.id", "fdfdac75430c4c47a959592f0aa5e68a"), + resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.overrides.0.rules.0.id", "dd4d0a93c065441fb7c99729d96c7c08"), resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.overrides.0.rules.0.sensitivity_level", "low"), resource.TestCheckResourceAttr(resourceName, "rules.0.expression", "true"), resource.TestCheckResourceAttr(resourceName, "rules.0.description", "override HTTP DDoS ruleset rule"), @@ -3560,123 +3398,6 @@ func testAccCheckCloudflareRulesetRateLimitWithMitigationTimeoutOfZero(rnd, name }`, rnd, name, zoneID, zoneName) } -func testAccCheckCloudflareRulesetTwoCustomRules(rnd, zoneID string) string { - return fmt.Sprintf(` - resource "cloudflare_ruleset" "%[1]s" { - zone_id = "%[2]s" - name = "Terraform provider test" - description = "%[1]s ruleset description" - kind = "zone" - phase = "http_request_firewall_custom" - rules { - action = "log" - enabled = true - expression = "(http.request.uri.path eq \"/admin\")" - } - rules { - action = "challenge" - enabled = true - expression = "(http.request.uri.path eq \"/login\")" - } - }`, rnd, zoneID) -} - -func testAccCheckCloudflareRulesetTwoCustomRulesReversed(rnd, zoneID string) string { - return fmt.Sprintf(` - resource "cloudflare_ruleset" "%[1]s" { - zone_id = "%[2]s" - name = "Terraform provider test" - description = "%[1]s ruleset description" - kind = "zone" - phase = "http_request_firewall_custom" - rules { - action = "challenge" - enabled = true - expression = "(http.request.uri.path eq \"/login\")" - } - rules { - action = "log" - enabled = true - expression = "(http.request.uri.path eq \"/admin\")" - } - }`, rnd, zoneID) -} - -func testAccCheckCloudflareRulesetThreeCustomRules(rnd, zoneID string, enableLoginRule bool) string { - return fmt.Sprintf(` - resource "cloudflare_ruleset" "%[1]s" { - zone_id = "%[2]s" - name = "Terraform provider test" - description = "%[1]s ruleset description" - kind = "zone" - phase = "http_request_firewall_custom" - rules { - action = "log" - enabled = true - expression = "(http.request.uri.path eq \"/admin\")" - } - rules { - action = "challenge" - enabled = %[3]t - expression = "(http.request.uri.path eq \"/login\")" - } - rules { - action = "log" - enabled = true - expression = "(http.request.uri.path eq \"/admin\")" - } - }`, rnd, zoneID, enableLoginRule) -} - -func testAccCheckCloudflareRulesetTwoCustomRulesWithRef(rnd, zoneID string, enableAdminRule bool) string { - return fmt.Sprintf(` - resource "cloudflare_ruleset" "%[1]s" { - zone_id = "%[2]s" - name = "Terraform provider test" - description = "%[1]s ruleset description" - kind = "zone" - phase = "http_request_firewall_custom" - rules { - action = "log" - enabled = %[3]t - expression = "(http.request.uri.path eq \"/admin\")" - ref = "foo" - } - rules { - action = "challenge" - enabled = true - expression = "(http.request.uri.path eq \"/login\")" - } - }`, rnd, zoneID, enableAdminRule) -} - -func testAccCheckCloudflareRulesetThreeCustomRulesWithRef(rnd, zoneID string) string { - return fmt.Sprintf(` - resource "cloudflare_ruleset" "%[1]s" { - zone_id = "%[2]s" - name = "Terraform provider test" - description = "%[1]s ruleset description" - kind = "zone" - phase = "http_request_firewall_custom" - rules { - action = "log" - enabled = false - expression = "(http.request.uri.path eq \"/admin\")" - } - rules { - action = "log" - enabled = false - expression = "(http.request.uri.path eq \"/admin\")" - ref = "foo" - } - rules { - action = "challenge" - enabled = true - expression = "(http.request.uri.path eq \"/login\")" - } - }`, rnd, zoneID) -} - func testAccCheckCloudflareRulesetActionParametersOverridesActionEnabled(rnd, name, zoneID, zoneName string) string { return fmt.Sprintf(` resource "cloudflare_ruleset" "%[1]s" { @@ -3784,7 +3505,7 @@ func testAccCheckCloudflareRulesetActionParametersHTTPDDosOverride(rnd, name, zo id = "4d21379b4f9f4bb088e0729962c8b3cf" overrides { rules { - id = "fdfdac75430c4c47a959592f0aa5e68a" # requests with odd HTTP headers or URI path + id = "dd4d0a93c065441fb7c99729d96c7c08" # HTTP requests from known botnet sensitivity_level = "low" } } @@ -5057,7 +4778,3 @@ func testAccCloudflareRulesetCacheSettingsBypassBrowserInvalid(rnd, zoneID strin } `, rnd, zoneID) } - -func testAccCheckCloudflareRulesetDestroy(s *terraform.State) error { - return nil -} From 4fd67ce413f0f3a4bbb975474f36fcaafb642547 Mon Sep 17 00:00:00 2001 From: Zak Cutner Date: Tue, 3 Dec 2024 13:29:41 -0500 Subject: [PATCH 07/35] Remove `version` and `last_updated` fields from ruleset rules These fields frequently change, leading to verbose diffs in plans. Since these fields are not really useful within Terraform, we remove them from the schema completely to prevent them from "polluting" diffs. This also matches the behavior we have for the ruleset-level `version` and `last_updated` fields, which are also not represented in the Terraform schema. --- internal/framework/service/rulesets/model.go | 2 -- .../framework/service/rulesets/resource.go | 18 ------------------ internal/framework/service/rulesets/schema.go | 8 -------- 3 files changed, 28 deletions(-) diff --git a/internal/framework/service/rulesets/model.go b/internal/framework/service/rulesets/model.go index 82083cf054..e39c30ddd5 100644 --- a/internal/framework/service/rulesets/model.go +++ b/internal/framework/service/rulesets/model.go @@ -14,7 +14,6 @@ type RulesetResourceModel struct { } type RulesModel struct { - Version types.String `tfsdk:"version"` Action types.String `tfsdk:"action"` ActionParameters []*ActionParametersModel `tfsdk:"action_parameters"` Description types.String `tfsdk:"description"` @@ -22,7 +21,6 @@ type RulesModel struct { ExposedCredentialCheck []*ExposedCredentialCheckModel `tfsdk:"exposed_credential_check"` Expression types.String `tfsdk:"expression"` ID types.String `tfsdk:"id"` - LastUpdated types.String `tfsdk:"last_updated"` Logging []*LoggingModel `tfsdk:"logging"` Ratelimit []*RatelimitModel `tfsdk:"ratelimit"` Ref types.String `tfsdk:"ref"` diff --git a/internal/framework/service/rulesets/resource.go b/internal/framework/service/rulesets/resource.go index 0357735baa..54842794ef 100644 --- a/internal/framework/service/rulesets/resource.go +++ b/internal/framework/service/rulesets/resource.go @@ -7,7 +7,6 @@ import ( "reflect" "sort" "strings" - "time" cfv1 "github.com/cloudflare/cloudflare-go" "github.com/cloudflare/terraform-provider-cloudflare/internal/framework/expanders" @@ -313,13 +312,6 @@ func toRulesetResourceModel(ctx context.Context, zoneID, accountID basetypes.Str Expression: flatteners.String(ruleResponse.Expression), Description: types.StringValue(ruleResponse.Description), Enabled: flatteners.Bool(ruleResponse.Enabled), - Version: flatteners.String(cfv1.String(ruleResponse.Version)), - } - - if ruleResponse.LastUpdated != nil { - rule.LastUpdated = types.StringValue(ruleResponse.LastUpdated.String()) - } else { - rule.LastUpdated = types.StringNull() } // action_parameters @@ -797,7 +789,6 @@ func (r *RulesModel) toRulesetRule(ctx context.Context) cfv1.RulesetRule { rr := cfv1.RulesetRule{ ID: r.ID.ValueString(), Ref: r.Ref.ValueString(), - Version: r.Version.ValueStringPointer(), Action: r.Action.ValueString(), Expression: r.Expression.ValueString(), Description: r.Description.ValueString(), @@ -1360,14 +1351,5 @@ func (r *RulesModel) toRulesetRule(ctx context.Context) cfv1.RulesetRule { } } - if !r.LastUpdated.IsNull() { - if lastUpdated, err := time.Parse( - "2006-01-02 15:04:05.999999999 -0700 MST", - r.LastUpdated.ValueString(), - ); err == nil { - rr.LastUpdated = &lastUpdated - } - } - return rr } diff --git a/internal/framework/service/rulesets/schema.go b/internal/framework/service/rulesets/schema.go index 928813301c..21d94a79fc 100644 --- a/internal/framework/service/rulesets/schema.go +++ b/internal/framework/service/rulesets/schema.go @@ -109,10 +109,6 @@ func (r *RulesetResource) Schema(ctx context.Context, req resource.SchemaRequest Computed: true, MarkdownDescription: "Unique rule identifier.", }, - "version": schema.StringAttribute{ - Computed: true, - MarkdownDescription: "Version of the ruleset to deploy.", - }, "ref": schema.StringAttribute{ Optional: true, Computed: true, @@ -145,10 +141,6 @@ func (r *RulesetResource) Schema(ctx context.Context, req resource.SchemaRequest }, Optional: true, }, - "last_updated": schema.StringAttribute{ - Computed: true, - MarkdownDescription: "The most recent update to this rule.", - }, }, Blocks: map[string]schema.Block{ "action_parameters": schema.ListNestedBlock{ From 34060ffd2c38bd6a17424c5671ee50334bb7d47a Mon Sep 17 00:00:00 2001 From: Zak Cutner Date: Tue, 3 Dec 2024 13:35:47 -0500 Subject: [PATCH 08/35] Prevent ruleset rule IDs incorrectly showing as changed in diffs In the Rulesets API, it is possible to prevent rule IDs from changing across updates by using rule refs. If a rule's ref does not change as part of an update, then neither will its ID. However, Terraform does not know this and always reports that rule IDs may change, which creates very large diffs. In this change, we use a resource plan modifier to "teach" Terraform this rule. --- .../framework/service/rulesets/resource.go | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/internal/framework/service/rulesets/resource.go b/internal/framework/service/rulesets/resource.go index 54842794ef..f2c6fe7f86 100644 --- a/internal/framework/service/rulesets/resource.go +++ b/internal/framework/service/rulesets/resource.go @@ -1353,3 +1353,46 @@ func (r *RulesModel) toRulesetRule(ctx context.Context) cfv1.RulesetRule { return rr } + +func (r *RulesetResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + var state *RulesetResourceModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + var plan *RulesetResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + // Do nothing if there is no state or no plan. + if state == nil || plan == nil { + return + } + + ruleIDsByRef := make(map[string]types.String) + for _, rule := range state.Rules { + if ref := rule.Ref.ValueString(); ref != "" { + ruleIDsByRef[ref] = rule.ID + } + } + + for _, rule := range plan.Rules { + // Do nothing if the rule's ID is a known planned value. + if !rule.ID.IsUnknown() { + continue + } + + // If the rule's ref matches a rule in the state, populate the planned + // value of its ID with the corresponding ID from the state. + if ref := rule.Ref.ValueString(); ref != "" { + if id, ok := ruleIDsByRef[ref]; ok { + rule.ID = id + } + } + } + + resp.Diagnostics.Append(resp.Plan.Set(ctx, plan)...) +} From 9088d8d37cb0bc3ab57cdd825e452ee12bfb5d81 Mon Sep 17 00:00:00 2001 From: Zak Cutner Date: Tue, 3 Dec 2024 13:39:26 -0500 Subject: [PATCH 09/35] Remove version in ruleset rule action parameters This action parameter is used within the execute action, but cannot actually be set to anything other than `latest`. Removing this field from Terraform prevents an issue where it shows up in diffs, due to it being a computed attribute. --- internal/framework/service/rulesets/model.go | 1 - internal/framework/service/rulesets/resource.go | 7 ------- internal/framework/service/rulesets/schema.go | 5 ----- 3 files changed, 13 deletions(-) diff --git a/internal/framework/service/rulesets/model.go b/internal/framework/service/rulesets/model.go index e39c30ddd5..36c27b1f1e 100644 --- a/internal/framework/service/rulesets/model.go +++ b/internal/framework/service/rulesets/model.go @@ -27,7 +27,6 @@ type RulesModel struct { } type ActionParametersModel struct { - Version types.String `tfsdk:"version"` AdditionalCacheablePorts types.Set `tfsdk:"additional_cacheable_ports"` AutomaticHTTPSRewrites types.Bool `tfsdk:"automatic_https_rewrites"` AutoMinify []*ActionParameterAutoMinifyModel `tfsdk:"autominify"` diff --git a/internal/framework/service/rulesets/resource.go b/internal/framework/service/rulesets/resource.go index f2c6fe7f86..84902ef3dc 100644 --- a/internal/framework/service/rulesets/resource.go +++ b/internal/framework/service/rulesets/resource.go @@ -343,7 +343,6 @@ func toRulesetResourceModel(ctx context.Context, zoneID, accountID basetypes.Str OriginErrorPagePassthru: flatteners.Bool(ruleResponse.ActionParameters.OriginErrorPagePassthru), RespectStrongEtags: flatteners.Bool(ruleResponse.ActionParameters.RespectStrongETags), ReadTimeout: flatteners.Int64(int64(cfv1.Uint(ruleResponse.ActionParameters.ReadTimeout))), - Version: flatteners.String(cfv1.String(ruleResponse.ActionParameters.Version)), }) if !reflect.ValueOf(ruleResponse.ActionParameters.Polish).IsNil() { @@ -842,12 +841,6 @@ func (r *RulesModel) toRulesetRule(ctx context.Context) cfv1.RulesetRule { rr.ActionParameters.Ruleset = ap.Ruleset.ValueString() } - if !ap.Version.IsNull() { - if ap.Version.ValueString() != "" { - rr.ActionParameters.Version = cfv1.StringPtr(ap.Version.ValueString()) - } - } - if !ap.Increment.IsNull() { rr.ActionParameters.Increment = int(ap.Increment.ValueInt64()) } diff --git a/internal/framework/service/rulesets/schema.go b/internal/framework/service/rulesets/schema.go index 21d94a79fc..dafa268ffb 100644 --- a/internal/framework/service/rulesets/schema.go +++ b/internal/framework/service/rulesets/schema.go @@ -303,11 +303,6 @@ func (r *RulesetResource) Schema(ctx context.Context, req resource.SchemaRequest Optional: true, MarkdownDescription: "Pass-through error page for origin.", }, - "version": schema.StringAttribute{ - Computed: true, - Optional: true, - MarkdownDescription: "Version of the ruleset to deploy.", - }, }, Blocks: map[string]schema.Block{ "algorithms": schema.ListNestedBlock{ From 9894c3a34312d0f3257a82381d4bfa8ed250f579 Mon Sep 17 00:00:00 2001 From: Zak Cutner Date: Tue, 3 Dec 2024 15:00:05 -0500 Subject: [PATCH 10/35] Add changelog --- .changelog/4697.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changelog/4697.txt diff --git a/.changelog/4697.txt b/.changelog/4697.txt new file mode 100644 index 0000000000..2fd951a01f --- /dev/null +++ b/.changelog/4697.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/cloudflare_ruleset: improve diffs when only some rules are changed +``` + +```release-note:note +resource/cloudflare_ruleset: rules must now be given an explicit `ref` to avoid their IDs changing across ruleset updates, see https://developers.cloudflare.com/terraform/troubleshooting/rule-id-changes/ +``` From f72483d695081d670a7e9b431c76ed75c5a3b67e Mon Sep 17 00:00:00 2001 From: Rex Scaria Date: Mon, 9 Dec 2024 14:44:11 -0500 Subject: [PATCH 11/35] feature: start using a single put call for teams list update --- .changelog/4737.txt | 3 + .../resource_cloudflare_teams_list.go | 97 ++++--------------- 2 files changed, 20 insertions(+), 80 deletions(-) create mode 100644 .changelog/4737.txt diff --git a/.changelog/4737.txt b/.changelog/4737.txt new file mode 100644 index 0000000000..c72f79b61f --- /dev/null +++ b/.changelog/4737.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/cloudflare_teams_list_test: use PUT call to update list items +``` diff --git a/internal/sdkv2provider/resource_cloudflare_teams_list.go b/internal/sdkv2provider/resource_cloudflare_teams_list.go index 14a1447b3f..3138368dbc 100644 --- a/internal/sdkv2provider/resource_cloudflare_teams_list.go +++ b/internal/sdkv2provider/resource_cloudflare_teams_list.go @@ -64,8 +64,8 @@ func resourceCloudflareTeamsListCreate(ctx context.Context, d *schema.ResourceDa itemsWithoutDescription := d.Get("items").(*schema.Set).List() itemsWithDescriptionValues := d.Get("items_with_description").(*schema.Set).List() - allItems := append([]interface{}{}, itemsWithoutDescription...) - allItems = append(allItems, itemsWithDescriptionValues...) + allItems := append([]interface{}{}, itemsWithDescriptionValues...) + allItems = append(allItems, itemsWithoutDescription...) for _, v := range allItems { item, err := convertItemCFTeamsListItems(v) if err != nil { @@ -135,12 +135,25 @@ func resourceCloudflareTeamsListUpdate(ctx context.Context, d *schema.ResourceDa Name: d.Get("name").(string), Type: d.Get("type").(string), Description: d.Get("description").(string), + Items: []cloudflare.TeamsListItem{}, } - tflog.Debug(ctx, fmt.Sprintf("Updating Cloudflare Teams List from struct: %+v", updatedTeamsList)) - accountID := d.Get(consts.AccountIDSchemaKey).(string) + itemsWithDescriptionValues := d.Get("items_with_description").(*schema.Set).List() + itemsWithoutDescription := d.Get("items").(*schema.Set).List() + allItems := append([]interface{}{}, itemsWithDescriptionValues...) + allItems = append(allItems, itemsWithoutDescription...) + for _, v := range allItems { + item, err := convertItemCFTeamsListItems(v) + if err != nil { + return diag.FromErr(fmt.Errorf("error creating Teams List for account %q: %w", accountID, err)) + } + updatedTeamsList.Items = append(updatedTeamsList.Items, *item) + } + + tflog.Debug(ctx, fmt.Sprintf("Updating Cloudflare Teams List from struct: %+v", updatedTeamsList)) + identifier := cloudflare.AccountIdentifier(accountID) teamsList, err := client.UpdateTeamsList(ctx, identifier, updatedTeamsList) if err != nil { @@ -150,62 +163,6 @@ func resourceCloudflareTeamsListUpdate(ctx context.Context, d *schema.ResourceDa return diag.FromErr(fmt.Errorf("failed to find Teams List ID in update response; resource was empty")) } - if d.HasChanges("items", "items_with_description") { - oldItemsIface, newItemsIface := d.GetChange("items") - oldItemsWithDescriptionIface, newItemsWithDescriptionIface := d.GetChange("items_with_description") - - oldItems := oldItemsIface.(*schema.Set).List() - newItems := newItemsIface.(*schema.Set).List() - oldItemsWithDescription := oldItemsWithDescriptionIface.(*schema.Set).List() - newItemsWithDescription := newItemsWithDescriptionIface.(*schema.Set).List() - - convertedOldItems := []cloudflare.TeamsListItem{} - convertedNewItems := []cloudflare.TeamsListItem{} - - for _, v := range oldItems { - item, err := convertItemCFTeamsListItems(v) - if err != nil { - return diag.FromErr(fmt.Errorf("error creating Teams List for account %q: %w", accountID, err)) - } - convertedOldItems = append(convertedOldItems, *item) - } - - for _, v := range oldItemsWithDescription { - item, err := convertItemCFTeamsListItems(v) - if err != nil { - return diag.FromErr(fmt.Errorf("error creating Teams List for account %q: %w", accountID, err)) - } - convertedOldItems = append(convertedOldItems, *item) - } - - for _, v := range newItems { - item, err := convertItemCFTeamsListItems(v) - if err != nil { - return diag.FromErr(fmt.Errorf("error creating Teams List for account %q: %w", accountID, err)) - } - convertedNewItems = append(convertedNewItems, *item) - } - - for _, v := range newItemsWithDescription { - item, err := convertItemCFTeamsListItems(v) - if err != nil { - return diag.FromErr(fmt.Errorf("error creating Teams List for account %q: %w", accountID, err)) - } - convertedNewItems = append(convertedNewItems, *item) - } - - patchTeamsList := cloudflare.PatchTeamsListParams{ID: d.Id()} - setListItemDiff(&patchTeamsList, convertedOldItems, convertedNewItems) - - l, err := client.PatchTeamsList(ctx, identifier, patchTeamsList) - - if err != nil { - return diag.FromErr(fmt.Errorf("error updating Teams List for account %q: %w", accountID, err)) - } - - teamsList.Items = l.Items - } - return resourceCloudflareTeamsListRead(ctx, d, meta) } @@ -246,26 +203,6 @@ func resourceCloudflareTeamsListImport(ctx context.Context, d *schema.ResourceDa return []*schema.ResourceData{d}, nil } -func setListItemDiff(patchList *cloudflare.PatchTeamsListParams, oldItems, newItems []cloudflare.TeamsListItem) { - counts := make(map[cloudflare.TeamsListItem]int) - - for _, item := range newItems { - counts[item] += 1 - } - for _, item := range oldItems { - counts[item] -= 1 - } - - for item, val := range counts { - if val > 0 { - patchList.Append = append(patchList.Append, item) - } - if val < 0 { - patchList.Remove = append(patchList.Remove, item.Value) - } - } -} - func convertItemCFTeamsListItems(item any) (*cloudflare.TeamsListItem, error) { switch item.(type) { case string: From 9669bf92151686ff2698b01bd04263eff5ee25bd Mon Sep 17 00:00:00 2001 From: Michele Russo Date: Tue, 10 Dec 2024 10:27:05 +0100 Subject: [PATCH 12/35] Fix update method --- .../service/leaked_credential_check_rule/resource.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/internal/framework/service/leaked_credential_check_rule/resource.go b/internal/framework/service/leaked_credential_check_rule/resource.go index 454fc00701..1e90cf7625 100644 --- a/internal/framework/service/leaked_credential_check_rule/resource.go +++ b/internal/framework/service/leaked_credential_check_rule/resource.go @@ -113,6 +113,14 @@ func (r *LeakedCredentialCheckRuleResource) Update(ctx context.Context, req reso if resp.Diagnostics.HasError() { return } + var state LeakedCredentialCheckRulesModel + diags = req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + data.ID = state.ID + zoneID := cloudflare.ZoneIdentifier(data.ZoneID.ValueString()) _, err := r.client.V1.LeakedCredentialCheckUpdateDetection(ctx, zoneID, cloudflare.LeakedCredentialCheckUpdateDetectionParams{ LeakedCredentialCheckDetectionEntry: cloudflare.LeakedCredentialCheckDetectionEntry{ From 4595f238114d802563ef2d6b63855c1a6569d046 Mon Sep 17 00:00:00 2001 From: Michele Russo Date: Tue, 10 Dec 2024 10:27:23 +0100 Subject: [PATCH 13/35] Expand acceptance test --- .../resource_test.go | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/internal/framework/service/leaked_credential_check_rule/resource_test.go b/internal/framework/service/leaked_credential_check_rule/resource_test.go index 62714a892d..14643192bf 100644 --- a/internal/framework/service/leaked_credential_check_rule/resource_test.go +++ b/internal/framework/service/leaked_credential_check_rule/resource_test.go @@ -46,7 +46,7 @@ func testSweepCloudflareLCCRules(r string) error { tflog.Error(ctx, fmt.Sprintf("Error deleting a user-defined detection patter for Leaked Credential Check: %s", err)) } } - + return nil } @@ -68,6 +68,18 @@ func TestAccCloudflareLeakedCredentialCheckRule_Basic(t *testing.T) { resource.TestCheckResourceAttr(name+"_first", "username", "lookup_json_string(http.request.body.raw, \"user\")"), resource.TestCheckResourceAttr(name+"_first", "password", "lookup_json_string(http.request.body.raw, \"pass\")"), + resource.TestCheckResourceAttr(name+"_second", "zone_id", zoneID), + resource.TestCheckResourceAttr(name+"_second", "username", "lookup_json_string(http.request.body.raw, \"id\")"), + resource.TestCheckResourceAttr(name+"_second", "password", "lookup_json_string(http.request.body.raw, \"secret\")"), + ), + }, + { + Config: testAccConfigAddHeader(rnd, zoneID, testAccLCCUpdateOneRule(rnd)), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name+"_first", "zone_id", zoneID), + resource.TestCheckResourceAttr(name+"_first", "username", "lookup_json_string(http.request.body.raw, \"username\")"), + resource.TestCheckResourceAttr(name+"_first", "password", "lookup_json_string(http.request.body.raw, \"password\")"), + resource.TestCheckResourceAttr(name+"_second", "zone_id", zoneID), resource.TestCheckResourceAttr(name+"_second", "username", "lookup_json_string(http.request.body.raw, \"id\")"), resource.TestCheckResourceAttr(name+"_second", "password", "lookup_json_string(http.request.body.raw, \"secret\")"), @@ -100,3 +112,18 @@ func testAccLCCTwoSimpleRules(name string) string { password = "lookup_json_string(http.request.body.raw, \"secret\")" }`, name) } + +func testAccLCCUpdateOneRule(name string) string { + return fmt.Sprintf(` + resource "cloudflare_leaked_credential_check_rule" "%[1]s_first" { + zone_id = cloudflare_leaked_credential_check.%[1]s.zone_id + username = "lookup_json_string(http.request.body.raw, \"username\")" + password = "lookup_json_string(http.request.body.raw, \"password\")" + } + + resource "cloudflare_leaked_credential_check_rule" "%[1]s_second" { + zone_id = cloudflare_leaked_credential_check.%[1]s.zone_id + username = "lookup_json_string(http.request.body.raw, \"id\")" + password = "lookup_json_string(http.request.body.raw, \"secret\")" + }`, name) +} From 01883bcb5fd36123375d8ec574c8ed84e1d6e24e Mon Sep 17 00:00:00 2001 From: Michele Russo Date: Tue, 10 Dec 2024 10:36:57 +0100 Subject: [PATCH 14/35] Add changelog --- .changelog/4741.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/4741.txt diff --git a/.changelog/4741.txt b/.changelog/4741.txt new file mode 100644 index 0000000000..4c9de3b447 --- /dev/null +++ b/.changelog/4741.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/cloudflare_leaked_credential_check_rule: Fix bug in update method +``` \ No newline at end of file From 9dccc2acd5f5858da15371764c8702d1e1bb3e56 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Tue, 10 Dec 2024 20:55:22 +1100 Subject: [PATCH 15/35] Update .changelog/4737.txt --- .changelog/4737.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/4737.txt b/.changelog/4737.txt index c72f79b61f..336287c6ad 100644 --- a/.changelog/4737.txt +++ b/.changelog/4737.txt @@ -1,3 +1,3 @@ ```release-note:enhancement -resource/cloudflare_teams_list_test: use PUT call to update list items +resource/cloudflare_teams_list: use PUT call to update list items ``` From b8120d4197ccccf6786f9519fecb62cf3abadfbe Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Wed, 11 Dec 2024 10:24:25 +1100 Subject: [PATCH 16/35] feat(rulesets): add state migration to clean up removed fields Follows on from #4697 to cleanup the state automatically. Signed-off-by: Jacob Bednarz --- go.mod | 1 + go.sum | 2 + .../framework/service/rulesets/migrate.go | 1123 +++++++++++++++++ internal/framework/service/rulesets/schema.go | 1 + 4 files changed, 1127 insertions(+) create mode 100644 internal/framework/service/rulesets/migrate.go diff --git a/go.mod b/go.mod index 5e7cef3553..cb0eb7a9d8 100644 --- a/go.mod +++ b/go.mod @@ -79,6 +79,7 @@ require ( github.com/hashicorp/terraform-json v0.23.0 // indirect github.com/hashicorp/terraform-registry-address v0.2.3 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect + github.com/jinzhu/copier v0.4.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect diff --git a/go.sum b/go.sum index 3d782cecd6..77d48dc86c 100644 --- a/go.sum +++ b/go.sum @@ -149,6 +149,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= +github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= +github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= diff --git a/internal/framework/service/rulesets/migrate.go b/internal/framework/service/rulesets/migrate.go new file mode 100644 index 0000000000..0ca3ba0916 --- /dev/null +++ b/internal/framework/service/rulesets/migrate.go @@ -0,0 +1,1123 @@ +package rulesets + +import ( + "context" + "fmt" + + cfv1 "github.com/cloudflare/cloudflare-go" + "github.com/cloudflare/terraform-provider-cloudflare/internal/consts" + "github.com/cloudflare/terraform-provider-cloudflare/internal/framework/modifiers/defaults" + "github.com/cloudflare/terraform-provider-cloudflare/internal/utils" + "github.com/hashicorp/terraform-plugin-framework-validators/boolvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/jinzhu/copier" +) + +type RulesetResourceModelV0 struct { + AccountID types.String `tfsdk:"account_id"` + Description types.String `tfsdk:"description"` + ID types.String `tfsdk:"id"` + Kind types.String `tfsdk:"kind"` + Name types.String `tfsdk:"name"` + Phase types.String `tfsdk:"phase"` + Rules []*RulesModelV0 `tfsdk:"rules"` + ZoneID types.String `tfsdk:"zone_id"` +} + +type RulesModelV0 struct { + Version types.String `tfsdk:"version"` + Action types.String `tfsdk:"action"` + ActionParameters []*ActionParametersModelV0 `tfsdk:"action_parameters"` + Description types.String `tfsdk:"description"` + Enabled types.Bool `tfsdk:"enabled"` + ExposedCredentialCheck []*ExposedCredentialCheckModel `tfsdk:"exposed_credential_check"` + Expression types.String `tfsdk:"expression"` + ID types.String `tfsdk:"id"` + LastUpdated types.String `tfsdk:"last_updated"` + Logging []*LoggingModel `tfsdk:"logging"` + Ratelimit []*RatelimitModel `tfsdk:"ratelimit"` + Ref types.String `tfsdk:"ref"` +} + +type ActionParametersModelV0 struct { + Version types.String `tfsdk:"version"` + AdditionalCacheablePorts types.Set `tfsdk:"additional_cacheable_ports"` + AutomaticHTTPSRewrites types.Bool `tfsdk:"automatic_https_rewrites"` + AutoMinify []*ActionParameterAutoMinifyModel `tfsdk:"autominify"` + BIC types.Bool `tfsdk:"bic"` + BrowserTTL []*ActionParameterBrowserTTLModel `tfsdk:"browser_ttl"` + Cache types.Bool `tfsdk:"cache"` + CacheKey []*ActionParameterCacheKeyModel `tfsdk:"cache_key"` + CacheReserve []*ActionParameterCacheReserveModel `tfsdk:"cache_reserve"` + Content types.String `tfsdk:"content"` + ContentType types.String `tfsdk:"content_type"` + CookieFields types.Set `tfsdk:"cookie_fields"` + DisableApps types.Bool `tfsdk:"disable_apps"` + DisableRailgun types.Bool `tfsdk:"disable_railgun"` + DisableRUM types.Bool `tfsdk:"disable_rum"` + DisableZaraz types.Bool `tfsdk:"disable_zaraz"` + EdgeTTL []*ActionParameterEdgeTTLModel `tfsdk:"edge_ttl"` + Fonts types.Bool `tfsdk:"fonts"` + EmailObfuscation types.Bool `tfsdk:"email_obfuscation"` + FromList []*ActionParameterFromListModel `tfsdk:"from_list"` + FromValue []*ActionParameterFromValueModel `tfsdk:"from_value"` + Headers []*ActionParametersHeadersModel `tfsdk:"headers"` + HostHeader types.String `tfsdk:"host_header"` + HotlinkProtection types.Bool `tfsdk:"hotlink_protection"` + ID types.String `tfsdk:"id"` + Increment types.Int64 `tfsdk:"increment"` + MatchedData []*ActionParametersMatchedDataModel `tfsdk:"matched_data"` + Mirage types.Bool `tfsdk:"mirage"` + OpportunisticEncryption types.Bool `tfsdk:"opportunistic_encryption"` + Origin []*ActionParameterOriginModel `tfsdk:"origin"` + OriginCacheControl types.Bool `tfsdk:"origin_cache_control"` + OriginErrorPagePassthru types.Bool `tfsdk:"origin_error_page_passthru"` + Overrides []*ActionParameterOverridesModel `tfsdk:"overrides"` + Phases types.Set `tfsdk:"phases"` + Polish types.String `tfsdk:"polish"` + Products types.Set `tfsdk:"products"` + ReadTimeout types.Int64 `tfsdk:"read_timeout"` + RequestFields types.Set `tfsdk:"request_fields"` + RespectStrongEtags types.Bool `tfsdk:"respect_strong_etags"` + Response []*ActionParameterResponseModel `tfsdk:"response"` + ResponseFields types.Set `tfsdk:"response_fields"` + RocketLoader types.Bool `tfsdk:"rocket_loader"` + Rules map[string]types.String `tfsdk:"rules"` + Ruleset types.String `tfsdk:"ruleset"` + Rulesets types.Set `tfsdk:"rulesets"` + SecurityLevel types.String `tfsdk:"security_level"` + ServerSideExcludes types.Bool `tfsdk:"server_side_excludes"` + ServeStale []*ActionParameterServeStaleModel `tfsdk:"serve_stale"` + SNI []*ActionParameterSNIModel `tfsdk:"sni"` + SSL types.String `tfsdk:"ssl"` + StatusCode types.Int64 `tfsdk:"status_code"` + SXG types.Bool `tfsdk:"sxg"` + URI []*ActionParametersURIModel `tfsdk:"uri"` + Algorithms []*ActionParametersCompressionAlgorithmModel `tfsdk:"algorithms"` +} + +func (r *RulesetResource) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader { + return map[int64]resource.StateUpgrader{ + 0: { + PriorSchema: &schema.Schema{ + Attributes: map[string]schema.Attribute{ + consts.IDSchemaKey: schema.StringAttribute{ + Computed: true, + MarkdownDescription: consts.IDSchemaDescription, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + consts.AccountIDSchemaKey: schema.StringAttribute{ + MarkdownDescription: consts.AccountIDSchemaDescription, + Optional: true, + Validators: []validator.String{ + stringvalidator.ConflictsWith( + path.Expression(path.MatchRoot(consts.ZoneIDSchemaKey)), + ), + }, + }, + consts.ZoneIDSchemaKey: schema.StringAttribute{ + MarkdownDescription: consts.ZoneIDSchemaDescription, + Optional: true, + Validators: []validator.String{ + stringvalidator.ConflictsWith( + path.Expression(path.MatchRoot(consts.AccountIDSchemaKey)), + ), + }, + }, + "name": schema.StringAttribute{ + Required: true, + MarkdownDescription: "Name of the ruleset.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + stringplanmodifier.UseStateForUnknown(), + }, + }, + "description": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Brief summary of the ruleset and its intended use.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + Computed: true, + }, + "kind": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.OneOfCaseInsensitive(cfv1.RulesetKindValues()...), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + MarkdownDescription: fmt.Sprintf("Type of Ruleset to create. %s.", utils.RenderAvailableDocumentationValuesStringSlice(cfv1.RulesetKindValues())), + }, + "phase": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.OneOfCaseInsensitive(cfv1.RulesetPhaseValues()...), + sbfmDeprecationWarningValidator{}, + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + stringplanmodifier.UseStateForUnknown(), + }, + MarkdownDescription: fmt.Sprintf("Point in the request/response lifecycle where the ruleset will be created. %s.", utils.RenderAvailableDocumentationValuesStringSlice(cfv1.RulesetPhaseValues())), + }, + }, + Blocks: map[string]schema.Block{ + "rules": schema.ListNestedBlock{ + MarkdownDescription: "List of rules to apply to the ruleset.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + consts.IDSchemaKey: schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Unique rule identifier.", + }, + "version": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Version of the ruleset to deploy.", + }, + "ref": schema.StringAttribute{ + Optional: true, + Computed: true, + MarkdownDescription: "Rule reference.", + }, + "enabled": schema.BoolAttribute{ + Optional: true, + Computed: true, + MarkdownDescription: "Whether the rule is active.", + PlanModifiers: []planmodifier.Bool{ + defaults.DefaultBool(true), + }, + }, + "description": schema.StringAttribute{ + Optional: true, + Computed: true, + MarkdownDescription: "Brief summary of the ruleset rule and its intended use.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "expression": schema.StringAttribute{ + Required: true, + MarkdownDescription: "Criteria for an HTTP request to trigger the ruleset rule action. Uses the Firewall Rules expression language based on Wireshark display filters. Refer to the [Firewall Rules language](https://developers.cloudflare.com/firewall/cf-firewall-language) documentation for all available fields, operators, and functions.", + }, + "action": schema.StringAttribute{ + MarkdownDescription: fmt.Sprintf("Action to perform in the ruleset rule. %s.", utils.RenderAvailableDocumentationValuesStringSlice(cfv1.RulesetRuleActionValues())), + Validators: []validator.String{ + stringvalidator.OneOfCaseInsensitive(cfv1.RulesetRuleActionValues()...), + }, + Optional: true, + }, + "last_updated": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "The most recent update to this rule.", + }, + }, + Blocks: map[string]schema.Block{ + "action_parameters": schema.ListNestedBlock{ + MarkdownDescription: "List of parameters that configure the behavior of the ruleset rule action.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "additional_cacheable_ports": schema.SetAttribute{ + ElementType: types.Int64Type, + Optional: true, + MarkdownDescription: "Specifies uncommon ports to allow cacheable assets to be served from.", + }, + "automatic_https_rewrites": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Turn on or off Cloudflare Automatic HTTPS rewrites.", + }, + "bic": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Inspect the visitor's browser for headers commonly associated with spammers and certain bots.", + }, + "cache": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Whether to cache if expression matches.", + }, + "content": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Content of the custom error response.", + }, + "content_type": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Content-Type of the custom error response.", + }, + "cookie_fields": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "List of cookie values to include as part of custom fields logging.", + }, + "disable_apps": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Turn off all active Cloudflare Apps.", + }, + "disable_railgun": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Turn off railgun feature of the Cloudflare Speed app.", + }, + "disable_zaraz": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Turn off zaraz feature.", + }, + "disable_rum": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Turn off RUM feature.", + }, + "fonts": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Toggle fonts.", + }, + "email_obfuscation": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Turn on or off the Cloudflare Email Obfuscation feature of the Cloudflare Scrape Shield app.", + }, + "host_header": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Host Header that request origin receives.", + }, + "hotlink_protection": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Turn on or off the hotlink protection feature.", + }, + consts.IDSchemaKey: schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Identifier of the action parameter to modify.", + }, + "increment": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "", + }, + "mirage": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Turn on or off Cloudflare Mirage of the Cloudflare Speed app.", + }, + "opportunistic_encryption": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Turn on or off the Cloudflare Opportunistic Encryption feature of the Edge Certificates tab in the Cloudflare SSL/TLS app.", + }, + "origin_cache_control": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Enable or disable the use of a more compliant Cache Control parsing mechanism, enabled by default for most zones.", + }, + "phases": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: fmt.Sprintf("Point in the request/response lifecycle where the ruleset will be created. %s.", utils.RenderAvailableDocumentationValuesStringSlice(cfv1.RulesetPhaseValues())), + }, + "polish": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Apply options from the Polish feature of the Cloudflare Speed app.", + }, + "products": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: fmt.Sprintf("Products to target with the actions. %s.", utils.RenderAvailableDocumentationValuesStringSlice(cfv1.RulesetActionParameterProductValues())), + }, + "read_timeout": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "Specifies a maximum timeout for reading content from an origin server.", + }, + "request_fields": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "List of request headers to include as part of custom fields logging, in lowercase.", + }, + "respect_strong_etags": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Respect strong ETags.", + }, + "response_fields": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "List of response headers to include as part of custom fields logging, in lowercase.", + }, + "rocket_loader": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Turn on or off Cloudflare Rocket Loader in the Cloudflare Speed app.", + }, + "rules": schema.MapAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "Map of managed WAF rule ID to comma-delimited string of ruleset rule IDs. Example: `rules = { \"efb7b8c949ac4650a09736fc376e9aee\" = \"5de7edfa648c4d6891dc3e7f84534ffa,e3a567afc347477d9702d9047e97d760\" }`.", + }, + "ruleset": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Which ruleset ID to target.", + }, + "rulesets": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "List of managed WAF rule IDs to target. Only valid when the `\"action\"` is set to skip.", + }, + "security_level": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Control options for the Security Level feature from the Security app.", + }, + "server_side_excludes": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Turn on or off the Server Side Excludes feature of the Cloudflare Scrape Shield app.", + }, + "ssl": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Control options for the SSL feature of the Edge Certificates tab in the Cloudflare SSL/TLS app.", + }, + "status_code": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "HTTP status code of the custom error response.", + }, + "sxg": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Turn on or off the SXG feature.", + }, + "origin_error_page_passthru": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Pass-through error page for origin.", + }, + "version": schema.StringAttribute{ + Computed: true, + Optional: true, + MarkdownDescription: "Version of the ruleset to deploy.", + }, + }, + Blocks: map[string]schema.Block{ + "algorithms": schema.ListNestedBlock{ + MarkdownDescription: "Compression algorithms to use in order of preference.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Required: true, + MarkdownDescription: fmt.Sprintf("Name of the compression algorithm to use. %s", utils.RenderAvailableDocumentationValuesStringSlice([]string{"zstd", "gzip", "brotli", "auto", "default", "none"})), + Validators: []validator.String{ + stringvalidator.OneOf("zstd", "gzip", "brotli", "auto", "default", "none"), + }, + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtLeast(1), + }, + }, + "uri": schema.ListNestedBlock{ + MarkdownDescription: "List of URI properties to configure for the ruleset rule when performing URL rewrite transformations.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "origin": schema.BoolAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "path": schema.ListNestedBlock{ + MarkdownDescription: "URI path configuration when performing a URL rewrite.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "value": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Static string value of the updated URI path or query string component.", + }, + "expression": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Expression that defines the updated (dynamic) value of the URI path or query string component. Uses the Firewall Rules expression language based on Wireshark display filters. Refer to the [Firewall Rules language](https://developers.cloudflare.com/firewall/cf-firewall-language) documentation for all available fields, operators, and functions.", + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "query": schema.ListNestedBlock{ + MarkdownDescription: "Query string configuration when performing a URL rewrite.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "value": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Static string value of the updated URI path or query string component.", + }, + "expression": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Expression that defines the updated (dynamic) value of the URI path or query string component. Uses the Firewall Rules expression language based on Wireshark display filters. Refer to the [Firewall Rules language](https://developers.cloudflare.com/firewall/cf-firewall-language) documentation for all available fields, operators, and functions.", + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "headers": schema.ListNestedBlock{ + MarkdownDescription: "List of HTTP header modifications to perform in the ruleset rule. Note: Headers are order dependent and must be provided sorted alphabetically ascending based on the `name` value.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Name of the HTTP request header to target.", + }, + "value": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Static value to provide as the HTTP request header value.", + Validators: []validator.String{ + stringvalidator.ConflictsWith(path.Expression(path.MatchRelative().AtParent().AtName("expression"))), + }, + }, + "expression": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Use a value dynamically determined by the Firewall Rules expression language based on Wireshark display filters. Refer to the [Firewall Rules language](https://developers.cloudflare.com/firewall/cf-firewall-language) documentation for all available fields, operators, and functions.", + Validators: []validator.String{ + stringvalidator.ConflictsWith(path.Expression(path.MatchRelative().AtParent().AtName("value"))), + }, + }, + "operation": schema.StringAttribute{ + Optional: true, + MarkdownDescription: fmt.Sprintf("Action to perform on the HTTP request header. %s.", utils.RenderAvailableDocumentationValuesStringSlice(cfv1.RulesetRuleActionParametersHTTPHeaderOperationValues())), + }, + }, + }, + }, + "matched_data": schema.ListNestedBlock{ + MarkdownDescription: "List of properties to configure WAF payload logging.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "public_key": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Public key to use within WAF Ruleset payload logging to view the HTTP request parameters. You can generate a public key [using the `matched-data-cli` command-line tool](https://developers.cloudflare.com/waf/managed-rulesets/payload-logging/command-line/generate-key-pair) or [in the Cloudflare dashboard](https://developers.cloudflare.com/waf/managed-rulesets/payload-logging/configure).", + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "response": schema.ListNestedBlock{ + MarkdownDescription: "List of parameters that configure the response given to end users.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "status_code": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "HTTP status code to send in the response.", + }, + "content_type": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "HTTP content type to send in the response.", + }, + "content": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Body content to include in the response.", + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "autominify": schema.ListNestedBlock{ + MarkdownDescription: "Indicate which file extensions to minify automatically.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "html": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "HTML minification.", + }, + "css": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "CSS minification.", + }, + "js": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "JS minification.", + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "edge_ttl": schema.ListNestedBlock{ + MarkdownDescription: "List of edge TTL parameters to apply to the request.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "mode": schema.StringAttribute{ + Required: true, + Validators: []validator.String{stringvalidator.OneOf("override_origin", "respect_origin", "bypass_by_default")}, + MarkdownDescription: fmt.Sprintf("Mode of the edge TTL. %s", utils.RenderAvailableDocumentationValuesStringSlice([]string{"override_origin", "respect_origin", "bypass_by_default"})), + }, + "default": schema.Int64Attribute{ + Optional: true, + Validators: []validator.Int64{int64validator.AtLeast(1)}, + MarkdownDescription: "Default edge TTL.", + }, + }, + Validators: []validator.Object{EdgeTTLValidator{}}, + Blocks: map[string]schema.Block{ + "status_code_ttl": schema.ListNestedBlock{ + MarkdownDescription: "Edge TTL for the status codes.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "status_code": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "Status code for which the edge TTL is applied.", + Validators: []validator.Int64{ + int64validator.ConflictsWith(path.Expression(path.MatchRelative().AtParent().AtName("status_code_range"))), + }, + }, + "value": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "Status code edge TTL value.", + }, + }, + Blocks: map[string]schema.Block{ + "status_code_range": schema.ListNestedBlock{ + MarkdownDescription: "Status code range for which the edge TTL is applied.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "from": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "From status code.", + }, + "to": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "To status code.", + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + listvalidator.ConflictsWith(path.Expression(path.MatchRelative().AtParent().AtName("status_code"))), + }, + }, + }, + }, + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "browser_ttl": schema.ListNestedBlock{ + MarkdownDescription: "List of browser TTL parameters to apply to the request.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "mode": schema.StringAttribute{ + Required: true, + Validators: []validator.String{stringvalidator.OneOf("override_origin", "respect_origin", "bypass")}, + MarkdownDescription: fmt.Sprintf("Mode of the browser TTL. %s", utils.RenderAvailableDocumentationValuesStringSlice([]string{"override_origin", "respect_origin", "bypass"})), + }, + "default": schema.Int64Attribute{ + Optional: true, + Validators: []validator.Int64{int64validator.AtLeast(1)}, + MarkdownDescription: "Default browser TTL. This value is required when override_origin is set", + }, + }, + Validators: []validator.Object{BrowserTTLValidator{}}, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "serve_stale": schema.ListNestedBlock{ + MarkdownDescription: "List of serve stale parameters to apply to the request.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "disable_stale_while_updating": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Disable stale while updating.", + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "cache_key": schema.ListNestedBlock{ + MarkdownDescription: "List of cache key parameters to apply to the request.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "cache_by_device_type": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Cache by device type.", + Validators: []validator.Bool{ + boolvalidator.ConflictsWith(path.Expression(path.MatchRelative().AtParent().AtName("custom_key").AtAnyListIndex().AtName("user"))), + }, + }, + "ignore_query_strings_order": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Ignore query strings order.", + }, + "cache_deception_armor": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Cache deception armor.", + }, + }, + Blocks: map[string]schema.Block{ + "custom_key": schema.ListNestedBlock{ + MarkdownDescription: "Custom key parameters for the request.", + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "query_string": schema.ListNestedBlock{ + MarkdownDescription: "Query string parameters for the custom key.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "include": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "List of query string parameters to include in the custom key.", + Validators: []validator.Set{ + setvalidator.ConflictsWith(path.Expression(path.MatchRelative().AtParent().AtName("exclude"))), + }, + }, + "exclude": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "List of query string parameters to exclude from the custom key.", + Validators: []validator.Set{ + setvalidator.ConflictsWith(path.Expression(path.MatchRelative().AtParent().AtName("include"))), + }, + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "header": schema.ListNestedBlock{ + MarkdownDescription: "Header parameters for the custom key.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "include": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "List of headers to include in the custom key.", + }, + "check_presence": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "List of headers to check for presence in the custom key.", + }, + "exclude_origin": schema.BoolAttribute{ + Computed: true, + Optional: true, + MarkdownDescription: "Exclude the origin header from the custom key.", + PlanModifiers: []planmodifier.Bool{ + defaults.DefaultBool(false), + }, + }, + "contains": schema.MapAttribute{ + ElementType: types.SetType{ + ElemType: types.StringType, + }, + Optional: true, + MarkdownDescription: "Dictionary of headers mapping to lists of values to check for presence in the custom key.", + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "cookie": schema.ListNestedBlock{ + MarkdownDescription: "Cookie parameters for the custom key.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "include": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "List of cookies to include in the custom key.", + }, + "check_presence": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "List of cookies to check for presence in the custom key.", + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "user": schema.ListNestedBlock{ + MarkdownDescription: "User parameters for the custom key.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "device_type": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Add device type to the custom key.", + }, + "geo": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Add geo data to the custom key.", + }, + "lang": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Add language data to the custom key.", + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + listvalidator.ConflictsWith(path.Expression(path.MatchRelative().AtParent().AtParent().AtParent().AtName("cache_by_device_type"))), + }, + }, + "host": schema.ListNestedBlock{ + MarkdownDescription: "Host parameters for the custom key.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "resolved": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Resolve hostname to IP address.", + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "cache_reserve": schema.ListNestedBlock{ + MarkdownDescription: "List of cache reserve parameters to apply to the request.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "eligible": schema.BoolAttribute{ + Required: true, + MarkdownDescription: "Determines whether Cloudflare will write the eligible resource to cache reserve.", + }, + "minimum_file_size": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "The minimum file size, in bytes, eligible for storage in cache reserve. If omitted and \"eligible\" is true, Cloudflare will use 0 bytes by default.", + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "from_list": schema.ListNestedBlock{ + MarkdownDescription: "Use a list to lookup information for the action.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Name of the list.", + }, + "key": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Expression to use for the list lookup.", + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "from_value": schema.ListNestedBlock{ + MarkdownDescription: "Use a value to lookup information for the action.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "status_code": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "Status code for redirect.", + }, + "preserve_query_string": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Preserve query string for redirect URL.", + }, + }, + Blocks: map[string]schema.Block{ + "target_url": schema.ListNestedBlock{ + MarkdownDescription: "Target URL for redirect.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "value": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Static value to provide as the HTTP request header value.", + Validators: []validator.String{ + stringvalidator.ConflictsWith(path.Expression(path.MatchRelative().AtParent().AtName("expression"))), + }, + }, + "expression": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Use a value dynamically determined by the Firewall Rules expression language based on Wireshark display filters. Refer to the [Firewall Rules language](https://developers.cloudflare.com/firewall/cf-firewall-language) documentation for all available fields, operators, and functions.", + Validators: []validator.String{ + stringvalidator.ConflictsWith(path.Expression(path.MatchRelative().AtParent().AtName("value"))), + }, + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "overrides": schema.ListNestedBlock{ + MarkdownDescription: "List of override configurations to apply to the ruleset.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "enabled": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Defines if the current ruleset-level override enables or disables the ruleset.", + }, + "action": schema.StringAttribute{ + Optional: true, + MarkdownDescription: fmt.Sprintf("Action to perform in the rule-level override. %s.", utils.RenderAvailableDocumentationValuesStringSlice(cfv1.RulesetRuleActionValues())), + }, + "sensitivity_level": schema.StringAttribute{ + Optional: true, + MarkdownDescription: fmt.Sprintf("Sensitivity level to override for all ruleset rules. %s.", utils.RenderAvailableDocumentationValuesStringSlice([]string{"default", "medium", "low", "eoff"})), + Validators: []validator.String{ + stringvalidator.OneOfCaseInsensitive("default", "medium", "low", "eoff"), + }, + }, + }, + Blocks: map[string]schema.Block{ + "categories": schema.ListNestedBlock{ + MarkdownDescription: "List of tag-based overrides.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "category": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Tag name to apply the ruleset rule override to.", + }, + "action": schema.StringAttribute{ + Optional: true, + MarkdownDescription: fmt.Sprintf("Action to perform in the tag-level override. %s.", utils.RenderAvailableDocumentationValuesStringSlice(cfv1.RulesetRuleActionValues())), + Validators: []validator.String{ + stringvalidator.OneOfCaseInsensitive(cfv1.RulesetRuleActionValues()...), + }, + }, + "enabled": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Defines if the current tag-level override enables or disables the ruleset rules with the specified tag.", + }, + }, + }, + }, + "rules": schema.ListNestedBlock{ + MarkdownDescription: "List of rule-based overrides.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + consts.IDSchemaKey: schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Rule ID to apply the override to.", + }, + "action": schema.StringAttribute{ + Optional: true, + MarkdownDescription: fmt.Sprintf("Action to perform in the rule-level override. %s.", utils.RenderAvailableDocumentationValuesStringSlice(cfv1.RulesetRuleActionValues())), + Validators: []validator.String{ + stringvalidator.OneOfCaseInsensitive(cfv1.RulesetRuleActionValues()...), + }, + }, + "enabled": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Defines if the current rule-level override enables or disables the rule.", + }, + "score_threshold": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "Anomaly score threshold to apply in the ruleset rule override. Only applicable to modsecurity-based rulesets.", + }, + "sensitivity_level": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Sensitivity level for a ruleset rule override.", + }, + }, + }, + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "origin": schema.ListNestedBlock{ + MarkdownDescription: "List of properties to change request origin.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "host": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Origin Hostname where request is sent.", + }, + "port": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "Origin Port where request is sent.", + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "sni": schema.ListNestedBlock{ + MarkdownDescription: "List of properties to manange Server Name Indication.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "value": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Value to define for SNI.", + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "ratelimit": schema.ListNestedBlock{ + MarkdownDescription: "List of parameters that configure HTTP rate limiting behaviour.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "characteristics": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "List of parameters that define how Cloudflare tracks the request rate for this rule.", + }, + "period": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "The period of time to consider (in seconds) when evaluating the request rate.", + }, + "requests_per_period": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "The number of requests over the period of time that will trigger the Rate Limiting rule.", + }, + "score_per_period": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "The maximum aggregate score over the period of time that will trigger Rate Limiting rule.", + }, + "score_response_header_name": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Name of HTTP header in the response, set by the origin server, with the score for the current request.", + }, + "mitigation_timeout": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "Once the request rate is reached, the Rate Limiting rule blocks further requests for the period of time defined in this field.", + }, + "counting_expression": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Criteria for counting HTTP requests to trigger the Rate Limiting action. Uses the Firewall Rules expression language based on Wireshark display filters. Refer to the [Firewall Rules language](https://developers.cloudflare.com/firewall/cf-firewall-language) documentation for all available fields, operators, and functions.", + }, + "requests_to_origin": schema.BoolAttribute{ + Optional: true, + Computed: true, + Default: booldefault.StaticBool(false), + MarkdownDescription: "Whether to include requests to origin within the Rate Limiting count.", + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "exposed_credential_check": schema.ListNestedBlock{ + MarkdownDescription: "List of parameters that configure exposed credential checks.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "username_expression": schema.StringAttribute{ + Optional: true, + MarkdownDescription: `Firewall Rules expression language based on Wireshark display filters for where to check for the "username" value. Refer to the [Firewall Rules language](https://developers.cloudflare.com/firewall/cf-firewall-language).`, + }, + "password_expression": schema.StringAttribute{ + Optional: true, + MarkdownDescription: `Firewall Rules expression language based on Wireshark display filters for where to check for the "password" value. Refer to the [Firewall Rules language](https://developers.cloudflare.com/firewall/cf-firewall-language).`, + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "logging": schema.ListNestedBlock{ + MarkdownDescription: "List parameters to configure how the rule generates logs. Only valid for skip action.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "enabled": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Override the default logging behavior when a rule is matched.", + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + }, + }, + }, + }, + }, + + StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { + var priorState RulesetResourceModelV0 + + resp.Diagnostics.Append(req.State.Get(ctx, &priorState)...) + if resp.Diagnostics.HasError() { + return + } + + for _, rule := range priorState.Rules { + rule.LastUpdated = types.StringNull() + rule.Version = types.StringNull() + + for _, actionParameter := range rule.ActionParameters { + actionParameter.Version = types.StringNull() + } + } + + oldRules := priorState.Rules + newRules := []*RulesModel{} + + if err := copier.Copy(&newRules, &oldRules); err != nil { + resp.Diagnostics.AddError("failed to copy existing rules into new struct", err.Error()) + } + + upgradedStateData := RulesetResourceModel{ + AccountID: priorState.AccountID, + Description: priorState.Description, + ID: priorState.ID, + Kind: priorState.Kind, + Name: priorState.Name, + Phase: priorState.Phase, + Rules: newRules, + ZoneID: priorState.ZoneID, + } + + resp.Diagnostics.Append(resp.State.Set(ctx, upgradedStateData)...) + }, + }, + } +} diff --git a/internal/framework/service/rulesets/schema.go b/internal/framework/service/rulesets/schema.go index dafa268ffb..bbf00dff3e 100644 --- a/internal/framework/service/rulesets/schema.go +++ b/internal/framework/service/rulesets/schema.go @@ -26,6 +26,7 @@ import ( func (r *RulesetResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ + Version: 1, MarkdownDescription: heredoc.Doc(` The [Cloudflare Ruleset Engine](https://developers.cloudflare.com/firewall/cf-rulesets) allows you to create and deploy rules and rulesets. From 0831e43d40008725488c3cf0ed01a628af77a648 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Wed, 11 Dec 2024 15:51:09 +1100 Subject: [PATCH 17/35] generate changelog --- CHANGELOG.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b36f2497ca..9bab736066 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ ## 4.48.0 (Unreleased) +NOTES: + +* resource/cloudflare_ruleset: rules must now be given an explicit `ref` to avoid their IDs changing across ruleset updates, see https://developers.cloudflare.com/terraform/troubleshooting/rule-id-changes/ ([#4697](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4697)) + +FEATURES: + +* **New Resource:** `cloudflare_leaked_credential_check` ([#4674](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4674)) +* **New Resource:** `cloudflare_leaked_credential_check_rule` ([#4676](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4676)) +* **New Resource:** `cloudflare_snippet_rules` ([#4565](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4565)) + +ENHANCEMENTS: + +* resource/access_application: add support for destinations and domain_type ([#4661](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4661)) +* resource/access_identity_provider: document scim_config fields ([#4721](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4721)) +* resource/cloudflare_access_policy: adds support for Access infrastructure `allow_email_alias` connection rule flag ([#4665](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4665)) +* resource/cloudflare_ruleset: improve diffs when only some rules are changed ([#4697](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4697)) +* resource/cloudflare_teams_list: use PUT call to update list items ([#4737](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4737)) +* resource/cloudflare_zero_trust_access_policy: adds support for Access infrastructure `allow_email_alias` connection rule flag ([#4665](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4665)) + +BUG FIXES: + +* resource/cloudflare_authenticated_origin_pulls: Fix issue where resources are disabled instead of being destroyed on `tf destroy` ([#4649](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4649)) +* resource/cloudflare_leaked_credential_check_rule: Fix bug in update method ([#4741](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4741)) + +DEPENDENCIES: + +* provider: bump github.com/cloudflare/cloudflare-go from 0.110.0 to 0.111.0 ([#4709](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4709)) +* provider: bump golang.org/x/net from 0.31.0 to 0.32.0 ([#4718](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4718)) + ## 4.47.0 (November 27th, 2024) ENHANCEMENTS: From 2e721b565f0dfac52378e4cba25fad69b328125c Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Wed, 11 Dec 2024 15:52:13 +1100 Subject: [PATCH 18/35] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bab736066..1cae5362be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 4.48.0 (Unreleased) +## 4.49.0 (Unreleased) + +## 4.48.0 (December 11th, 2024) NOTES: From 1acafecea40da9fa4dcddf7252b5c9faa6a7540d Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Wed, 11 Dec 2024 16:47:52 +1100 Subject: [PATCH 19/35] `make docs` --- docs/resources/ruleset.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/resources/ruleset.md b/docs/resources/ruleset.md index 52006e56e3..350e3b9838 100644 --- a/docs/resources/ruleset.md +++ b/docs/resources/ruleset.md @@ -483,8 +483,6 @@ Optional: Read-Only: - `id` (String) Unique rule identifier. -- `last_updated` (String) The most recent update to this rule. -- `version` (String) Version of the ruleset to deploy. ### Nested Schema for `rules.action_parameters` @@ -544,7 +542,6 @@ Optional: - `status_code` (Number) HTTP status code of the custom error response. - `sxg` (Boolean) Turn on or off the SXG feature. - `uri` (Block List) List of URI properties to configure for the ruleset rule when performing URL rewrite transformations. (see [below for nested schema](#nestedblock--rules--action_parameters--uri)) -- `version` (String) Version of the ruleset to deploy. ### Nested Schema for `rules.action_parameters.algorithms` From 25b478ae7897006d901d01b4ff33701d358fcac8 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Wed, 11 Dec 2024 16:48:39 +1100 Subject: [PATCH 20/35] `make docs` --- docs/resources/snippet.md | 37 +++++++++++++++++++++++++++++++++ docs/resources/snippet_rules.md | 37 +++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 docs/resources/snippet.md create mode 100644 docs/resources/snippet_rules.md diff --git a/docs/resources/snippet.md b/docs/resources/snippet.md new file mode 100644 index 0000000000..379fe1bb37 --- /dev/null +++ b/docs/resources/snippet.md @@ -0,0 +1,37 @@ +--- +page_title: "cloudflare_snippet Resource - Cloudflare" +subcategory: "" +description: |- + The Snippet https://developers.cloudflare.com/rules/snippets/ resource allows you to create and manage snippet for a zone. +--- + +# cloudflare_snippet (Resource) + +The [Snippet](https://developers.cloudflare.com/rules/snippets/) resource allows you to create and manage snippet for a zone. + + + +## Schema + +### Required + +- `main_module` (String) Main module file name of the snippet. +- `name` (String) Name of the snippet. +- `zone_id` (String) The zone identifier to target for the resource. + +### Optional + +- `files` (Block Set) List of Snippet Files (see [below for nested schema](#nestedblock--files)) + + +### Nested Schema for `files` + +Required: + +- `name` (String) Name of the snippet file. + +Optional: + +- `content` (String) Content of the snippet file. + + diff --git a/docs/resources/snippet_rules.md b/docs/resources/snippet_rules.md new file mode 100644 index 0000000000..dbf1dd6723 --- /dev/null +++ b/docs/resources/snippet_rules.md @@ -0,0 +1,37 @@ +--- +page_title: "cloudflare_snippet_rules Resource - Cloudflare" +subcategory: "" +description: |- + The Snippet Rules https://developers.cloudflare.com/rules/snippets/ resource allows you to create and manage snippet rules for a zone. +--- + +# cloudflare_snippet_rules (Resource) + +The [Snippet Rules](https://developers.cloudflare.com/rules/snippets/) resource allows you to create and manage snippet rules for a zone. + + + +## Schema + +### Required + +- `zone_id` (String) The zone identifier to target for the resource. + +### Optional + +- `rules` (Block Set) List of Snippet Rules (see [below for nested schema](#nestedblock--rules)) + + +### Nested Schema for `rules` + +Required: + +- `expression` (String) Criteria for an HTTP request to trigger the snippet rule. Uses the Firewall Rules expression language based on Wireshark display filters. +- `snippet_name` (String) Name of the snippet invoked by this rule. + +Optional: + +- `description` (String) Brief summary of the snippet rule and its intended use. +- `enabled` (Boolean) Whether the headers rule is active. + + From a4d4471ea0ccc9e8f6ead4a48d4132d7d405421e Mon Sep 17 00:00:00 2001 From: Zak Cutner Date: Wed, 11 Dec 2024 12:02:37 -0500 Subject: [PATCH 21/35] resource/cloudflare_ruleset: Improve documentation * Update links to the developer documentation. * Remove mention of Firewall Rules, which is deprecated. * Improve consistency of examples. * Add usage of `ref` field in examples. --- docs/resources/ruleset.md | 235 +++++++++--------- .../resources/cloudflare_ruleset/resource.tf | 219 ++++++++-------- internal/framework/service/rulesets/schema.go | 8 +- 3 files changed, 225 insertions(+), 237 deletions(-) diff --git a/docs/resources/ruleset.md b/docs/resources/ruleset.md index 350e3b9838..6a8962d906 100644 --- a/docs/resources/ruleset.md +++ b/docs/resources/ruleset.md @@ -2,23 +2,19 @@ page_title: "cloudflare_ruleset Resource - Cloudflare" subcategory: "" description: |- - The Cloudflare Ruleset Engine https://developers.cloudflare.com/firewall/cf-rulesets + The Cloudflare Ruleset Engine (https://developers.cloudflare.com/ruleset-engine/about/) allows you to create and deploy rules and rulesets. - The engine syntax, inspired by the Wireshark Display Filter language, is the - same syntax used in custom Firewall Rules. Cloudflare uses the Ruleset Engine - in different products, allowing you to configure several products using the same - basic syntax. + Cloudflare uses the Ruleset Engine in different products, allowing + you to configure several products using the same basic syntax. --- # cloudflare_ruleset (Resource) -The [Cloudflare Ruleset Engine](https://developers.cloudflare.com/firewall/cf-rulesets) +The Cloudflare Ruleset Engine (https://developers.cloudflare.com/ruleset-engine/about/) allows you to create and deploy rules and rulesets. -The engine syntax, inspired by the Wireshark Display Filter language, is the -same syntax used in custom Firewall Rules. Cloudflare uses the Ruleset Engine -in different products, allowing you to configure several products using the same -basic syntax. +Cloudflare uses the Ruleset Engine in different products, allowing +you to configure several products using the same basic syntax. ## Example Usage @@ -26,47 +22,51 @@ basic syntax. # Magic Transit resource "cloudflare_ruleset" "magic_transit_example" { account_id = "f037e56e89293a057740de681ac9abbe" - name = "account magic transit" - description = "example magic transit ruleset description" - kind = "root" + name = "My example Magic Transit ruleset" + description = "My example Magic Transit ruleset description" phase = "magic_transit" + kind = "root" rules { - action = "allow" - expression = "tcp.dstport in { 32768..65535 }" + ref = "allow_tcp_ephemeral_ports" description = "Allow TCP Ephemeral Ports" + expression = "tcp.dstport in { 32768..65535 }" + action = "allow" } } # Zone-level WAF Managed Ruleset resource "cloudflare_ruleset" "zone_level_managed_waf" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "managed WAF" - description = "managed WAF ruleset description" - kind = "zone" + name = "My example managed WAF ruleset" + description = "My example managed WAF ruleset description" phase = "http_request_firewall_managed" + kind = "zone" rules { - action = "execute" + ref = "execute_managed_ruleset" + description = "Execute Cloudflare Managed Ruleset on my zone-level phase entry point ruleset" + expression = "(http.host eq \"example.host.com\")" + action = "execute" action_parameters { id = "efb7b8c949ac4650a09736fc376e9aee" } - expression = "(http.host eq \"example.host.com\")" - description = "Execute Cloudflare Managed Ruleset on my zone-level phase entry point ruleset" - enabled = true } } # Zone-level WAF with tag-based overrides resource "cloudflare_ruleset" "zone_level_managed_waf_with_category_based_overrides" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "managed WAF with tag-based overrides" - description = "managed WAF with tag-based overrides ruleset description" - kind = "zone" + name = "My example managed WAF ruleset with tag-based overrides" + description = "My example managed WAF ruleset with tag-based overrides ruleset description" phase = "http_request_firewall_managed" + kind = "zone" rules { - action = "execute" + ref = "execute_managed_ruleset" + description = "Execute Cloudflare Managed Ruleset with overrides to change Wordpress rules to block" + expression = "(http.host eq \"example.host.com\")" + action = "execute" action_parameters { id = "efb7b8c949ac4650a09736fc376e9aee" overrides { @@ -83,23 +83,23 @@ resource "cloudflare_ruleset" "zone_level_managed_waf_with_category_based_overri } } } - - expression = "(http.host eq \"example.host.com\")" - description = "overrides to only enable wordpress rules to block" - enabled = false + enabled = false } } # Rewrite the URI path component to a static path resource "cloudflare_ruleset" "transform_uri_rule_path" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "transform rule for URI path" - description = "change the URI path to a new static path" - kind = "zone" + name = "My example transform ruleset" + description = "My example transform ruleset description" phase = "http_request_transform" + kind = "zone" rules { - action = "rewrite" + ref = "transform_old_path" + description = "Transform old path" + expression = "(http.host eq \"example.com\" and http.request.uri.path eq \"/old-path\")" + action = "rewrite" action_parameters { uri { path { @@ -107,23 +107,22 @@ resource "cloudflare_ruleset" "transform_uri_rule_path" { } } } - - expression = "(http.host eq \"example.com\" and http.request.uri.path eq \"/old-path\")" - description = "example URI path transform rule" - enabled = true } } # Rewrite the URI query component to a static query resource "cloudflare_ruleset" "transform_uri_rule_query" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "transform rule for URI query parameter" - description = "change the URI query to a new static query" - kind = "zone" + name = "My example transform ruleset" + description = "My example transform ruleset description" phase = "http_request_transform" + kind = "zone" rules { - action = "rewrite" + ref = "transform_uri_query_parameter" + description = "Transform URI query parameter" + expression = "(http.host eq \"example.host.com\")" + action = "rewrite" action_parameters { uri { query { @@ -131,23 +130,22 @@ resource "cloudflare_ruleset" "transform_uri_rule_query" { } } } - - expression = "(http.host eq \"example.host.com\")" - description = "URI transformation query example" - enabled = true } } # Rewrite HTTP headers to a modified values resource "cloudflare_ruleset" "transform_uri_http_headers" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "transform rule for HTTP headers" - description = "modify HTTP headers before reaching origin" + name = "My example transform ruleset" + description = "My example transform ruleset description" + phase = "http_request_transform" kind = "zone" - phase = "http_request_late_transform" rules { - action = "rewrite" + ref = "transform_request_headers" + description = "Transform request headers" + expression = "(http.host eq \"example.host.com\")" + action = "rewrite" action_parameters { headers { name = "example-http-header-1" @@ -166,23 +164,22 @@ resource "cloudflare_ruleset" "transform_uri_http_headers" { operation = "remove" } } - - expression = "(http.host eq \"example.host.com\")" - description = "example request header transform rule" - enabled = false } } # HTTP rate limit for an API route resource "cloudflare_ruleset" "rate_limiting_example" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "restrict API requests count" - description = "apply HTTP rate limiting for a route" - kind = "zone" + name = "My example rate limit ruleset" + description = "My example rate limit ruleset description" phase = "http_ratelimit" + kind = "zone" rules { - action = "block" + ref = "rate_limit_api_requests" + description = "Rate limit API requests" + expression = "(http.request.uri.path matches \"^/api/\")" + action = "block" ratelimit { characteristics = [ "cf.colo.id", @@ -192,23 +189,22 @@ resource "cloudflare_ruleset" "rate_limiting_example" { requests_per_period = 100 mitigation_timeout = 600 } - - expression = "(http.request.uri.path matches \"^/api/\")" - description = "rate limit for API" - enabled = true } } # Change origin for an API route resource "cloudflare_ruleset" "http_origin_example" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "Change to some origin" - description = "Change origin for a route" - kind = "zone" + name = "My example origin ruleset" + description = "My example origin ruleset description" phase = "http_request_origin" + kind = "zone" rules { - action = "route" + ref = "change_origin" + description = "Change to some.host" + expression = "(http.request.uri.path matches \"^/api/\")" + action = "route" action_parameters { host_header = "some.host" origin { @@ -216,22 +212,22 @@ resource "cloudflare_ruleset" "http_origin_example" { port = 80 } } - expression = "(http.request.uri.path matches \"^/api/\")" - description = "change origin to some.host" - enabled = true } } # Custom fields logging resource "cloudflare_ruleset" "custom_fields_logging_example" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "log custom fields" - description = "add custom fields to logging" - kind = "zone" + name = "My example log custom field ruleset" + description = "My example log custom field ruleset description" phase = "http_log_custom_fields" + kind = "zone" rules { - action = "log_custom_field" + ref = "log_custom_fields" + description = "Log custom fields" + expression = "(http.host eq \"example.host.com\")" + action = "log_custom_field" action_parameters { request_fields = [ "content-type", @@ -249,23 +245,22 @@ resource "cloudflare_ruleset" "custom_fields_logging_example" { "__cfruid" ] } - - expression = "(http.host eq \"example.host.com\")" - description = "log custom fields rule" - enabled = true } } -# Custom cache keys + settings +# Custom cache keys and settings resource "cloudflare_ruleset" "cache_settings_example" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "set cache settings" - description = "set cache settings for the request" - kind = "zone" + name = "My example cache settings ruleset" + description = "My example cache settings ruleset description" phase = "http_request_cache_settings" + kind = "zone" rules { - action = "set_cache_settings" + ref = "cache_settings" + description = "Set cache settings rule" + expression = "(http.host eq \"example.host.com\")" + action = "set_cache_settings" action_parameters { edge_ttl { mode = "override_origin" @@ -325,44 +320,44 @@ resource "cloudflare_ruleset" "cache_settings_example" { } origin_error_page_passthru = false } - expression = "(http.host eq \"example.host.com\")" - description = "set cache settings rule" - enabled = true } } # Redirects based on a List resource resource "cloudflare_ruleset" "redirect_from_list_example" { account_id = "f037e56e89293a057740de681ac9abbe" - name = "redirects" - description = "Redirect ruleset" - kind = "root" + name = "My example redirect ruleset" + description = "My example redirect ruleset description" phase = "http_request_redirect" + kind = "root" rules { - action = "redirect" + ref = "redirects_from_list" + description = "Apply redirects from redirect_list" + expression = "http.request.full_uri in $redirect_list" + action = "redirect" action_parameters { from_list { name = "redirect_list" key = "http.request.full_uri" } } - expression = "http.request.full_uri in $redirect_list" - description = "Apply redirects from redirect_list" - enabled = true } } # Dynamic Redirects from value resource resource "cloudflare_ruleset" "redirect_from_value_example" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "redirects" - description = "Redirect ruleset" - kind = "zone" + name = "My example dynamic redirect ruleset" + description = "My example dynamic redirect ruleset description" phase = "http_request_dynamic_redirect" + kind = "zone" rules { - action = "redirect" + ref = "redirect_from_value" + description = "Apply redirect from value" + expression = "(http.request.uri.path matches \"^/api/\")" + action = "redirect" action_parameters { from_value { status_code = 301 @@ -372,62 +367,63 @@ resource "cloudflare_ruleset" "redirect_from_value_example" { preserve_query_string = true } } - expression = "(http.request.uri.path matches \"^/api/\")" - description = "Apply redirect from value" - enabled = true } } # Serve some custom error response resource "cloudflare_ruleset" "http_custom_error_example" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "Serve some error response" - description = "Serve some error response" - kind = "zone" + name = "My example custom errors ruleset" + description = "My example custom errors ruleset description" phase = "http_custom_errors" + kind = "zone" + rules { - action = "serve_error" + ref = "serve_some_error_response" + description = "Serve some error response" + expression = "(http.request.uri.path matches \"^/api/\")" + action = "serve_error" action_parameters { content = "some error html" content_type = "text/html" status_code = "530" } - expression = "(http.request.uri.path matches \"^/api/\")" - description = "serve some error response" - enabled = true } } # Set Configuration Rules for an API route resource "cloudflare_ruleset" "http_config_rules_example" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "set config rules" - description = "set config rules for request" - kind = "zone" + name = "My example config settings ruleset" + description = "My example config settings ruleset description" phase = "http_config_settings" + kind = "zone" rules { - action = "set_config" + ref = "set_config_settings" + description = "Set config settings" + expression = "(http.request.uri.path matches \"^/api/\")" + action = "set_config" action_parameters { email_obfuscation = true bic = true } - expression = "(http.request.uri.path matches \"^/api/\")" - description = "set config rules for matching request" - enabled = true } } -# Set compress algorithm for response. +# Set compress algorithm for response resource "cloudflare_ruleset" "response_compress_brotli_html" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "Brotli response compression for HTML" - description = "Response compression ruleset" - kind = "zone" + name = "My example response compression ruleset" + description = "My example response compression description" phase = "http_response_compression" + kind = "zone" rules { - action = "compress_response" + ref = "prefer_brotli_for_html" + description = "Prefer Brotli compression for HTML" + expression = "http.response.content_type.media_type == \"text/html\"" + action = "compress_response" action_parameters { algorithms { name = "brotli" @@ -436,9 +432,6 @@ resource "cloudflare_ruleset" "response_compress_brotli_html" { name = "auto" } } - expression = "http.response.content_type.media_type == \"text/html\"" - description = "Prefer brotli compression for HTML" - enabled = true } } ``` diff --git a/examples/resources/cloudflare_ruleset/resource.tf b/examples/resources/cloudflare_ruleset/resource.tf index fbed325d81..10aa6ff169 100644 --- a/examples/resources/cloudflare_ruleset/resource.tf +++ b/examples/resources/cloudflare_ruleset/resource.tf @@ -1,47 +1,51 @@ # Magic Transit resource "cloudflare_ruleset" "magic_transit_example" { account_id = "f037e56e89293a057740de681ac9abbe" - name = "account magic transit" - description = "example magic transit ruleset description" - kind = "root" + name = "My example Magic Transit ruleset" + description = "My example Magic Transit ruleset description" phase = "magic_transit" + kind = "root" rules { - action = "allow" - expression = "tcp.dstport in { 32768..65535 }" + ref = "allow_tcp_ephemeral_ports" description = "Allow TCP Ephemeral Ports" + expression = "tcp.dstport in { 32768..65535 }" + action = "allow" } } # Zone-level WAF Managed Ruleset resource "cloudflare_ruleset" "zone_level_managed_waf" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "managed WAF" - description = "managed WAF ruleset description" - kind = "zone" + name = "My example managed WAF ruleset" + description = "My example managed WAF ruleset description" phase = "http_request_firewall_managed" + kind = "zone" rules { - action = "execute" + ref = "execute_managed_ruleset" + description = "Execute Cloudflare Managed Ruleset on my zone-level phase entry point ruleset" + expression = "(http.host eq \"example.host.com\")" + action = "execute" action_parameters { id = "efb7b8c949ac4650a09736fc376e9aee" } - expression = "(http.host eq \"example.host.com\")" - description = "Execute Cloudflare Managed Ruleset on my zone-level phase entry point ruleset" - enabled = true } } # Zone-level WAF with tag-based overrides resource "cloudflare_ruleset" "zone_level_managed_waf_with_category_based_overrides" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "managed WAF with tag-based overrides" - description = "managed WAF with tag-based overrides ruleset description" - kind = "zone" + name = "My example managed WAF ruleset with tag-based overrides" + description = "My example managed WAF ruleset with tag-based overrides ruleset description" phase = "http_request_firewall_managed" + kind = "zone" rules { - action = "execute" + ref = "execute_managed_ruleset" + description = "Execute Cloudflare Managed Ruleset with overrides to change Wordpress rules to block" + expression = "(http.host eq \"example.host.com\")" + action = "execute" action_parameters { id = "efb7b8c949ac4650a09736fc376e9aee" overrides { @@ -58,23 +62,23 @@ resource "cloudflare_ruleset" "zone_level_managed_waf_with_category_based_overri } } } - - expression = "(http.host eq \"example.host.com\")" - description = "overrides to only enable wordpress rules to block" - enabled = false + enabled = false } } # Rewrite the URI path component to a static path resource "cloudflare_ruleset" "transform_uri_rule_path" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "transform rule for URI path" - description = "change the URI path to a new static path" - kind = "zone" + name = "My example transform ruleset" + description = "My example transform ruleset description" phase = "http_request_transform" + kind = "zone" rules { - action = "rewrite" + ref = "transform_old_path" + description = "Transform old path" + expression = "(http.host eq \"example.com\" and http.request.uri.path eq \"/old-path\")" + action = "rewrite" action_parameters { uri { path { @@ -82,23 +86,22 @@ resource "cloudflare_ruleset" "transform_uri_rule_path" { } } } - - expression = "(http.host eq \"example.com\" and http.request.uri.path eq \"/old-path\")" - description = "example URI path transform rule" - enabled = true } } # Rewrite the URI query component to a static query resource "cloudflare_ruleset" "transform_uri_rule_query" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "transform rule for URI query parameter" - description = "change the URI query to a new static query" - kind = "zone" + name = "My example transform ruleset" + description = "My example transform ruleset description" phase = "http_request_transform" + kind = "zone" rules { - action = "rewrite" + ref = "transform_uri_query_parameter" + description = "Transform URI query parameter" + expression = "(http.host eq \"example.host.com\")" + action = "rewrite" action_parameters { uri { query { @@ -106,23 +109,22 @@ resource "cloudflare_ruleset" "transform_uri_rule_query" { } } } - - expression = "(http.host eq \"example.host.com\")" - description = "URI transformation query example" - enabled = true } } # Rewrite HTTP headers to a modified values resource "cloudflare_ruleset" "transform_uri_http_headers" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "transform rule for HTTP headers" - description = "modify HTTP headers before reaching origin" + name = "My example transform ruleset" + description = "My example transform ruleset description" + phase = "http_request_transform" kind = "zone" - phase = "http_request_late_transform" rules { - action = "rewrite" + ref = "transform_request_headers" + description = "Transform request headers" + expression = "(http.host eq \"example.host.com\")" + action = "rewrite" action_parameters { headers { name = "example-http-header-1" @@ -141,23 +143,22 @@ resource "cloudflare_ruleset" "transform_uri_http_headers" { operation = "remove" } } - - expression = "(http.host eq \"example.host.com\")" - description = "example request header transform rule" - enabled = false } } # HTTP rate limit for an API route resource "cloudflare_ruleset" "rate_limiting_example" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "restrict API requests count" - description = "apply HTTP rate limiting for a route" - kind = "zone" + name = "My example rate limit ruleset" + description = "My example rate limit ruleset description" phase = "http_ratelimit" + kind = "zone" rules { - action = "block" + ref = "rate_limit_api_requests" + description = "Rate limit API requests" + expression = "(http.request.uri.path matches \"^/api/\")" + action = "block" ratelimit { characteristics = [ "cf.colo.id", @@ -167,23 +168,22 @@ resource "cloudflare_ruleset" "rate_limiting_example" { requests_per_period = 100 mitigation_timeout = 600 } - - expression = "(http.request.uri.path matches \"^/api/\")" - description = "rate limit for API" - enabled = true } } # Change origin for an API route resource "cloudflare_ruleset" "http_origin_example" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "Change to some origin" - description = "Change origin for a route" - kind = "zone" + name = "My example origin ruleset" + description = "My example origin ruleset description" phase = "http_request_origin" + kind = "zone" rules { - action = "route" + ref = "change_origin" + description = "Change to some.host" + expression = "(http.request.uri.path matches \"^/api/\")" + action = "route" action_parameters { host_header = "some.host" origin { @@ -191,22 +191,22 @@ resource "cloudflare_ruleset" "http_origin_example" { port = 80 } } - expression = "(http.request.uri.path matches \"^/api/\")" - description = "change origin to some.host" - enabled = true } } # Custom fields logging resource "cloudflare_ruleset" "custom_fields_logging_example" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "log custom fields" - description = "add custom fields to logging" - kind = "zone" + name = "My example log custom field ruleset" + description = "My example log custom field ruleset description" phase = "http_log_custom_fields" + kind = "zone" rules { - action = "log_custom_field" + ref = "log_custom_fields" + description = "Log custom fields" + expression = "(http.host eq \"example.host.com\")" + action = "log_custom_field" action_parameters { request_fields = [ "content-type", @@ -224,23 +224,22 @@ resource "cloudflare_ruleset" "custom_fields_logging_example" { "__cfruid" ] } - - expression = "(http.host eq \"example.host.com\")" - description = "log custom fields rule" - enabled = true } } -# Custom cache keys + settings +# Custom cache keys and settings resource "cloudflare_ruleset" "cache_settings_example" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "set cache settings" - description = "set cache settings for the request" - kind = "zone" + name = "My example cache settings ruleset" + description = "My example cache settings ruleset description" phase = "http_request_cache_settings" + kind = "zone" rules { - action = "set_cache_settings" + ref = "cache_settings" + description = "Set cache settings rule" + expression = "(http.host eq \"example.host.com\")" + action = "set_cache_settings" action_parameters { edge_ttl { mode = "override_origin" @@ -300,44 +299,44 @@ resource "cloudflare_ruleset" "cache_settings_example" { } origin_error_page_passthru = false } - expression = "(http.host eq \"example.host.com\")" - description = "set cache settings rule" - enabled = true } } # Redirects based on a List resource resource "cloudflare_ruleset" "redirect_from_list_example" { account_id = "f037e56e89293a057740de681ac9abbe" - name = "redirects" - description = "Redirect ruleset" - kind = "root" + name = "My example redirect ruleset" + description = "My example redirect ruleset description" phase = "http_request_redirect" + kind = "root" rules { - action = "redirect" + ref = "redirects_from_list" + description = "Apply redirects from redirect_list" + expression = "http.request.full_uri in $redirect_list" + action = "redirect" action_parameters { from_list { name = "redirect_list" key = "http.request.full_uri" } } - expression = "http.request.full_uri in $redirect_list" - description = "Apply redirects from redirect_list" - enabled = true } } # Dynamic Redirects from value resource resource "cloudflare_ruleset" "redirect_from_value_example" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "redirects" - description = "Redirect ruleset" - kind = "zone" + name = "My example dynamic redirect ruleset" + description = "My example dynamic redirect ruleset description" phase = "http_request_dynamic_redirect" + kind = "zone" rules { - action = "redirect" + ref = "redirect_from_value" + description = "Apply redirect from value" + expression = "(http.request.uri.path matches \"^/api/\")" + action = "redirect" action_parameters { from_value { status_code = 301 @@ -347,62 +346,63 @@ resource "cloudflare_ruleset" "redirect_from_value_example" { preserve_query_string = true } } - expression = "(http.request.uri.path matches \"^/api/\")" - description = "Apply redirect from value" - enabled = true } } # Serve some custom error response resource "cloudflare_ruleset" "http_custom_error_example" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "Serve some error response" - description = "Serve some error response" - kind = "zone" + name = "My example custom errors ruleset" + description = "My example custom errors ruleset description" phase = "http_custom_errors" + kind = "zone" + rules { - action = "serve_error" + ref = "serve_some_error_response" + description = "Serve some error response" + expression = "(http.request.uri.path matches \"^/api/\")" + action = "serve_error" action_parameters { content = "some error html" content_type = "text/html" status_code = "530" } - expression = "(http.request.uri.path matches \"^/api/\")" - description = "serve some error response" - enabled = true } } # Set Configuration Rules for an API route resource "cloudflare_ruleset" "http_config_rules_example" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "set config rules" - description = "set config rules for request" - kind = "zone" + name = "My example config settings ruleset" + description = "My example config settings ruleset description" phase = "http_config_settings" + kind = "zone" rules { - action = "set_config" + ref = "set_config_settings" + description = "Set config settings" + expression = "(http.request.uri.path matches \"^/api/\")" + action = "set_config" action_parameters { email_obfuscation = true bic = true } - expression = "(http.request.uri.path matches \"^/api/\")" - description = "set config rules for matching request" - enabled = true } } -# Set compress algorithm for response. +# Set compress algorithm for response resource "cloudflare_ruleset" "response_compress_brotli_html" { zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - name = "Brotli response compression for HTML" - description = "Response compression ruleset" - kind = "zone" + name = "My example response compression ruleset" + description = "My example response compression description" phase = "http_response_compression" + kind = "zone" rules { - action = "compress_response" + ref = "prefer_brotli_for_html" + description = "Prefer Brotli compression for HTML" + expression = "http.response.content_type.media_type == \"text/html\"" + action = "compress_response" action_parameters { algorithms { name = "brotli" @@ -411,8 +411,5 @@ resource "cloudflare_ruleset" "response_compress_brotli_html" { name = "auto" } } - expression = "http.response.content_type.media_type == \"text/html\"" - description = "Prefer brotli compression for HTML" - enabled = true } } diff --git a/internal/framework/service/rulesets/schema.go b/internal/framework/service/rulesets/schema.go index bbf00dff3e..c430417a2b 100644 --- a/internal/framework/service/rulesets/schema.go +++ b/internal/framework/service/rulesets/schema.go @@ -28,13 +28,11 @@ func (r *RulesetResource) Schema(ctx context.Context, req resource.SchemaRequest resp.Schema = schema.Schema{ Version: 1, MarkdownDescription: heredoc.Doc(` - The [Cloudflare Ruleset Engine](https://developers.cloudflare.com/firewall/cf-rulesets) + The Cloudflare Ruleset Engine (https://developers.cloudflare.com/ruleset-engine/about/) allows you to create and deploy rules and rulesets. - The engine syntax, inspired by the Wireshark Display Filter language, is the - same syntax used in custom Firewall Rules. Cloudflare uses the Ruleset Engine - in different products, allowing you to configure several products using the same - basic syntax. + Cloudflare uses the Ruleset Engine in different products, allowing + you to configure several products using the same basic syntax. `), Attributes: map[string]schema.Attribute{ consts.IDSchemaKey: schema.StringAttribute{ From 57bcdcda044245f358bc6654e9d0b5290ba9ee40 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Thu, 12 Dec 2024 08:16:18 +1100 Subject: [PATCH 22/35] Update 4565.txt --- .changelog/4565.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/4565.txt b/.changelog/4565.txt index 667c22bc4c..d4ed3c86fa 100644 --- a/.changelog/4565.txt +++ b/.changelog/4565.txt @@ -1,4 +1,4 @@ - ```release-note:new-resource +```release-note:new-resource cloudflare_snippet ``` From bd967e2ed9b5a189a6c7d6f308225a9d23e5ae20 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Thu, 12 Dec 2024 08:16:54 +1100 Subject: [PATCH 23/35] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cae5362be..67a986f4ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ FEATURES: * **New Resource:** `cloudflare_leaked_credential_check` ([#4674](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4674)) * **New Resource:** `cloudflare_leaked_credential_check_rule` ([#4676](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4676)) +* **New Resource:** `cloudflare_snippet` ([#4565](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4565)) * **New Resource:** `cloudflare_snippet_rules` ([#4565](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4565)) ENHANCEMENTS: From 3c7037127de26c3b0d67925edb5885485511c26d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Dec 2024 23:52:22 +0000 Subject: [PATCH 24/35] build(deps): bump golang.org/x/crypto from 0.21.0 to 0.31.0 in /tools Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.21.0 to 0.31.0. - [Commits](https://github.com/golang/crypto/compare/v0.21.0...v0.31.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: indirect ... Signed-off-by: dependabot[bot] --- tools/go.mod | 14 +++++++------- tools/go.sum | 32 ++++++++++++++++---------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/tools/go.mod b/tools/go.mod index 2ae6f55778..af4f68e3dd 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -238,16 +238,16 @@ require ( go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4 // indirect - golang.org/x/crypto v0.21.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect golang.org/x/exp/typeparams v0.0.0-20230307190834-24139beb5833 // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.14.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect golang.org/x/vuln v0.0.0-20230110180137-6ad3e3d07815 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/protobuf v1.33.0 // indirect diff --git a/tools/go.sum b/tools/go.sum index 8d93732c4e..11dc8a6ea2 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -990,8 +990,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1038,8 +1038,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1091,8 +1091,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1118,8 +1118,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1195,8 +1195,8 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1204,8 +1204,8 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1220,8 +1220,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1304,8 +1304,8 @@ golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools/gopls v0.13.2 h1:Pyvx6MKvatbX3zzZmdGiFRfQZl0ohPlt2sFxO/5j6Ro= golang.org/x/tools/gopls v0.13.2/go.mod h1:oX62MpkeKkpDdUDJNgcjAvXQ5bs+QgH+L/QMkLQPgQs= golang.org/x/vuln v0.0.0-20230110180137-6ad3e3d07815 h1:A9kONVi4+AnuOr1dopsibH6hLi1Huy54cbeJxnq4vmU= From e342a134362f8774e44b96f469421615c6c67546 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 11 Dec 2024 23:52:36 +0000 Subject: [PATCH 25/35] add CHANGELOG for #4755 --- .changelog/4755.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/4755.txt diff --git a/.changelog/4755.txt b/.changelog/4755.txt new file mode 100644 index 0000000000..408abf9e33 --- /dev/null +++ b/.changelog/4755.txt @@ -0,0 +1,3 @@ +```release-note:dependency +provider: bump golang.org/x/crypto from 0.21.0 to 0.31.0 in /tools +``` From 1e9289c2f84e010c80b981aaca945f8ff9088aba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Dec 2024 23:57:46 +0000 Subject: [PATCH 26/35] build(deps): bump golang.org/x/crypto from 0.30.0 to 0.31.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.30.0 to 0.31.0. - [Commits](https://github.com/golang/crypto/compare/v0.30.0...v0.31.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: indirect ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index cb0eb7a9d8..ee03152b51 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/oklog/run v1.1.0 // indirect github.com/pkg/errors v0.9.1 github.com/zclconf/go-cty v1.15.0 // indirect - golang.org/x/crypto v0.30.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/net v0.32.0 golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect @@ -45,6 +45,7 @@ require ( github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-mux v0.17.0 github.com/hashicorp/terraform-plugin-testing v1.11.0 + github.com/jinzhu/copier v0.4.0 github.com/stretchr/testify v1.10.0 ) @@ -79,7 +80,6 @@ require ( github.com/hashicorp/terraform-json v0.23.0 // indirect github.com/hashicorp/terraform-registry-address v0.2.3 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect - github.com/jinzhu/copier v0.4.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect diff --git a/go.sum b/go.sum index 77d48dc86c..db9920553c 100644 --- a/go.sum +++ b/go.sum @@ -223,8 +223,8 @@ github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6 github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= -golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= From 2820bdf63274bcce8c3cfaf23d6b4d9e35751cb1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 11 Dec 2024 23:57:59 +0000 Subject: [PATCH 27/35] add CHANGELOG for #4756 --- .changelog/4756.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/4756.txt diff --git a/.changelog/4756.txt b/.changelog/4756.txt new file mode 100644 index 0000000000..98bcefa2ba --- /dev/null +++ b/.changelog/4756.txt @@ -0,0 +1,3 @@ +```release-note:dependency +provider: bump golang.org/x/crypto from 0.30.0 to 0.31.0 +``` From 0c22f4b7e1cc39e332827ffcdd278048e6bc2ac2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 23:22:00 +0000 Subject: [PATCH 28/35] build(deps): bump github.com/hashicorp/terraform-plugin-framework-validators Bumps [github.com/hashicorp/terraform-plugin-framework-validators](https://github.com/hashicorp/terraform-plugin-framework-validators) from 0.15.0 to 0.16.0. - [Release notes](https://github.com/hashicorp/terraform-plugin-framework-validators/releases) - [Changelog](https://github.com/hashicorp/terraform-plugin-framework-validators/blob/main/CHANGELOG.md) - [Commits](https://github.com/hashicorp/terraform-plugin-framework-validators/compare/v0.15.0...v0.16.0) --- updated-dependencies: - dependency-name: github.com/hashicorp/terraform-plugin-framework-validators dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ee03152b51..93b95dc6f8 100644 --- a/go.mod +++ b/go.mod @@ -41,7 +41,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/terraform-plugin-framework v1.13.0 - github.com/hashicorp/terraform-plugin-framework-validators v0.15.0 + github.com/hashicorp/terraform-plugin-framework-validators v0.16.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-mux v0.17.0 github.com/hashicorp/terraform-plugin-testing v1.11.0 diff --git a/go.sum b/go.sum index db9920553c..dabe140416 100644 --- a/go.sum +++ b/go.sum @@ -127,8 +127,8 @@ github.com/hashicorp/terraform-json v0.23.0 h1:sniCkExU4iKtTADReHzACkk8fnpQXrdD2 github.com/hashicorp/terraform-json v0.23.0/go.mod h1:MHdXbBAbSg0GvzuWazEGKAn/cyNfIB7mN6y7KJN6y2c= github.com/hashicorp/terraform-plugin-framework v1.13.0 h1:8OTG4+oZUfKgnfTdPTJwZ532Bh2BobF4H+yBiYJ/scw= github.com/hashicorp/terraform-plugin-framework v1.13.0/go.mod h1:j64rwMGpgM3NYXTKuxrCnyubQb/4VKldEKlcG8cvmjU= -github.com/hashicorp/terraform-plugin-framework-validators v0.15.0 h1:RXMmu7JgpFjnI1a5QjMCBb11usrW2OtAG+iOTIj5c9Y= -github.com/hashicorp/terraform-plugin-framework-validators v0.15.0/go.mod h1:Bh89/hNmqsEWug4/XWKYBwtnw3tbz5BAy1L1OgvbIaY= +github.com/hashicorp/terraform-plugin-framework-validators v0.16.0 h1:O9QqGoYDzQT7lwTXUsZEtgabeWW96zUBh47Smn2lkFA= +github.com/hashicorp/terraform-plugin-framework-validators v0.16.0/go.mod h1:Bh89/hNmqsEWug4/XWKYBwtnw3tbz5BAy1L1OgvbIaY= github.com/hashicorp/terraform-plugin-go v0.25.0 h1:oi13cx7xXA6QciMcpcFi/rwA974rdTxjqEhXJjbAyks= github.com/hashicorp/terraform-plugin-go v0.25.0/go.mod h1:+SYagMYadJP86Kvn+TGeV+ofr/R3g4/If0O5sO96MVw= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= From 18487e82eef6af7ab46ccd0d9803929e59912bf9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 12 Dec 2024 23:22:17 +0000 Subject: [PATCH 29/35] add CHANGELOG for #4762 --- .changelog/4762.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/4762.txt diff --git a/.changelog/4762.txt b/.changelog/4762.txt new file mode 100644 index 0000000000..7eac00a8bf --- /dev/null +++ b/.changelog/4762.txt @@ -0,0 +1,3 @@ +```release-note:dependency +provider: bump github.com/hashicorp/terraform-plugin-framework-validators from 0.15.0 to 0.16.0 +``` From 2d9a144f0c812752dd57ea812d61da428ad7c18d Mon Sep 17 00:00:00 2001 From: Pedro Sousa <680496+pedrosousa@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:39:58 +0000 Subject: [PATCH 30/35] [Docs] Update deprecation date --- docs/resources/filter.md | 2 +- docs/resources/firewall_rule.md | 2 +- docs/resources/rate_limit.md | 2 +- internal/sdkv2provider/resource_cloudflare_filter.go | 2 +- internal/sdkv2provider/resource_cloudflare_firewall_rule.go | 2 +- internal/sdkv2provider/resource_cloudflare_rate_limit.go | 2 +- templates/resources/filter.md.tmpl | 2 +- templates/resources/firewall_rule.md.tmpl | 2 +- templates/resources/rate_limit.md.tmpl | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/resources/filter.md b/docs/resources/filter.md index b17bc92490..9ec58fbd06 100644 --- a/docs/resources/filter.md +++ b/docs/resources/filter.md @@ -13,7 +13,7 @@ Filter expressions that can be referenced across multiple features, e.g. Firewall Rules. See [what is a filter](https://developers.cloudflare.com/firewall/api/cf-filters/what-is-a-filter/) for more details and available fields and operators. -~> `cloudflare_filter` is in a deprecation phase until January 15th, 2025. +~> `cloudflare_filter` is in a deprecation phase until June 15th, 2025. During this time period, this resource is still fully supported but you are strongly advised to move to the `cloudflare_ruleset` resource. Full details can be found in the diff --git a/docs/resources/firewall_rule.md b/docs/resources/firewall_rule.md index 3a42b92e20..c83b4397e2 100644 --- a/docs/resources/firewall_rule.md +++ b/docs/resources/firewall_rule.md @@ -20,7 +20,7 @@ rule creation. Filter expressions needs to be created first before using Firewall Rule. -~> `cloudflare_firewall_rule` is in a deprecation phase until January 15th, 2025. +~> `cloudflare_firewall_rule` is in a deprecation phase until June 15th, 2025. During this time period, this resource is still fully supported but you are strongly advised to move to the `cloudflare_ruleset` resource. Full details can be found in the diff --git a/docs/resources/rate_limit.md b/docs/resources/rate_limit.md index 3b38f9430b..010dee68ae 100644 --- a/docs/resources/rate_limit.md +++ b/docs/resources/rate_limit.md @@ -13,7 +13,7 @@ Provides a Cloudflare rate limit resource for a given zone. This can be used to limit the traffic you receive zone-wide, or matching more specific types of requests/responses. -~> `cloudflare_rate_limit` is in a deprecation phase until January 15th, 2025. +~> `cloudflare_rate_limit` is in a deprecation phase until June 15th, 2025. During this time period, this resource is still fully supported but you are strongly advised to move to the `cloudflare_ruleset` resource. Full details can be found in the diff --git a/internal/sdkv2provider/resource_cloudflare_filter.go b/internal/sdkv2provider/resource_cloudflare_filter.go index 0916199b30..9892590241 100644 --- a/internal/sdkv2provider/resource_cloudflare_filter.go +++ b/internal/sdkv2provider/resource_cloudflare_filter.go @@ -30,7 +30,7 @@ func resourceCloudflareFilter() *schema.Resource { for more details and available fields and operators. `), DeprecationMessage: heredoc.Doc(fmt.Sprintf(` - %s resource is in a deprecation phase until January 15th, 2025. + %s resource is in a deprecation phase until June 15th, 2025. During this time period, this resource is still fully supported but you are strongly advised to move to the %s resource. diff --git a/internal/sdkv2provider/resource_cloudflare_firewall_rule.go b/internal/sdkv2provider/resource_cloudflare_firewall_rule.go index 7758e7d5dc..a007af3a23 100644 --- a/internal/sdkv2provider/resource_cloudflare_firewall_rule.go +++ b/internal/sdkv2provider/resource_cloudflare_firewall_rule.go @@ -35,7 +35,7 @@ func resourceCloudflareFirewallRule() *schema.Resource { Rule. `), DeprecationMessage: heredoc.Doc(fmt.Sprintf(` - %s resource is in a deprecation phase until January 15th, 2025. + %s resource is in a deprecation phase until June 15th, 2025. During this time period, this resource is still fully supported but you are strongly advised to move to the %s resource. diff --git a/internal/sdkv2provider/resource_cloudflare_rate_limit.go b/internal/sdkv2provider/resource_cloudflare_rate_limit.go index 8b9994dafe..bff3c01338 100644 --- a/internal/sdkv2provider/resource_cloudflare_rate_limit.go +++ b/internal/sdkv2provider/resource_cloudflare_rate_limit.go @@ -30,7 +30,7 @@ func resourceCloudflareRateLimit() *schema.Resource { specific types of requests/responses. `), DeprecationMessage: heredoc.Doc(fmt.Sprintf(` - %s resource is in a deprecation phase until January 15th, 2025. + %s resource is in a deprecation phase until June 15th, 2025. During this time period, this resource is still fully supported but you are strongly advised to move to the %s resource. diff --git a/templates/resources/filter.md.tmpl b/templates/resources/filter.md.tmpl index 39f7167092..b8e8cf39a8 100644 --- a/templates/resources/filter.md.tmpl +++ b/templates/resources/filter.md.tmpl @@ -9,7 +9,7 @@ description: |- {{ .Description | trimspace }} -~> `cloudflare_filter` is in a deprecation phase until January 15th, 2025. +~> `cloudflare_filter` is in a deprecation phase until June 15th, 2025. During this time period, this resource is still fully supported but you are strongly advised to move to the `cloudflare_ruleset` resource. Full details can be found in the diff --git a/templates/resources/firewall_rule.md.tmpl b/templates/resources/firewall_rule.md.tmpl index 286338e7bf..e246704590 100644 --- a/templates/resources/firewall_rule.md.tmpl +++ b/templates/resources/firewall_rule.md.tmpl @@ -9,7 +9,7 @@ description: |- {{ .Description | trimspace }} -~> `cloudflare_firewall_rule` is in a deprecation phase until January 15th, 2025. +~> `cloudflare_firewall_rule` is in a deprecation phase until June 15th, 2025. During this time period, this resource is still fully supported but you are strongly advised to move to the `cloudflare_ruleset` resource. Full details can be found in the diff --git a/templates/resources/rate_limit.md.tmpl b/templates/resources/rate_limit.md.tmpl index 1ee9912082..6f87a4c714 100644 --- a/templates/resources/rate_limit.md.tmpl +++ b/templates/resources/rate_limit.md.tmpl @@ -9,7 +9,7 @@ description: |- {{ .Description | trimspace }} -~> `cloudflare_rate_limit` is in a deprecation phase until January 15th, 2025. +~> `cloudflare_rate_limit` is in a deprecation phase until June 15th, 2025. During this time period, this resource is still fully supported but you are strongly advised to move to the `cloudflare_ruleset` resource. Full details can be found in the From 2d8ead8a169c4035e1c7016bcd063301646ae579 Mon Sep 17 00:00:00 2001 From: Pedro Sousa <680496+pedrosousa@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:46:45 +0000 Subject: [PATCH 31/35] [Docs] cloud_connector_rules: Add link to Dev Docs --- docs/resources/cloud_connector_rules.md | 4 ++-- internal/framework/service/cloud_connector_rules/schema.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/resources/cloud_connector_rules.md b/docs/resources/cloud_connector_rules.md index 6625c152da..e4d2ee0cae 100644 --- a/docs/resources/cloud_connector_rules.md +++ b/docs/resources/cloud_connector_rules.md @@ -2,12 +2,12 @@ page_title: "cloudflare_cloud_connector_rules Resource - Cloudflare" subcategory: "" description: |- - The Cloud Connector Rules add link to doc resource allows you to create and manage cloud connector rules for a zone. + The Cloud Connector Rules resource allows you to create and manage cloud connector rules for a zone. --- # cloudflare_cloud_connector_rules (Resource) -The [Cloud Connector Rules](add link to doc) resource allows you to create and manage cloud connector rules for a zone. +The [Cloud Connector Rules](https://developers.cloudflare.com/rules/cloud-connector/) resource allows you to create and manage cloud connector rules for a zone. ## Example Usage diff --git a/internal/framework/service/cloud_connector_rules/schema.go b/internal/framework/service/cloud_connector_rules/schema.go index f5f27a3aee..c55340e96d 100644 --- a/internal/framework/service/cloud_connector_rules/schema.go +++ b/internal/framework/service/cloud_connector_rules/schema.go @@ -19,7 +19,7 @@ import ( func (r *CloudConnectorRulesResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ MarkdownDescription: heredoc.Doc(` - The [Cloud Connector Rules](add link to doc) resource allows you to create and manage cloud connector rules for a zone. + The [Cloud Connector Rules](https://developers.cloudflare.com/rules/cloud-connector/) resource allows you to create and manage cloud connector rules for a zone. `), Version: 1, From aaf5f2ecd3912b5772c53b964e8e3ea86b0138e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Dec 2024 23:23:08 +0000 Subject: [PATCH 32/35] build(deps): bump golang.org/x/net from 0.32.0 to 0.33.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.32.0 to 0.33.0. - [Commits](https://github.com/golang/net/compare/v0.32.0...v0.33.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 93b95dc6f8..5ba24453f3 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/zclconf/go-cty v1.15.0 // indirect golang.org/x/crypto v0.31.0 // indirect - golang.org/x/net v0.32.0 + golang.org/x/net v0.33.0 golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.8.0 // indirect diff --git a/go.sum b/go.sum index dabe140416..d64aa7b089 100644 --- a/go.sum +++ b/go.sum @@ -232,8 +232,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= -golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= From 56dddc86661f8222d23773e1922262b6a60a77f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Dec 2024 23:23:13 +0000 Subject: [PATCH 33/35] build(deps): bump github.com/cloudflare/cloudflare-go Bumps [github.com/cloudflare/cloudflare-go](https://github.com/cloudflare/cloudflare-go) from 0.111.0 to 0.112.0. - [Release notes](https://github.com/cloudflare/cloudflare-go/releases) - [Changelog](https://github.com/cloudflare/cloudflare-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/cloudflare/cloudflare-go/compare/v0.111.0...v0.112.0) --- updated-dependencies: - dependency-name: github.com/cloudflare/cloudflare-go dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 93b95dc6f8..10b8f1f80b 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.23.3 require ( github.com/agext/levenshtein v1.2.3 // indirect - github.com/cloudflare/cloudflare-go v0.111.0 + github.com/cloudflare/cloudflare-go v0.112.0 github.com/fatih/color v1.16.0 // indirect github.com/google/uuid v1.6.0 github.com/hashicorp/errwrap v1.1.0 // indirect @@ -68,7 +68,7 @@ require ( github.com/aws/smithy-go v1.21.0 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/goccy/go-json v0.10.3 // indirect + github.com/goccy/go-json v0.10.4 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect diff --git a/go.sum b/go.sum index dabe140416..9ede536107 100644 --- a/go.sum +++ b/go.sum @@ -51,8 +51,8 @@ github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZ github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= -github.com/cloudflare/cloudflare-go v0.111.0 h1:bFgl5OyR7iaV9DkTaoI2jU8X4rXDzEaFDaPfMTp+Ewo= -github.com/cloudflare/cloudflare-go v0.111.0/go.mod h1:w5c4Vm00JjZM+W0mPi6QOC+eWLncGQPURtgDck3z5xU= +github.com/cloudflare/cloudflare-go v0.112.0 h1:caFwqXdGJCl3rjVMgbPEn8iCYAg9JsRYV3dIVQE5d7g= +github.com/cloudflare/cloudflare-go v0.112.0/go.mod h1:QB55kuJ5ZTeLNFcLJePfMuBilhu/LDKpLBmKFQIoSZ0= github.com/cloudflare/cloudflare-go/v2 v2.4.0 h1:gys/26GoVDklgfq8NYV39WgvOEwzK/XAqYObmnI6iFg= github.com/cloudflare/cloudflare-go/v2 v2.4.0/go.mod h1:AoIzb05z/rvdJLztPct4tSa+3IqXJJ6c+pbUFMOlTr8= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -74,8 +74,8 @@ github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZt github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= -github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= +github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= From 529f143cdcf2f2f6b1669930d7c949b861c833db Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 18 Dec 2024 23:27:06 +0000 Subject: [PATCH 34/35] add CHANGELOG for #4802 --- .changelog/4802.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/4802.txt diff --git a/.changelog/4802.txt b/.changelog/4802.txt new file mode 100644 index 0000000000..6c25bb2133 --- /dev/null +++ b/.changelog/4802.txt @@ -0,0 +1,3 @@ +```release-note:dependency +provider: bump golang.org/x/net from 0.32.0 to 0.33.0 +``` From a41692b786bd2e6db2bc4a4e4a08ea5cda6ac955 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 18 Dec 2024 23:27:30 +0000 Subject: [PATCH 35/35] add CHANGELOG for #4803 --- .changelog/4803.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/4803.txt diff --git a/.changelog/4803.txt b/.changelog/4803.txt new file mode 100644 index 0000000000..f283139993 --- /dev/null +++ b/.changelog/4803.txt @@ -0,0 +1,3 @@ +```release-note:dependency +provider: bump github.com/cloudflare/cloudflare-go from 0.111.0 to 0.112.0 +```