diff --git a/docs/data-sources/cdn_distribution.md b/docs/data-sources/cdn_distribution.md index e9f592bd6..6f13cc25e 100644 --- a/docs/data-sources/cdn_distribution.md +++ b/docs/data-sources/cdn_distribution.md @@ -50,7 +50,10 @@ Optional: Read-Only: - `backend` (Attributes) The configured backend for the distribution (see [below for nested schema](#nestedatt--config--backend)) +- `blocked_ips` (List of String) Restricts access to your content by specifying a list of blocked IPv4 addresses. This feature enhances security and privacy by preventing these addresses from accessing your distribution. +- `default_cache_duration` (String) Sets the default cache duration for the distribution. The default cache duration is applied when a 'Cache-Control' header is not presented in the origin's response. We use ISO8601 duration format for cache duration (e.g. P1DT2H30M) - `forward_host_header` (Boolean) Enable this allows the 'Host' header to be passed through to the origin. +- `monthly_limit_bytes` (Number) Sets the monthly limit of bandwidth in bytes that the pullzone is allowed to use. - `optimizer` (Attributes) Configuration for the Image Optimizer. This is a paid feature that automatically optimizes images to reduce their file size for faster delivery, leading to improved website performance and a better user experience. (see [below for nested schema](#nestedatt--config--optimizer)) - `redirects` (Attributes) A wrapper for a list of redirect rules that allows for redirect settings on a distribution (see [below for nested schema](#nestedatt--config--redirects)) - `regions` (List of String) The configured regions where content will be hosted diff --git a/docs/resources/cdn_distribution.md b/docs/resources/cdn_distribution.md index cf24cf2cf..1afd88157 100644 --- a/docs/resources/cdn_distribution.md +++ b/docs/resources/cdn_distribution.md @@ -28,6 +28,10 @@ resource "stackit_cdn_distribution" "example_distribution" { } regions = ["EU", "US", "ASIA", "AF", "SA"] blocked_countries = ["DE", "AT", "CH"] + blocked_ips = ["1.1.1.1"] + + default_cache_duration = "P1DT2H30M" + monthly_limit_bytes = 1048576 optimizer = { enabled = true @@ -52,6 +56,10 @@ resource "stackit_cdn_distribution" "example_bucket_distribution" { } regions = ["EU", "US"] blocked_countries = ["CN", "RU"] + blocked_ips = ["1.1.1.1"] + + default_cache_duration = "P1DT2H30M" + monthly_limit_bytes = 1048576 optimizer = { enabled = false @@ -147,7 +155,10 @@ Required: Optional: - `blocked_countries` (List of String) The configured countries where distribution of content is blocked +- `blocked_ips` (List of String) Restricts access to your content by specifying a list of blocked IPv4 addresses. This feature enhances security and privacy by preventing these addresses from accessing your distribution. +- `default_cache_duration` (String) Sets the default cache duration for the distribution. The default cache duration is applied when a 'Cache-Control' header is not presented in the origin's response. We use ISO8601 duration format for cache duration (e.g. P1DT2H30M) - `forward_host_header` (Boolean) Enable this allows the 'Host' header to be passed through to the origin. +- `monthly_limit_bytes` (Number) Sets the monthly limit of bandwidth in bytes that the pullzone is allowed to use. - `optimizer` (Attributes) Configuration for the Image Optimizer. This is a paid feature that automatically optimizes images to reduce their file size for faster delivery, leading to improved website performance and a better user experience. (see [below for nested schema](#nestedatt--config--optimizer)) - `redirects` (Attributes) A wrapper for a list of redirect rules that allows for redirect settings on a distribution (see [below for nested schema](#nestedatt--config--redirects)) - `strip_response_cookies` (Boolean) Enable this to prevent origin-level cookies from being forwarded to the end user. diff --git a/examples/resources/stackit_cdn_distribution/resource.tf b/examples/resources/stackit_cdn_distribution/resource.tf index 65c6c560b..96ca7caae 100644 --- a/examples/resources/stackit_cdn_distribution/resource.tf +++ b/examples/resources/stackit_cdn_distribution/resource.tf @@ -10,6 +10,10 @@ resource "stackit_cdn_distribution" "example_distribution" { } regions = ["EU", "US", "ASIA", "AF", "SA"] blocked_countries = ["DE", "AT", "CH"] + blocked_ips = ["1.1.1.1"] + + default_cache_duration = "P1DT2H30M" + monthly_limit_bytes = 1048576 optimizer = { enabled = true @@ -34,6 +38,10 @@ resource "stackit_cdn_distribution" "example_bucket_distribution" { } regions = ["EU", "US"] blocked_countries = ["CN", "RU"] + blocked_ips = ["1.1.1.1"] + + default_cache_duration = "P1DT2H30M" + monthly_limit_bytes = 1048576 optimizer = { enabled = false diff --git a/stackit/internal/services/cdn/cdn_acc_test.go b/stackit/internal/services/cdn/cdn_acc_test.go index 4e8c45839..942c9a05d 100644 --- a/stackit/internal/services/cdn/cdn_acc_test.go +++ b/stackit/internal/services/cdn/cdn_acc_test.go @@ -107,39 +107,59 @@ func wafConfigVariable(mode, wafType, paranoiaLevel, allowedMethod, allowedConte } var testConfigVarsHttp = config.Variables{ - "project_id": config.StringVariable(testutil.ProjectId), - "name": config.StringVariable(httpTestName), - "regions": config.ListVariable(config.StringVariable("EU"), config.StringVariable("US")), - "dns_zone_name": config.StringVariable("acc_cdn_test_zone"), - "dns_name": config.StringVariable(dnsNameHttp), - "dns_record_name": config.StringVariable(dnsRecordNameHttp), - "optimizer": config.BoolVariable(true), - "backend_http_type": config.StringVariable("http"), - "blocked_countries": config.ListVariable(config.StringVariable("CU")), - "backend_origin_url": config.StringVariable("https://test-backend-1.cdn-dev.runs.onstackit.cloud"), - "geofencing_list": config.ListVariable(config.StringVariable("DE")), - "origin_request_headers_name": config.StringVariable("X-Custom-Header"), - "origin_request_headers_value": config.StringVariable("x-custom-value"), - "certificate": config.StringVariable(string(cert)), - "private_key": config.StringVariable(string(key)), - "redirect_target_url": config.StringVariable("https://example.com"), - "redirect_status_code": config.IntegerVariable(301), - "redirect_matcher_values": config.ListVariable(config.StringVariable("/shop/*")), - "redirect_rule_description": config.StringVariable("Acc test redirect"), - "redirect_rule_enabled": config.BoolVariable(true), - "redirect_rule_match_condition": config.StringVariable("ANY"), - "redirect_matcher_condition": config.StringVariable("ANY"), - "waf": wafConfigVariable("ENABLED", "FREE", "L2", "GET", "application/json", "HTTP/1.0", wafRule1, wafRule2, wafRule3), - "tls_enable_tls_10": config.BoolVariable(true), - "tls_enable_tls_11": config.BoolVariable(true), - "strip_response_cookies": config.BoolVariable(false), - "forward_host_header": config.BoolVariable(true), + "project_id": config.StringVariable(testutil.ProjectId), + "name": config.StringVariable(httpTestName), + "regions": config.ListVariable(config.StringVariable("EU"), config.StringVariable("US")), + "dns_zone_name": config.StringVariable("acc_cdn_test_zone"), + "dns_name": config.StringVariable(dnsNameHttp), + "dns_record_name": config.StringVariable(dnsRecordNameHttp), + "optimizer": config.BoolVariable(true), + "backend_http_type": config.StringVariable("http"), + "blocked_countries": config.ListVariable(config.StringVariable("CU")), + "blocked_ips": config.ListVariable(config.StringVariable("1.1.1.1")), + "backend_origin_url": config.StringVariable("https://test-backend-1.cdn-dev.runs.onstackit.cloud"), + "geofencing_list": config.ListVariable(config.StringVariable("DE")), + "origin_request_headers_name": config.StringVariable("X-Custom-Header"), + "origin_request_headers_value": config.StringVariable("x-custom-value"), + "certificate": config.StringVariable(string(cert)), + "private_key": config.StringVariable(string(key)), + "redirect_target_url": config.StringVariable("https://example.com"), + "redirect_status_code": config.IntegerVariable(301), + "redirect_matcher_value": config.StringVariable("/shop/*"), + "redirect_rule_description": config.StringVariable("Acc test redirect"), + "redirect_rule_enabled": config.BoolVariable(true), + "redirect_rule_match_condition": config.StringVariable("ANY"), + "redirect_matcher_condition": config.StringVariable("ANY"), + "waf_mode": config.StringVariable("ENABLED"), + "waf_type": config.StringVariable("FREE"), + "waf_allowed_http_methods_0": config.StringVariable("GET"), + "waf_allowed_request_content_types_0": config.StringVariable("application/json"), + "waf_allowed_http_versions_0": config.StringVariable("HTTP/1.0"), + "waf_paranoia_level": config.StringVariable("L2"), + "waf_disabled_rule_ids_0": config.StringVariable(wafRule1), + "waf_disabled_rule_group_ids_0": config.StringVariable(wafRule1), + "waf_disabled_rule_collection_ids_0": config.StringVariable(wafRule1), + "waf_enabled_rule_ids_0": config.StringVariable(wafRule2), + "waf_enabled_rule_group_ids_0": config.StringVariable(wafRule2), + "waf_enabled_rule_collection_ids_0": config.StringVariable(wafRule2), + "waf_log_only_rule_ids_0": config.StringVariable(wafRule3), + "waf_log_only_rule_group_ids_0": config.StringVariable(wafRule3), + "waf_log_only_rule_collection_ids_0": config.StringVariable(wafRule3), + "tls_enable_tls_10": config.BoolVariable(true), + "tls_enable_tls_11": config.BoolVariable(true), + "strip_response_cookies": config.BoolVariable(false), + "forward_host_header": config.BoolVariable(true), + "monthly_limit_bytes": config.IntegerVariable(104857600), + "default_cache_duration": config.StringVariable("PT2H"), } func configVarsHttpUpdated() config.Variables { updatedConfig := maps.Clone(testConfigVarsHttp) updatedConfig["regions"] = config.ListVariable(config.StringVariable("EU"), config.StringVariable("US"), config.StringVariable("ASIA")) updatedConfig["redirect_target_url"] = config.StringVariable("https://example.com/updated") + updatedConfig["monthly_limit_bytes"] = config.IntegerVariable(209715200) + updatedConfig["default_cache_duration"] = config.StringVariable("PT4H") + updatedConfig["blocked_ips"] = config.ListVariable(config.StringVariable("1.1.1.1"), config.StringVariable("2.2.2.2")) // Update WAF configuration to test mutation updatedConfig["waf"] = wafConfigVariable("LOG_ONLY", "PREMIUM", "L3", "POST", "text/plain", "HTTP/1.1", wafRule3, wafRule2, wafRule1) @@ -220,8 +240,13 @@ func TestAccCDNDistributionHttp(t *testing.T) { resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.redirects.rules.0.matchers.#", "1"), testutil.CheckListAttr("stackit_cdn_distribution.distribution", "config.redirects.rules.0.matchers.0.values", testConfigVarsHttp["redirect_matcher_values"]), resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.redirects.rules.0.matchers.0.value_match_condition", testutil.ConvertConfigVariable(testConfigVarsHttp["redirect_matcher_condition"])), - testutil.CheckListAttr("stackit_cdn_distribution.distribution", "config.regions", testConfigVarsHttp["regions"]), - testutil.CheckListAttr("stackit_cdn_distribution.distribution", "config.blocked_countries", testConfigVarsHttp["blocked_countries"]), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.#", "2"), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.0", "EU"), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.1", "US"), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.blocked_countries.#", "1"), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.blocked_countries.0", "CU"), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.blocked_ips.#", "1"), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.blocked_ips.0", "1.1.1.1"), resource.TestCheckResourceAttr( "stackit_cdn_distribution.distribution", fmt.Sprintf("config.backend.origin_request_headers.%s", testutil.ConvertConfigVariable(testConfigVarsHttp["origin_request_headers_name"])), @@ -239,6 +264,8 @@ func TestAccCDNDistributionHttp(t *testing.T) { resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.tls.enable_tls_11", testutil.ConvertConfigVariable(testConfigVarsHttp["tls_enable_tls_11"])), resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.forward_host_header", testutil.ConvertConfigVariable(testConfigVarsHttp["forward_host_header"])), resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.strip_response_cookies", testutil.ConvertConfigVariable(testConfigVarsHttp["strip_response_cookies"])), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.monthly_limit_bytes", testutil.ConvertConfigVariable(testConfigVarsHttp["monthly_limit_bytes"])), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.default_cache_duration", testutil.ConvertConfigVariable(testConfigVarsHttp["default_cache_duration"])), // WAF Checks testutil.CheckObjectAttr("stackit_cdn_distribution.distribution", "config.waf", testConfigVarsHttp["waf"]), @@ -340,7 +367,12 @@ func TestAccCDNDistributionHttp(t *testing.T) { fmt.Sprintf("config.backend.geofencing.%s", testutil.ConvertConfigVariable(testConfigVarsHttp["backend_origin_url"])), testConfigVarsHttp["geofencing_list"], ), - testutil.CheckListAttr("data.stackit_cdn_distribution.distribution", "config.blocked_countries", testConfigVarsHttp["blocked_countries"]), + resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.regions.0", "EU"), + resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.regions.1", "US"), + resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.blocked_countries.#", "1"), + resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.blocked_countries.0", "CU"), + resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.blocked_ips.#", "1"), + resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.blocked_ips.0", "1.1.1.1"), resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.optimizer.enabled", testutil.ConvertConfigVariable(testConfigVarsHttp["optimizer"])), // TLS Checks inside Data Source @@ -348,6 +380,8 @@ func TestAccCDNDistributionHttp(t *testing.T) { resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.tls.enable_tls_11", testutil.ConvertConfigVariable(testConfigVarsHttp["tls_enable_tls_11"])), resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.forward_host_header", testutil.ConvertConfigVariable(testConfigVarsHttp["forward_host_header"])), resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.strip_response_cookies", testutil.ConvertConfigVariable(testConfigVarsHttp["strip_response_cookies"])), + resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.monthly_limit_bytes", testutil.ConvertConfigVariable(testConfigVarsHttp["monthly_limit_bytes"])), + resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.default_cache_duration", testutil.ConvertConfigVariable(testConfigVarsHttp["default_cache_duration"])), // WAF Checks inside Data Source testutil.CheckObjectAttr("data.stackit_cdn_distribution.distribution", "config.waf", testConfigVarsHttp["waf"]), @@ -384,8 +418,15 @@ func TestAccCDNDistributionHttp(t *testing.T) { resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.1.status", "ACTIVE"), resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.0.type", "managed"), resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.1.type", "custom"), - testutil.CheckListAttr("stackit_cdn_distribution.distribution", "config.regions", configVarsHttpUpdated()["regions"]), - testutil.CheckListAttr("stackit_cdn_distribution.distribution", "config.blocked_countries", configVarsHttpUpdated()["blocked_countries"]), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.#", "3"), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.0", "EU"), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.1", "US"), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.2", "ASIA"), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.blocked_countries.#", "1"), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.blocked_countries.0", "CU"), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.blocked_ips.#", "2"), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.blocked_ips.0", "1.1.1.1"), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.blocked_ips.1", "2.2.2.2"), resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.optimizer.enabled", testutil.ConvertConfigVariable(testConfigVarsHttp["optimizer"])), // TLS Configuration @@ -393,6 +434,8 @@ func TestAccCDNDistributionHttp(t *testing.T) { resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.tls.enable_tls_11", testutil.ConvertConfigVariable(configVarsHttpUpdated()["tls_enable_tls_11"])), resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.forward_host_header", testutil.ConvertConfigVariable(configVarsHttpUpdated()["forward_host_header"])), resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.strip_response_cookies", testutil.ConvertConfigVariable(configVarsHttpUpdated()["strip_response_cookies"])), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.monthly_limit_bytes", testutil.ConvertConfigVariable(configVarsHttpUpdated()["monthly_limit_bytes"])), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.default_cache_duration", testutil.ConvertConfigVariable(configVarsHttpUpdated()["default_cache_duration"])), // Checking WAF Mutated Configurations testutil.CheckObjectAttr("stackit_cdn_distribution.distribution", "config.waf", configVarsHttpUpdated()["waf"]), diff --git a/stackit/internal/services/cdn/distribution/datasource.go b/stackit/internal/services/cdn/distribution/datasource.go index e8be210fa..d3d6680f0 100644 --- a/stackit/internal/services/cdn/distribution/datasource.go +++ b/stackit/internal/services/cdn/distribution/datasource.go @@ -34,9 +34,12 @@ var dataSourceBackendTypes = map[string]attr.Type{ } var dataSourceConfigTypes = map[string]attr.Type{ - "backend": types.ObjectType{AttrTypes: dataSourceBackendTypes}, - "regions": types.ListType{ElemType: types.StringType}, - "blocked_countries": types.ListType{ElemType: types.StringType}, + "backend": types.ObjectType{AttrTypes: dataSourceBackendTypes}, + "regions": types.ListType{ElemType: types.StringType}, + "blocked_countries": types.ListType{ElemType: types.StringType}, + "blocked_ips": types.ListType{ElemType: types.StringType}, + "default_cache_duration": types.StringType, + "monthly_limit_bytes": types.Int64Type, "optimizer": types.ObjectType{ AttrTypes: optimizerTypes, // Shared from resource.go }, @@ -203,6 +206,19 @@ func (r *distributionDataSource) Schema(_ context.Context, _ datasource.SchemaRe Description: schemaDescriptions["config_blocked_countries"], ElementType: types.StringType, }, + "blocked_ips": schema.ListAttribute{ + Computed: true, + Description: schemaDescriptions["config_blocked_ips"], + ElementType: types.StringType, + }, + "default_cache_duration": schema.StringAttribute{ + Computed: true, + Description: schemaDescriptions["config_default_cache_duration"], + }, + "monthly_limit_bytes": schema.Int64Attribute{ + Computed: true, + Description: schemaDescriptions["config_monthly_limit_bytes"], + }, "optimizer": schema.SingleNestedAttribute{ Description: schemaDescriptions["config_optimizer"], Computed: true, @@ -678,11 +694,41 @@ func mapDataSourceFields(ctx context.Context, distribution *cdnSdk.Distribution, return core.DiagsToError(diagWaf) } + // blockedIps + var blockedIps []attr.Value + if distribution.Config.BlockedIps != nil { + for _, ip := range distribution.Config.BlockedIps { + blockedIps = append(blockedIps, types.StringValue(ip)) + } + } + + modelBlockedIps, diags := types.ListValue(types.StringType, blockedIps) + if diags.HasError() { + return core.DiagsToError(diags) + } + + var defaultCacheDuration types.String + if distribution.Config.DefaultCacheDuration.IsSet() { + defaultCacheDuration = types.StringPointerValue(distribution.Config.DefaultCacheDuration.Get()) + } else { + defaultCacheDuration = types.StringNull() + } + + var monthlyLimitBytes types.Int64 + if distribution.Config.MonthlyLimitBytes.IsSet() { + monthlyLimitBytes = types.Int64PointerValue(distribution.Config.MonthlyLimitBytes.Get()) + } else { + monthlyLimitBytes = types.Int64Null() + } + // Use dataSourceConfigTypes cfg, diags := types.ObjectValue(dataSourceConfigTypes, map[string]attr.Value{ "backend": backend, "regions": modelRegions, "blocked_countries": modelBlockedCountries, + "blocked_ips": modelBlockedIps, + "default_cache_duration": defaultCacheDuration, + "monthly_limit_bytes": monthlyLimitBytes, "optimizer": optimizerVal, "redirects": redirectsVal, "waf": wafVal, diff --git a/stackit/internal/services/cdn/distribution/datasource_test.go b/stackit/internal/services/cdn/distribution/datasource_test.go index 911957675..67b56d6fc 100644 --- a/stackit/internal/services/cdn/distribution/datasource_test.go +++ b/stackit/internal/services/cdn/distribution/datasource_test.go @@ -66,6 +66,9 @@ func TestMapDataSourceFields(t *testing.T) { "backend": backend, "regions": regionsFixture, "blocked_countries": blockedCountriesFixture, + "blocked_ips": types.ListValueMust(types.StringType, []attr.Value{}), + "default_cache_duration": types.StringNull(), + "monthly_limit_bytes": types.Int64Null(), "optimizer": types.ObjectNull(optimizerTypes), "redirects": types.ObjectNull(redirectsTypes), "waf": emptyWaf, @@ -241,8 +244,11 @@ func TestMapDataSourceFields(t *testing.T) { m.Config = types.ObjectValueMust(dataSourceConfigTypes, map[string]attr.Value{ "backend": backend, "regions": regionsFixture, - "optimizer": optimizer, "blocked_countries": blockedCountriesFixture, + "blocked_ips": types.ListValueMust(types.StringType, []attr.Value{}), + "default_cache_duration": types.StringNull(), + "monthly_limit_bytes": types.Int64Null(), + "optimizer": optimizer, "redirects": types.ObjectNull(redirectsTypes), "waf": emptyWaf, "tls": defaultTls, @@ -272,6 +278,9 @@ func TestMapDataSourceFields(t *testing.T) { "backend": bucketBackendExpected, "regions": regionsFixture, "blocked_countries": blockedCountriesFixture, + "blocked_ips": types.ListValueMust(types.StringType, []attr.Value{}), + "default_cache_duration": types.StringNull(), + "monthly_limit_bytes": types.Int64Null(), "optimizer": types.ObjectNull(optimizerTypes), "redirects": types.ObjectNull(redirectsTypes), "waf": emptyWaf, @@ -295,8 +304,11 @@ func TestMapDataSourceFields(t *testing.T) { m.Config = types.ObjectValueMust(dataSourceConfigTypes, map[string]attr.Value{ "backend": backendWithGeofencing, "regions": regionsFixture, - "optimizer": types.ObjectNull(optimizerTypes), "blocked_countries": blockedCountriesFixture, + "blocked_ips": types.ListValueMust(types.StringType, []attr.Value{}), + "default_cache_duration": types.StringNull(), + "monthly_limit_bytes": types.Int64Null(), + "optimizer": types.ObjectNull(optimizerTypes), "redirects": types.ObjectNull(redirectsTypes), "waf": emptyWaf, "tls": defaultTls, @@ -323,8 +335,11 @@ func TestMapDataSourceFields(t *testing.T) { m.Config = types.ObjectValueMust(dataSourceConfigTypes, map[string]attr.Value{ "backend": backend, "regions": regionsFixture, - "optimizer": types.ObjectNull(optimizerTypes), "blocked_countries": blockedCountriesFixture, + "blocked_ips": types.ListValueMust(types.StringType, []attr.Value{}), + "default_cache_duration": types.StringNull(), + "monthly_limit_bytes": types.Int64Null(), + "optimizer": types.ObjectNull(optimizerTypes), "redirects": redirectsConfigExpected, "waf": emptyWaf, "tls": defaultTls, @@ -342,8 +357,11 @@ func TestMapDataSourceFields(t *testing.T) { m.Config = types.ObjectValueMust(dataSourceConfigTypes, map[string]attr.Value{ "backend": backend, "regions": regionsFixture, - "optimizer": types.ObjectNull(optimizerTypes), "blocked_countries": blockedCountriesFixture, + "blocked_ips": types.ListValueMust(types.StringType, []attr.Value{}), + "default_cache_duration": types.StringNull(), + "monthly_limit_bytes": types.Int64Null(), + "optimizer": types.ObjectNull(optimizerTypes), "redirects": types.ObjectNull(redirectsTypes), "waf": populatedWaf, "tls": defaultTls, @@ -392,12 +410,15 @@ func TestMapDataSourceFields(t *testing.T) { "happy_path_with_tls_and_strip_response_and_cookies_forward": { Expected: expectedModel(func(m *Model) { m.Config = types.ObjectValueMust(dataSourceConfigTypes, map[string]attr.Value{ - "backend": backend, - "regions": regionsFixture, - "optimizer": types.ObjectNull(optimizerTypes), - "blocked_countries": blockedCountriesFixture, - "redirects": types.ObjectNull(redirectsTypes), - "waf": emptyWaf, + "backend": backend, + "regions": regionsFixture, + "blocked_countries": blockedCountriesFixture, + "blocked_ips": types.ListValueMust(types.StringType, []attr.Value{}), + "default_cache_duration": types.StringNull(), + "monthly_limit_bytes": types.Int64Null(), + "optimizer": types.ObjectNull(optimizerTypes), + "redirects": types.ObjectNull(redirectsTypes), + "waf": emptyWaf, "tls": types.ObjectValueMust(tlsTypes, map[string]attr.Value{ "enable_tls_10": types.BoolValue(true), "enable_tls_11": types.BoolValue(true), diff --git a/stackit/internal/services/cdn/distribution/resource.go b/stackit/internal/services/cdn/distribution/resource.go index bae5e5c41..88336454c 100644 --- a/stackit/internal/services/cdn/distribution/resource.go +++ b/stackit/internal/services/cdn/distribution/resource.go @@ -68,6 +68,9 @@ var schemaDescriptions = map[string]string{ "config_backend_origin_request_headers": "The configured type http origin request headers for the backend", "config_backend_geofencing": "The configured type http to configure countries where content is allowed. A map of URLs to a list of countries", "config_blocked_countries": "The configured countries where distribution of content is blocked", + "config_blocked_ips": "Restricts access to your content by specifying a list of blocked IPv4 addresses. This feature enhances security and privacy by preventing these addresses from accessing your distribution.", + "config_default_cache_duration": "Sets the default cache duration for the distribution. The default cache duration is applied when a 'Cache-Control' header is not presented in the origin's response. We use ISO8601 duration format for cache duration (e.g. P1DT2H30M)", + "config_monthly_limit_bytes": "Sets the monthly limit of bandwidth in bytes that the pullzone is allowed to use.", "config_redirects": "A wrapper for a list of redirect rules that allows for redirect settings on a distribution", "config_redirects_rules": "A list of redirect rules. The order of rules matters for evaluation", "config_redirects_rule_description": "An optional description for the redirect rule", @@ -144,6 +147,9 @@ type distributionConfig struct { Redirects *redirectConfig `tfsdk:"redirects"` // A wrapper for a list of redirect rules that allows for redirect settings on a distribution Regions *[]string `tfsdk:"regions"` // The regions in which data will be cached BlockedCountries *[]string `tfsdk:"blocked_countries"` // The countries for which content will be blocked + BlockedIps *[]string `tfsdk:"blocked_ips"` // Restricts access to your content by specifying a list of blocked IPv4 addresses. + DefaultCacheDuration types.String `tfsdk:"default_cache_duration"` // Sets the default cache duration for the distribution. + MonthlyLimitBytes types.Int64 `tfsdk:"monthly_limit_bytes"` // Sets the monthly limit of bandwidth in bytes. Optimizer types.Object `tfsdk:"optimizer"` // The optimizer configuration Waf types.Object `tfsdk:"waf"` // The WAF configuration Tls types.Object `tfsdk:"tls"` // The TLS configuration @@ -194,9 +200,12 @@ type backendCredentials struct { } var configTypes = map[string]attr.Type{ - "backend": types.ObjectType{AttrTypes: backendTypes}, - "regions": types.ListType{ElemType: types.StringType}, - "blocked_countries": types.ListType{ElemType: types.StringType}, + "backend": types.ObjectType{AttrTypes: backendTypes}, + "regions": types.ListType{ElemType: types.StringType}, + "blocked_countries": types.ListType{ElemType: types.StringType}, + "blocked_ips": types.ListType{ElemType: types.StringType}, + "default_cache_duration": types.StringType, + "monthly_limit_bytes": types.Int64Type, "optimizer": types.ObjectType{ AttrTypes: optimizerTypes, }, @@ -714,6 +723,23 @@ func (r *distributionResource) Schema(_ context.Context, _ resource.SchemaReques // API response (empty list). Default: listdefault.StaticValue(types.ListValueMust(types.StringType, []attr.Value{})), }, + "blocked_ips": schema.ListAttribute{ + Optional: true, + Computed: true, // Required when using Default + Description: schemaDescriptions["config_blocked_ips"], + ElementType: types.StringType, + Default: listdefault.StaticValue(types.ListValueMust(types.StringType, []attr.Value{})), + }, + "default_cache_duration": schema.StringAttribute{ + Optional: true, + Computed: true, + Description: schemaDescriptions["config_default_cache_duration"], + }, + "monthly_limit_bytes": schema.Int64Attribute{ + Optional: true, + Computed: true, + Description: schemaDescriptions["config_monthly_limit_bytes"], + }, }, }, }, @@ -918,6 +944,12 @@ func (r *distributionResource) Update(ctx context.Context, req resource.UpdateRe blockedCountries = tempBlockedCountries } + // blockedIps + var blockedIps []string + if configModel.BlockedIps != nil { + blockedIps = *configModel.BlockedIps + } + // tls var tls *cdnSdk.TlsConfigPatch if !utils.IsUndefined(configModel.Tls) { @@ -981,6 +1013,7 @@ func (r *distributionResource) Update(ctx context.Context, req resource.UpdateRe Backend: configPatchBackend, Regions: regions, BlockedCountries: blockedCountries, + BlockedIps: blockedIps, Redirects: redirectsConfig, Tls: tls, } @@ -993,6 +1026,12 @@ func (r *distributionResource) Update(ctx context.Context, req resource.UpdateRe if !utils.IsUndefined(configModel.StripResponseCookies) { configPatch.StripResponseCookies = configModel.StripResponseCookies.ValueBoolPointer() } + if !utils.IsUndefined(configModel.DefaultCacheDuration) { + configPatch.DefaultCacheDuration = *cdnSdk.NewNullableString(conversion.StringValueToPointer(configModel.DefaultCacheDuration)) + } + if !utils.IsUndefined(configModel.MonthlyLimitBytes) { + configPatch.MonthlyLimitBytes = *cdnSdk.NewNullableInt64(conversion.Int64ValueToPointer(configModel.MonthlyLimitBytes)) + } configPatch.Waf = &cdnSdk.WafConfigPatch{ Mode: new(cdnSdk.WAFMODE_DISABLED), @@ -1446,10 +1485,40 @@ func mapFields(ctx context.Context, distribution *cdnSdk.Distribution, model *Mo return core.DiagsToError(diagWaf) } + // blockedIps + var blockedIps []attr.Value + if distribution.Config.BlockedIps != nil { + for _, ip := range distribution.Config.BlockedIps { + blockedIps = append(blockedIps, types.StringValue(ip)) + } + } + + modelBlockedIps, diags := types.ListValue(types.StringType, blockedIps) + if diags.HasError() { + return core.DiagsToError(diags) + } + + var defaultCacheDuration types.String + if distribution.Config.DefaultCacheDuration.IsSet() { + defaultCacheDuration = types.StringPointerValue(distribution.Config.DefaultCacheDuration.Get()) + } else { + defaultCacheDuration = types.StringNull() + } + + var monthlyLimitBytes types.Int64 + if distribution.Config.MonthlyLimitBytes.IsSet() { + monthlyLimitBytes = types.Int64PointerValue(distribution.Config.MonthlyLimitBytes.Get()) + } else { + monthlyLimitBytes = types.Int64Null() + } + cfg, diags := types.ObjectValue(configTypes, map[string]attr.Value{ "backend": backend, "regions": modelRegions, "blocked_countries": modelBlockedCountries, + "blocked_ips": modelBlockedIps, + "default_cache_duration": defaultCacheDuration, + "monthly_limit_bytes": monthlyLimitBytes, "optimizer": optimizerVal, "redirects": redirectsVal, "waf": wafVal, @@ -1572,6 +1641,7 @@ func toCreatePayload(ctx context.Context, model *Model) (*cdnSdk.CreateDistribut Regions: cfg.Regions, Backend: *backend, BlockedCountries: cfg.BlockedCountries, + BlockedIps: cfg.BlockedIps, Optimizer: optimizer, Redirects: cfg.Redirects, Waf: wafPayload, @@ -1584,6 +1654,12 @@ func toCreatePayload(ctx context.Context, model *Model) (*cdnSdk.CreateDistribut if !utils.IsUndefined(rawConfig.StripResponseCookies) { payload.StripResponseCookies = rawConfig.StripResponseCookies.ValueBoolPointer() } + if !utils.IsUndefined(rawConfig.DefaultCacheDuration) { + payload.DefaultCacheDuration = conversion.StringValueToPointer(rawConfig.DefaultCacheDuration) + } + if !utils.IsUndefined(rawConfig.MonthlyLimitBytes) { + payload.MonthlyLimitBytes = conversion.Int64ValueToPointer(rawConfig.MonthlyLimitBytes) + } return payload, nil } @@ -1747,14 +1823,29 @@ func convertConfig(ctx context.Context, model *Model) (*cdnSdk.Config, error) { } } + // blockedIps + var blockedIps []string + if configModel.BlockedIps != nil { + blockedIps = *configModel.BlockedIps + } + cdnConfig := &cdnSdk.Config{ Backend: cdnSdk.ConfigBackend{}, Regions: regions, BlockedCountries: blockedCountries, + BlockedIps: blockedIps, Redirects: redirectsConfig, Tls: tls, } + if !utils.IsUndefined(configModel.DefaultCacheDuration) { + cdnConfig.DefaultCacheDuration = *cdnSdk.NewNullableString(conversion.StringValueToPointer(configModel.DefaultCacheDuration)) + } + + if !utils.IsUndefined(configModel.MonthlyLimitBytes) { + cdnConfig.MonthlyLimitBytes = *cdnSdk.NewNullableInt64(conversion.Int64ValueToPointer(configModel.MonthlyLimitBytes)) + } + if !utils.IsUndefined(configModel.Waf) { var wafModel wafConfig diags := configModel.Waf.As(ctx, &wafModel, basetypes.ObjectAsOptions{}) diff --git a/stackit/internal/services/cdn/distribution/resource_test.go b/stackit/internal/services/cdn/distribution/resource_test.go index 7752039d3..7bd19bbfc 100644 --- a/stackit/internal/services/cdn/distribution/resource_test.go +++ b/stackit/internal/services/cdn/distribution/resource_test.go @@ -13,6 +13,19 @@ import ( cdnSdk "github.com/stackitcloud/stackit-sdk-go/services/cdn/v1api" ) +func createTestConfig(vals map[string]attr.Value) types.Object { + if _, ok := vals["blocked_ips"]; !ok { + vals["blocked_ips"] = types.ListValueMust(types.StringType, []attr.Value{}) + } + if _, ok := vals["default_cache_duration"]; !ok { + vals["default_cache_duration"] = types.StringNull() + } + if _, ok := vals["monthly_limit_bytes"]; !ok { + vals["monthly_limit_bytes"] = types.Int64Null() + } + return types.ObjectValueMust(configTypes, vals) +} + func TestToCreatePayload(t *testing.T) { headers := map[string]attr.Value{ "testHeader0": types.StringValue("testHeaderValue0"), @@ -84,7 +97,7 @@ func TestToCreatePayload(t *testing.T) { } redirectsAttrTypes := redirectsObjType.AttrTypes - config := types.ObjectValueMust(configTypes, map[string]attr.Value{ + config := createTestConfig(map[string]attr.Value{ "backend": backend, "regions": regionsFixture, "blocked_countries": blockedCountriesFixture, @@ -194,7 +207,7 @@ func TestToCreatePayload(t *testing.T) { }, "happy_path_with_optimizer": { Input: modelFixture(func(m *Model) { - m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{ + m.Config = createTestConfig(map[string]attr.Value{ "backend": backend, "regions": regionsFixture, "optimizer": optimizer, @@ -224,7 +237,7 @@ func TestToCreatePayload(t *testing.T) { }, "happy_path_with_redirects": { Input: modelFixture(func(m *Model) { - m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{ + m.Config = createTestConfig(map[string]attr.Value{ "backend": backend, "regions": regionsFixture, "optimizer": types.ObjectNull(optimizerTypes), @@ -283,7 +296,7 @@ func TestToCreatePayload(t *testing.T) { "origin_request_headers": types.MapNull(types.StringType), "geofencing": types.MapNull(geofencingTypes.ElemType), }) - m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{ + m.Config = createTestConfig(map[string]attr.Value{ "backend": bucketBackend, "regions": regionsFixture, // reusing the existing one "blocked_countries": blockedCountriesFixture, @@ -315,7 +328,7 @@ func TestToCreatePayload(t *testing.T) { }, "happy_path_with_waf": { Input: modelFixture(func(m *Model) { - m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{ + m.Config = createTestConfig(map[string]attr.Value{ "backend": backend, "regions": regionsFixture, "optimizer": types.ObjectNull(optimizerTypes), @@ -344,7 +357,7 @@ func TestToCreatePayload(t *testing.T) { }, "happy_path_with_strip_response_and_cookies_forward": { Input: modelFixture(func(m *Model) { - m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{ + m.Config = createTestConfig(map[string]attr.Value{ "backend": backend, "regions": regionsFixture, "optimizer": types.ObjectNull(optimizerTypes), @@ -375,7 +388,7 @@ func TestToCreatePayload(t *testing.T) { }, "happy_path_with_tls": { Input: modelFixture(func(m *Model) { - m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{ + m.Config = createTestConfig(map[string]attr.Value{ "backend": backend, "regions": regionsFixture, "optimizer": types.ObjectNull(optimizerTypes), @@ -435,7 +448,7 @@ func TestToCreatePayload(t *testing.T) { // set generated ID before diffing tc.Expected.IntentId = res.IntentId - diff := cmp.Diff(res, tc.Expected) + diff := cmp.Diff(res, tc.Expected, cmpopts.EquateEmpty()) if diff != "" { t.Fatalf("Create Payload not as expected: %s", diff) } @@ -472,7 +485,7 @@ func TestConvertConfig(t *testing.T) { blockedCountriesFixture := types.ListValueMust(types.StringType, blockedCountries) optimizer := types.ObjectValueMust(optimizerTypes, map[string]attr.Value{"enabled": types.BoolValue(true)}) - config := types.ObjectValueMust(configTypes, map[string]attr.Value{ + config := createTestConfig(map[string]attr.Value{ "backend": backend, "regions": regionsFixture, "optimizer": types.ObjectNull(optimizerTypes), @@ -587,7 +600,7 @@ func TestConvertConfig(t *testing.T) { }, "happy_path_with_optimizer": { Input: modelFixture(func(m *Model) { - m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{ + m.Config = createTestConfig(map[string]attr.Value{ "backend": backend, "regions": regionsFixture, "optimizer": optimizer, @@ -621,7 +634,7 @@ func TestConvertConfig(t *testing.T) { }, "happy_path_with_tls": { Input: modelFixture(func(m *Model) { - m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{ + m.Config = createTestConfig(map[string]attr.Value{ "backend": backend, "regions": regionsFixture, "optimizer": types.ObjectNull(optimizerTypes), @@ -661,7 +674,7 @@ func TestConvertConfig(t *testing.T) { }, "happy_path_with_waf": { Input: modelFixture(func(m *Model) { - m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{ + m.Config = createTestConfig(map[string]attr.Value{ "backend": backend, "regions": regionsFixture, "optimizer": types.ObjectNull(optimizerTypes), @@ -695,7 +708,7 @@ func TestConvertConfig(t *testing.T) { }, "happy_path_with_redirects": { Input: modelFixture(func(m *Model) { - m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{ + m.Config = createTestConfig(map[string]attr.Value{ "backend": backend, "regions": regionsFixture, "optimizer": types.ObjectNull(optimizerTypes), @@ -758,7 +771,7 @@ func TestConvertConfig(t *testing.T) { "origin_request_headers": types.MapNull(types.StringType), "geofencing": types.MapNull(geofencingTypes.ElemType), }) - m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{ + m.Config = createTestConfig(map[string]attr.Value{ "backend": bucketBackend, "regions": regionsFixture, "blocked_countries": blockedCountriesFixture, @@ -816,7 +829,9 @@ func TestConvertConfig(t *testing.T) { cmpopts.IgnoreUnexported( cdnSdk.NullableString{}, cdnSdk.NullableInt64{}, - )) + ), + cmpopts.EquateEmpty(), + ) if diff != "" { t.Fatalf("Create Payload not as expected: %s", diff) } @@ -922,7 +937,7 @@ func TestMapFields(t *testing.T) { "enable_tls_10": types.BoolValue(false), "enable_tls_11": types.BoolValue(false), }) - config := types.ObjectValueMust(configTypes, map[string]attr.Value{ + config := createTestConfig(map[string]attr.Value{ "backend": backend, "regions": regionsFixture, "blocked_countries": blockedCountriesFixture, @@ -1057,7 +1072,7 @@ func TestMapFields(t *testing.T) { "origin_request_headers": types.MapNull(types.StringType), "geofencing": types.MapNull(geofencingTypes.ElemType), }) - configOld := types.ObjectValueMust(configTypes, map[string]attr.Value{ + configOld := createTestConfig(map[string]attr.Value{ "backend": bucketBackendOld, "regions": regionsFixture, "blocked_countries": blockedCountriesFixture, @@ -1081,7 +1096,7 @@ func TestMapFields(t *testing.T) { }, "happy_path_with_optimizer": { Expected: expectedModel(func(m *Model) { - m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{ + m.Config = createTestConfig(map[string]attr.Value{ "backend": backend, "regions": regionsFixture, "optimizer": optimizer, @@ -1111,7 +1126,7 @@ func TestMapFields(t *testing.T) { "region": types.StringNull(), "credentials": types.ObjectNull(backendCredentialsTypes), }) - m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{ + m.Config = createTestConfig(map[string]attr.Value{ "backend": backendWithGeofencing, "regions": regionsFixture, "optimizer": types.ObjectNull(optimizerTypes), @@ -1130,7 +1145,7 @@ func TestMapFields(t *testing.T) { }, "happy_path_with_redirects": { Expected: expectedModel(func(m *Model) { - m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{ + m.Config = createTestConfig(map[string]attr.Value{ "backend": backend, "regions": regionsFixture, "optimizer": types.ObjectNull(optimizerTypes), @@ -1158,7 +1173,7 @@ func TestMapFields(t *testing.T) { }, "happy_path_with_waf": { Expected: expectedModel(func(m *Model) { - m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{ + m.Config = createTestConfig(map[string]attr.Value{ "backend": backend, "regions": regionsFixture, "optimizer": types.ObjectNull(optimizerTypes), @@ -1177,7 +1192,7 @@ func TestMapFields(t *testing.T) { }, "happy_path_with_tls_and_strip_response_and_cookies_forward": { Expected: expectedModel(func(m *Model) { - m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{ + m.Config = createTestConfig(map[string]attr.Value{ "backend": backend, "regions": regionsFixture, "optimizer": types.ObjectNull(optimizerTypes), @@ -1249,7 +1264,7 @@ func TestMapFields(t *testing.T) { m.Config = configOld }), Expected: expectedModel(func(m *Model) { - m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{ + m.Config = createTestConfig(map[string]attr.Value{ "backend": bucketBackendOld, "regions": regionsFixture, "blocked_countries": blockedCountriesFixture, diff --git a/stackit/internal/services/cdn/testdata/resource-http-base.tf b/stackit/internal/services/cdn/testdata/resource-http-base.tf index 2699404d5..57be68efd 100644 --- a/stackit/internal/services/cdn/testdata/resource-http-base.tf +++ b/stackit/internal/services/cdn/testdata/resource-http-base.tf @@ -4,6 +4,7 @@ variable "backend_http_type" {} variable "backend_origin_url" {} variable "geofencing_list" {} variable "blocked_countries" {} +variable "blocked_ips" {} variable "optimizer" {} variable "origin_request_headers_name" {} variable "origin_request_headers_value" {} @@ -21,6 +22,8 @@ variable "tls_enable_tls_10" {} variable "tls_enable_tls_11" {} variable "strip_response_cookies" {} variable "forward_host_header" {} +variable "monthly_limit_bytes" {} +variable "default_cache_duration" {} # dns variable "dns_zone_name" {} @@ -74,7 +77,25 @@ resource "stackit_cdn_distribution" "distribution" { } strip_response_cookies = var.strip_response_cookies forward_host_header = var.forward_host_header - waf = var.waf + monthly_limit_bytes = var.monthly_limit_bytes + default_cache_duration = var.default_cache_duration + waf = { + mode = var.waf_mode + type = var.waf_type + allowed_http_methods = [var.waf_allowed_http_methods_0] + allowed_request_content_types = [var.waf_allowed_request_content_types_0] + allowed_http_versions = [var.waf_allowed_http_versions_0] + paranoia_level = var.waf_paranoia_level + disabled_rule_ids = [var.waf_disabled_rule_ids_0] + enabled_rule_ids = [var.waf_enabled_rule_ids_0] + log_only_rule_ids = [var.waf_log_only_rule_ids_0] + disabled_rule_group_ids = [var.waf_disabled_rule_group_ids_0] + enabled_rule_group_ids = [var.waf_enabled_rule_group_ids_0] + log_only_rule_group_ids = [var.waf_log_only_rule_group_ids_0] + disabled_rule_collection_ids = [var.waf_disabled_rule_collection_ids_0] + enabled_rule_collection_ids = [var.waf_enabled_rule_collection_ids_0] + log_only_rule_collection_ids = [var.waf_log_only_rule_collection_ids_0] + } backend = { type = var.backend_http_type origin_url = var.backend_origin_url @@ -86,6 +107,7 @@ resource "stackit_cdn_distribution" "distribution" { } } blocked_countries = var.blocked_countries + blocked_ips = var.blocked_ips } }