diff --git a/.changelog/3521.txt b/.changelog/3521.txt new file mode 100644 index 0000000000..e654ba4012 --- /dev/null +++ b/.changelog/3521.txt @@ -0,0 +1,3 @@ +```release-note:note +resource/zone_settings_override: deprecate `minify` setting and include state migration to remove from local state. You should immediately remove the configuration from the resource to prevent permadiffs. +``` diff --git a/docs/resources/zone_settings_override.md b/docs/resources/zone_settings_override.md index f23fb86046..402e569dd8 100644 --- a/docs/resources/zone_settings_override.md +++ b/docs/resources/zone_settings_override.md @@ -104,7 +104,7 @@ Optional: - `log_to_cloudflare` (String) - `max_upload` (Number) - `min_tls_version` (String) -- `minify` (Block List, Max: 1) (see [below for nested schema](#nestedblock--settings--minify)) +- `minify` (Block List, Max: 1, Deprecated) (see [below for nested schema](#nestedblock--settings--minify)) - `mirage` (String) - `mobile_redirect` (Block List, Max: 1, Deprecated) (see [below for nested schema](#nestedblock--settings--mobile_redirect)) - `nel` (Block List, Max: 1) (see [below for nested schema](#nestedblock--settings--nel)) diff --git a/internal/sdkv2provider/resource_cloudflare_zone_settings_override.go b/internal/sdkv2provider/resource_cloudflare_zone_settings_override.go index d9c5293d0d..0aa6321cfa 100644 --- a/internal/sdkv2provider/resource_cloudflare_zone_settings_override.go +++ b/internal/sdkv2provider/resource_cloudflare_zone_settings_override.go @@ -19,7 +19,7 @@ import ( func resourceCloudflareZoneSettingsOverride() *schema.Resource { return &schema.Resource{ - SchemaVersion: 1, + SchemaVersion: 2, Schema: resourceCloudflareZoneSettingsOverrideSchema(), CreateContext: resourceCloudflareZoneSettingsOverrideCreate, ReadContext: resourceCloudflareZoneSettingsOverrideRead, @@ -32,6 +32,11 @@ func resourceCloudflareZoneSettingsOverride() *schema.Resource { Type: resourceCloudflareZoneSettingsOverrideV0().CoreConfigSchema().ImpliedType(), Upgrade: resourceCloudflareZoneSettingsOverrideStateUpgradeV1, }, + { + Version: 1, + Type: resourceCloudflareZoneSettingsOverrideV1().CoreConfigSchema().ImpliedType(), + Upgrade: resourceCloudflareZoneSettingsOverrideStateUpgradeV2, + }, }, } } @@ -195,7 +200,7 @@ func flattenZoneSettings(ctx context.Context, d *schema.ResourceData, settings [ continue } - if s.ID == "minify" || s.ID == "nel" { + if s.ID == "nel" { cfg[s.ID] = []interface{}{s.Value.(map[string]interface{})} } else if s.ID == "security_header" { cfg[s.ID] = []interface{}{s.Value.(map[string]interface{})["strict_transport_security"]} @@ -363,7 +368,7 @@ func expandZoneSetting(d *schema.ResourceData, keyFormatString, k string, settin zoneSettingValue = settingValue } } - case "minify", "nel": + case "nel": { listValue := settingValue.([]interface{}) if len(listValue) > 0 && listValue != nil { diff --git a/internal/sdkv2provider/resource_cloudflare_zone_settings_override_migrate.go b/internal/sdkv2provider/resource_cloudflare_zone_settings_override_migrate.go index 9d07a5226f..fa71186a8b 100644 --- a/internal/sdkv2provider/resource_cloudflare_zone_settings_override_migrate.go +++ b/internal/sdkv2provider/resource_cloudflare_zone_settings_override_migrate.go @@ -52,3 +52,49 @@ func resourceCloudflareZoneSettingsOverrideStateUpgradeV1( return state, nil } + +func resourceCloudflareZoneSettingsOverrideV1() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "settings": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: resourceCloudflareZoneSettingsSchemaV1, + }, + }, + + "initial_settings": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: resourceCloudflareZoneSettingsSchemaV1, + }, + }, + }, + } +} + +func resourceCloudflareZoneSettingsOverrideStateUpgradeV2( + _ context.Context, + rawState map[string]interface{}, + _ interface{}, +) (map[string]interface{}, error) { + errMsg := "could not upgrade cloudflare_zone_settings_override from v1 to v2" + + if rawState == nil { + return nil, fmt.Errorf("%s: state is nil", errMsg) + } + + upgrade := func(state map[string]interface{}, name string) map[string]interface{} { + delete(state[name].([]interface{})[0].(map[string]interface{}), "minify") + return state + } + + state := upgrade(rawState, "settings") + state = upgrade(state, "initial_settings") + + return state, nil +} diff --git a/internal/sdkv2provider/resource_cloudflare_zone_settings_override_test.go b/internal/sdkv2provider/resource_cloudflare_zone_settings_override_test.go index f136f8c02d..c41214bafd 100644 --- a/internal/sdkv2provider/resource_cloudflare_zone_settings_override_test.go +++ b/internal/sdkv2provider/resource_cloudflare_zone_settings_override_test.go @@ -287,3 +287,38 @@ func TestCloudflareZoneSettingsOverrideStateUpgradeV0(t *testing.T) { require.Equal(t, expectedV1, actualV1) } + +func TestCloudflareZoneSettingsOverrideStateUpgradeV1(t *testing.T) { + v1 := map[string]interface{}{ + "settings": []interface{}{map[string]interface{}{ + "minify": map[string]interface{}{ + "css": "on", + "js": "on", + "html": "off", + }, + "other_thing": "foo", + }}, + "initial_settings": []interface{}{map[string]interface{}{ + "minify": map[string]interface{}{ + "css": "on", + "js": "on", + "html": "off", + }, + "other_thing": "foo", + }}, + } + + expectedV2 := map[string]interface{}{ + "settings": []interface{}{map[string]interface{}{ + "other_thing": "foo", + }}, + "initial_settings": []interface{}{map[string]interface{}{ + "other_thing": "foo", + }}, + } + + actualV2, err := resourceCloudflareZoneSettingsOverrideStateUpgradeV2(context.Background(), v1, nil) + require.NoError(t, err) + + require.Equal(t, expectedV2, actualV2) +} diff --git a/internal/sdkv2provider/schema_cloudflare_zone_settings_override.go b/internal/sdkv2provider/schema_cloudflare_zone_settings_override.go index 506fdc4e79..1e268dc416 100644 --- a/internal/sdkv2provider/schema_cloudflare_zone_settings_override.go +++ b/internal/sdkv2provider/schema_cloudflare_zone_settings_override.go @@ -1051,3 +1051,501 @@ var resourceCloudflareZoneSettingsSchemaV0 = map[string]*schema.Schema{ }, }, } + +var resourceCloudflareZoneSettingsSchemaV1 = map[string]*schema.Schema{ + "always_online": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "brotli": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "browser_cache_ttl": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntInSlice([]int{0, 30, 60, 120, 300, 1200, 1800, 3600, 7200, 10800, 14400, 18000, 28800, + 43200, 57600, 72000, 86400, 172800, 259200, 345600, 432000, 691200, 1382400, 2073600, 2678400, 5356800, + 16070400, 31536000}), + // minimum TTL available depends on the plan level of the zone. + // - Respect existing headers = 0 + // - Enterprise = 30 + // - Business, Pro, Free = 1800 + }, + + "browser_check": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "cache_level": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{"aggressive", "basic", "simplified"}, false), + }, + + "ciphers": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + "challenge_ttl": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntInSlice([]int{300, 900, 1800, 2700, 3600, 7200, 10800, 14400, 28800, 57600, + 86400, 604800, 2592000, 31536000}), + }, + + "development_mode": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "origin_error_page_pass_thru": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "sort_query_string_for_cache": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "email_obfuscation": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "hotlink_protection": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "ip_geolocation": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "ipv6": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "websockets": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "minify": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MinItems: 1, + MaxItems: 1, + Deprecated: "Auto Minify has been deprecated and disabled", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "css": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Required: true, + }, + + "html": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Required: true, + }, + + "js": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Required: true, + }, + }, + }, + }, + + "mobile_redirect": { + Type: schema.TypeList, // on/off + Optional: true, + Computed: true, + MinItems: 1, + MaxItems: 1, + Deprecated: "Mobile redirects has been deprecated and disabled in favour of [Single Redirects](https://developers.cloudflare.com/rules/url-forwarding/single-redirects/) and are no longer configurable using the API. Refer to [Perform mobile redirects](https://developers.cloudflare.com/rules/url-forwarding/single-redirects/examples/#perform-mobile-redirects) for examples of performing mobile redirects with Single Redirects.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + // which parameters are mandatory is not specified + "mobile_subdomain": { + Type: schema.TypeString, + Required: true, + }, + + "strip_uri": { + Type: schema.TypeBool, + Required: true, + }, + + "status": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Required: true, + }, + }, + }, + }, + + "mirage": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "opportunistic_encryption": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "opportunistic_onion": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "origin_max_http_version": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{"1", "2"}, false), + }, + + "polish": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{"off", "lossless", "lossy"}, false), + }, + + "webp": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + }, + + "prefetch_preload": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "privacy_pass": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "response_buffering": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "rocket_loader": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{"on", "off", "manual"}, false), + }, + + "security_header": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + + "preload": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + + "max_age": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + + "include_subdomains": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + + "nosniff": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + }, + }, + }, + + "security_level": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{"off", "essentially_off", "low", "medium", "high", "under_attack"}, false), + }, + + "server_side_exclude": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "ssl": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{"off", "flexible", "full", "strict", "origin_pull"}, false), // depends on plan + }, + + "universal_ssl": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + }, + + "tls_client_auth": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "true_client_ip_header": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "waf": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "min_tls_version": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"1.0", "1.1", "1.2", "1.3"}, false), + Optional: true, + Computed: true, + }, + + "tls_1_2_only": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + Deprecated: "tls_1_2_only has been deprecated in favour of using `min_tls_version = \"1.2\"` instead.", + }, + + "tls_1_3": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off", "zrt"}, false), + Optional: true, + Computed: true, // default depends on plan level + }, + + "automatic_https_rewrites": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "http2": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "http3": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "pseudo_ipv4": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{"off", "add_header", "overwrite_header"}, false), + }, + + "always_use_https": { + // may cause an error: HTTP status 400: content "{\"success\":false,\"errors\":[{\"code\":1016,\"message\":\"An unknown error has occurred\"}],\"messages\":[],\"result\":null}" + // but it still gets set at the API + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "cname_flattening": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"flatten_at_root", "flatten_all", "flatten_none"}, false), + Optional: true, + Computed: true, + }, + + "max_upload": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + + "h2_prioritization": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off", "custom"}, false), + Optional: true, + Computed: true, + }, + + "image_resizing": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off", "open"}, false), + Optional: true, + Computed: true, + }, + + "zero_rtt": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "orange_to_orange": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "filter_logs_to_cloudflare": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "log_to_cloudflare": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "visitor_ip": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "proxy_read_timeout": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "binary_ast": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "early_hints": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "fonts": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), + Optional: true, + Computed: true, + }, + + "nel": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + }, +}