diff --git a/.changelog/1688.txt b/.changelog/1688.txt
new file mode 100644
index 0000000000..0289f8b758
--- /dev/null
+++ b/.changelog/1688.txt
@@ -0,0 +1,3 @@
+```release-note:new-resource
+cloudflare_managed_headers
+```
\ No newline at end of file
diff --git a/docs/resources/managed_headers.md b/docs/resources/managed_headers.md
new file mode 100644
index 0000000000..80e23ad9a6
--- /dev/null
+++ b/docs/resources/managed_headers.md
@@ -0,0 +1,71 @@
+---
+page_title: "cloudflare_managed_headers Resource - Cloudflare"
+subcategory: ""
+description: |-
+ The Cloudflare Managed Headers https://developers.cloudflare.com/rules/transform/managed-transforms/
+ allows you to add or remove some predefined headers to one's requests or origin responses.
+---
+
+# cloudflare_managed_headers (Resource)
+
+The [Cloudflare Managed Headers](https://developers.cloudflare.com/rules/transform/managed-transforms/)
+allows you to add or remove some predefined headers to one's requests or origin responses.
+
+~> You can configure Managed Headers using the dashboard (https://api.cloudflare.com/#managed-headers-api-properties)
+Terraform will override your configuration if it exists.
+
+## Example Usage
+
+```terraform
+# Enable security headers using Managed Meaders
+resource "cloudflare_managed_headers" "example" {
+ zone_id = "cb029e245cfdd66dc8d2e570d5dd3322"
+
+ managed_request_headers {
+ id = "add_true_client_ip_headers"
+ enabled = true
+ }
+
+ managed_response_headers {
+ id = "remove_x-powered-by_header"
+ enabled = true
+ }
+}
+```
+
+
+## Schema
+
+### Required
+
+- `zone_id` (String) The zone identifier to target for the resource.
+
+### Optional
+
+- `managed_request_headers` (Block Set) The list of managed request headers. (see [below for nested schema](#nestedblock--managed_request_headers))
+- `managed_response_headers` (Block Set) The list of managed response headers. (see [below for nested schema](#nestedblock--managed_response_headers))
+
+### Read-Only
+
+- `id` (String) The ID of this resource.
+
+
+### Nested Schema for `managed_request_headers`
+
+Required:
+
+- `enabled` (Boolean) Whether the headers rule is active.
+- `id` (String) Unique headers rule identifier.
+
+
+
+### Nested Schema for `managed_response_headers`
+
+Required:
+
+- `enabled` (Boolean) Whether the headers rule is active.
+- `id` (String) Unique headers rule identifier.
+
+## Import
+
+Import is not supported for this resource.
diff --git a/docs/resources/ruleset.md b/docs/resources/ruleset.md
index 4a428c2af5..877a578c86 100644
--- a/docs/resources/ruleset.md
+++ b/docs/resources/ruleset.md
@@ -274,7 +274,7 @@ resource "cloudflare_ruleset" "custom_fields_logging_example" {
- `kind` (String) Type of Ruleset to create. Available values: `custom`, `managed`, `root`, `schema`, `zone`.
- `name` (String) Name of the ruleset.
-- `phase` (String) Point in the request/response lifecycle where the ruleset will be created. Available values: `ddos_l4`, `ddos_l7`, `http_log_custom_fields`, `http_request_firewall_custom`, `http_request_firewall_managed`, `http_request_late_transform`, `http_request_main`, `http_request_sanitize`, `http_request_transform`, `http_request_origin`, `http_response_firewall_managed`, `http_response_headers_transform`, `magic_transit`, `http_ratelimit`, `http_request_sbfm`.
+- `phase` (String) Point in the request/response lifecycle where the ruleset will be created. Available values: `ddos_l4`, `ddos_l7`, `http_log_custom_fields`, `http_request_cache_settings`, `http_request_firewall_custom`, `http_request_firewall_managed`, `http_request_late_transform`, `http_request_late_transform_managed`, `http_request_main`, `http_request_origin`, `http_request_redirect`, `http_request_sanitize`, `http_request_transform`, `http_response_firewall_managed`, `http_response_headers_transform`, `magic_transit`, `http_ratelimit`, `http_request_sbfm`.
### Optional
@@ -297,7 +297,7 @@ Required:
Optional:
-- `action` (String) Action to perform in the ruleset rule. Available values: `block`, `challenge`, `ddos_dynamic`, `execute`, `force_connection_close`, `js_challenge`, `managed_challenge`, `log`, `log_custom_field`, `rewrite`, `score`, `skip`, `route`.
+- `action` (String) Action to perform in the ruleset rule. Available values: `block`, `challenge`, `ddos_dynamic`, `execute`, `force_connection_close`, `js_challenge`, `log`, `log_custom_field`, `managed_challenge`, `redirect`, `rewrite`, `route`, `score`, `set_cache_settings`, `skip`.
- `action_parameters` (Block List, Max: 1) List of parameters that configure the behavior of the ruleset rule action. (see [below for nested schema](#nestedblock--rules--action_parameters))
- `description` (String) Brief summary of the ruleset rule and its intended use.
- `enabled` (Boolean) Whether the rule is active.
@@ -324,7 +324,7 @@ Optional:
- `matched_data` (Block List, Max: 1) List of properties to configure WAF payload logging. (see [below for nested schema](#nestedblock--rules--action_parameters--matched_data))
- `origin` (Block List, Max: 1) List of properties to change request origin. (see [below for nested schema](#nestedblock--rules--action_parameters--origin))
- `overrides` (Block List, Max: 1) List of override configurations to apply to the ruleset. (see [below for nested schema](#nestedblock--rules--action_parameters--overrides))
-- `phases` (Set of String) Point in the request/response lifecycle where the ruleset will be created. Available values: `ddos_l4`, `ddos_l7`, `http_log_custom_fields`, `http_request_firewall_custom`, `http_request_firewall_managed`, `http_request_late_transform`, `http_request_main`, `http_request_sanitize`, `http_request_transform`, `http_request_origin`, `http_response_firewall_managed`, `http_response_headers_transform`, `magic_transit`, `http_ratelimit`, `http_request_sbfm`.
+- `phases` (Set of String) Point in the request/response lifecycle where the ruleset will be created. Available values: `ddos_l4`, `ddos_l7`, `http_log_custom_fields`, `http_request_cache_settings`, `http_request_firewall_custom`, `http_request_firewall_managed`, `http_request_late_transform`, `http_request_late_transform_managed`, `http_request_main`, `http_request_origin`, `http_request_redirect`, `http_request_sanitize`, `http_request_transform`, `http_response_firewall_managed`, `http_response_headers_transform`, `magic_transit`, `http_ratelimit`, `http_request_sbfm`.
- `products` (Set of String) Products to target with the actions. Available values: `bic`, `hot`, `ratelimit`, `securityLevel`, `uablock`, `waf`, `zonelockdown`.
- `request_fields` (Set of String) List of request headers to include as part of custom fields logging, in lowercase.
- `response` (Block List) List of parameters that configure the response given to end users. (see [below for nested schema](#nestedblock--rules--action_parameters--response))
@@ -368,7 +368,7 @@ Optional:
Optional:
-- `action` (String) Action to perform in the rule-level override. Available values: `block`, `challenge`, `ddos_dynamic`, `execute`, `force_connection_close`, `js_challenge`, `managed_challenge`, `log`, `log_custom_field`, `rewrite`, `score`, `skip`, `route`.
+- `action` (String) Action to perform in the rule-level override. Available values: `block`, `challenge`, `ddos_dynamic`, `execute`, `force_connection_close`, `js_challenge`, `log`, `log_custom_field`, `managed_challenge`, `redirect`, `rewrite`, `route`, `score`, `set_cache_settings`, `skip`.
- `categories` (Block List) List of tag-based overrides. (see [below for nested schema](#nestedblock--rules--action_parameters--overrides--categories))
- `enabled` (Boolean, Deprecated) Defines if the current ruleset-level override enables or disables the ruleset.
- `rules` (Block List) List of rule-based overrides. (see [below for nested schema](#nestedblock--rules--action_parameters--overrides--rules))
@@ -379,7 +379,7 @@ Optional:
Optional:
-- `action` (String) Action to perform in the tag-level override. Available values: `block`, `challenge`, `ddos_dynamic`, `execute`, `force_connection_close`, `js_challenge`, `managed_challenge`, `log`, `log_custom_field`, `rewrite`, `score`, `skip`, `route`.
+- `action` (String) Action to perform in the tag-level override. Available values: `block`, `challenge`, `ddos_dynamic`, `execute`, `force_connection_close`, `js_challenge`, `log`, `log_custom_field`, `managed_challenge`, `redirect`, `rewrite`, `route`, `score`, `set_cache_settings`, `skip`.
- `category` (String) Tag name to apply the ruleset rule override to.
- `enabled` (Boolean, Deprecated) Defines if the current tag-level override enables or disables the ruleset rules with the specified tag.
- `status` (String) Defines if the current tag-level override enables or disables the ruleset rules with the specified tag. Available values: `enabled`, `disabled`. Defaults to `""`.
@@ -390,7 +390,7 @@ Optional:
Optional:
-- `action` (String) Action to perform in the rule-level override. Available values: `block`, `challenge`, `ddos_dynamic`, `execute`, `force_connection_close`, `js_challenge`, `managed_challenge`, `log`, `log_custom_field`, `rewrite`, `score`, `skip`, `route`.
+- `action` (String) Action to perform in the rule-level override. Available values: `block`, `challenge`, `ddos_dynamic`, `execute`, `force_connection_close`, `js_challenge`, `log`, `log_custom_field`, `managed_challenge`, `redirect`, `rewrite`, `route`, `score`, `set_cache_settings`, `skip`.
- `enabled` (Boolean, Deprecated) Defines if the current rule-level override enables or disables the rule.
- `id` (String) Rule ID to apply the override to.
- `score_threshold` (Number) Anomaly score threshold to apply in the ruleset rule override. Only applicable to modsecurity-based rulesets.
diff --git a/examples/resources/managed_headers/resource.tf b/examples/resources/managed_headers/resource.tf
new file mode 100644
index 0000000000..9340aa8d12
--- /dev/null
+++ b/examples/resources/managed_headers/resource.tf
@@ -0,0 +1,14 @@
+# Enable security headers using Managed Meaders
+resource "cloudflare_managed_headers" "add_security_headers_example" {
+ zone_id = "cb029e245cfdd66dc8d2e570d5dd3322"
+
+ managed_request_headers {
+ id = "add_true_client_ip_headers"
+ enabled = true
+ }
+
+ managed_response_headers {
+ id = "remove_x-powered-by_header"
+ enabled = true
+ }
+}
diff --git a/internal/provider/provider.go b/internal/provider/provider.go
index e64ede1823..031fce9b3c 100644
--- a/internal/provider/provider.go
+++ b/internal/provider/provider.go
@@ -197,6 +197,7 @@ func New(version string) func() *schema.Provider {
"cloudflare_logpush_job": resourceCloudflareLogpushJob(),
"cloudflare_logpush_ownership_challenge": resourceCloudflareLogpushOwnershipChallenge(),
"cloudflare_magic_firewall_ruleset": resourceCloudflareMagicFirewallRuleset(),
+ "cloudflare_managed_headers": resourceCloudflareManagedHeaders(),
"cloudflare_notification_policy_webhooks": resourceCloudflareNotificationPolicyWebhooks(),
"cloudflare_notification_policy": resourceCloudflareNotificationPolicy(),
"cloudflare_origin_ca_certificate": resourceCloudflareOriginCACertificate(),
diff --git a/internal/provider/resource_cloudflare_managed_headers.go b/internal/provider/resource_cloudflare_managed_headers.go
new file mode 100644
index 0000000000..55a7ddc78e
--- /dev/null
+++ b/internal/provider/resource_cloudflare_managed_headers.go
@@ -0,0 +1,183 @@
+package provider
+
+import (
+ "context"
+ "errors"
+ "fmt"
+
+ cloudflare "github.com/cloudflare/cloudflare-go"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+func resourceCloudflareManagedHeaders() *schema.Resource {
+ return &schema.Resource{
+ Schema: resourceCloudflareManagedHeadersSchema(),
+ CreateContext: resourceCloudflareManagedHeadersCreate,
+ ReadContext: resourceCloudflareManagedHeadersRead,
+ UpdateContext: resourceCloudflareManagedHeadersUpdate,
+ DeleteContext: resourceCloudflareManagedHeadersDelete,
+ SchemaVersion: 0,
+ Description: `
+The [Cloudflare Managed Headers](https://developers.cloudflare.com/rules/transform/managed-transforms/)
+allows you to add or remove some predefined headers to one's requests or origin responses.`,
+ }
+}
+
+func resourceCloudflareManagedHeadersCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ zoneID := d.Get("zone_id").(string)
+ d.SetId(zoneID)
+ return resourceCloudflareManagedHeadersUpdate(ctx, d, meta)
+}
+
+func resourceCloudflareManagedHeadersRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ client := meta.(*cloudflare.API)
+ zoneID := d.Get("zone_id").(string)
+ headers, err := client.ListZoneManagedHeaders(ctx, cloudflare.ListManagedHeadersParams{
+ ZoneID: zoneID,
+ })
+ if err != nil {
+ return diag.FromErr(fmt.Errorf("error reading managed headers: %w", err))
+ }
+
+ // Filter out headers that are not enabled. This will eventually move into
+ // the API endpoint or the SDK.
+ var enabledRequestHeaders []cloudflare.ManagedHeader
+ var enabledResponseHeaders []cloudflare.ManagedHeader
+
+ for _, header := range headers.ManagedRequestHeaders {
+ if header.Enabled {
+ enabledRequestHeaders = append(enabledRequestHeaders, header)
+ }
+ }
+
+ for _, header := range headers.ManagedResponseHeaders {
+ if header.Enabled {
+ enabledResponseHeaders = append(enabledResponseHeaders, header)
+ }
+ }
+
+ if err := d.Set("managed_request_headers", buildResourceFromManagedHeaders(enabledRequestHeaders)); err != nil {
+ return diag.FromErr(err)
+ }
+ if err := d.Set("managed_response_headers", buildResourceFromManagedHeaders(enabledResponseHeaders)); err != nil {
+ return diag.FromErr(err)
+ }
+ return nil
+}
+
+func buildResourceFromManagedHeaders(headers []cloudflare.ManagedHeader) interface{} {
+ headersState := []map[string]interface{}{}
+ for _, header := range headers {
+ headersState = append(headersState, map[string]interface{}{
+ "id": header.ID,
+ "enabled": header.Enabled,
+ })
+ }
+
+ return headersState
+}
+
+func resourceCloudflareManagedHeadersUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ client := meta.(*cloudflare.API)
+ zoneID := d.Get("zone_id").(string)
+
+ mh, err := buildManagedHeadersFromResource(d)
+ if err != nil {
+ return diag.FromErr(fmt.Errorf("error building managed headers from resource: %w", err))
+ }
+ if _, err := client.UpdateZoneManagedHeaders(ctx, cloudflare.UpdateManagedHeadersParams{
+ ManagedHeaders: mh,
+ ZoneID: zoneID,
+ }); err != nil {
+ return diag.FromErr(fmt.Errorf("error updating managed headers: %w", err))
+ }
+ return resourceCloudflareManagedHeadersRead(ctx, d, meta)
+}
+
+// receives the resource config and builds a managed headers struct.
+func buildManagedHeadersFromResource(d *schema.ResourceData) (cloudflare.ManagedHeaders, error) {
+ requestHeaders, ok := d.Get("managed_request_headers").(*schema.Set)
+ if !ok {
+ return cloudflare.ManagedHeaders{}, errors.New("unable to create interface array type assertion")
+ }
+ reqHeaders, err := buildManagedHeadersListFromResource(requestHeaders)
+ if err != nil {
+ return cloudflare.ManagedHeaders{}, err
+ }
+
+ responseHeaders, ok := d.Get("managed_response_headers").(*schema.Set)
+ if !ok {
+ return cloudflare.ManagedHeaders{}, errors.New("unable to create interface array type assertion")
+ }
+ respHeaders, err := buildManagedHeadersListFromResource(responseHeaders)
+ if err != nil {
+ return cloudflare.ManagedHeaders{}, err
+ }
+
+ return cloudflare.ManagedHeaders{
+ ManagedRequestHeaders: reqHeaders,
+ ManagedResponseHeaders: respHeaders,
+ }, nil
+}
+
+func buildManagedHeadersListFromResource(resource *schema.Set) ([]cloudflare.ManagedHeader, error) {
+ headers := make([]cloudflare.ManagedHeader, 0, len(resource.List()))
+ for _, header := range resource.List() {
+ h, ok := header.(map[string]interface{})
+ if !ok {
+ return nil, errors.New("unable to create interface map type assertion for managed header")
+ }
+ id, ok := h["id"].(string)
+ if !ok {
+ return nil, errors.New("unable to create string type assertion for managed header ID")
+ }
+ enabled, ok := h["enabled"].(bool)
+ if !ok {
+ return nil, errors.New("unable to create bool type assertion for managed header enabled")
+ }
+
+ if enabled {
+ headers = append(headers, cloudflare.ManagedHeader{
+ ID: id,
+ Enabled: enabled,
+ })
+ }
+ }
+ return headers, nil
+}
+
+func resourceCloudflareManagedHeadersDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ client := meta.(*cloudflare.API)
+ zoneID := d.Get("zone_id").(string)
+
+ headers, err := client.ListZoneManagedHeaders(ctx, cloudflare.ListManagedHeadersParams{
+ ZoneID: zoneID,
+ })
+ if err != nil {
+ return diag.FromErr(fmt.Errorf("error reading managed headers: %w", err))
+ }
+
+ requestHeaders := make([]cloudflare.ManagedHeader, 0, len(headers.ManagedRequestHeaders))
+ for _, header := range headers.ManagedRequestHeaders {
+ header.Enabled = false
+ requestHeaders = append(requestHeaders, header)
+ }
+ responseHeaders := make([]cloudflare.ManagedHeader, 0, len(headers.ManagedResponseHeaders))
+ for _, header := range headers.ManagedResponseHeaders {
+ header.Enabled = false
+ responseHeaders = append(responseHeaders, header)
+ }
+
+ if _, err := client.UpdateZoneManagedHeaders(ctx, cloudflare.UpdateManagedHeadersParams{
+ ManagedHeaders: cloudflare.ManagedHeaders{
+ ManagedRequestHeaders: requestHeaders,
+ ManagedResponseHeaders: responseHeaders,
+ },
+ ZoneID: zoneID,
+ }); err != nil {
+ return diag.FromErr(fmt.Errorf("error deleting managed headers with ID %q: %w", d.Id(), err))
+ }
+
+ return nil
+}
diff --git a/internal/provider/resource_cloudflare_managed_headers_test.go b/internal/provider/resource_cloudflare_managed_headers_test.go
new file mode 100644
index 0000000000..38a3900873
--- /dev/null
+++ b/internal/provider/resource_cloudflare_managed_headers_test.go
@@ -0,0 +1,119 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "testing"
+
+ cloudflare "github.com/cloudflare/cloudflare-go"
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+ "github.com/pkg/errors"
+)
+
+func init() {
+ resource.AddTestSweepers("cloudflare_managed_headers", &resource.Sweeper{
+ Name: "cloudflare_managed_headers",
+ F: testSweepCloudflareManagedHeaders,
+ })
+}
+
+func testSweepCloudflareManagedHeaders(r string) error {
+ ctx := context.Background()
+ client, clientErr := sharedClient()
+ if clientErr != nil {
+ tflog.Error(ctx, fmt.Sprintf("Failed to create Cloudflare client: %s", clientErr))
+ }
+
+ zone := os.Getenv("CLOUDFLARE_ZONE_ID")
+ if zone == "" {
+ return errors.New("CLOUDFLARE_ZONE_ID must be set")
+ }
+
+ managedHeaders, err := client.ListZoneManagedHeaders(context.Background(), cloudflare.ListManagedHeadersParams{
+ ZoneID: zoneID,
+ })
+ if err != nil {
+ tflog.Error(ctx, fmt.Sprintf("Failed to fetch Cloudflare Zone Managed Headers: %s", err))
+ }
+
+ requestHeaders := make([]cloudflare.ManagedHeader, 0, len(managedHeaders.ManagedRequestHeaders))
+ for _, h := range managedHeaders.ManagedRequestHeaders {
+ tflog.Info(ctx, fmt.Sprintf("Disabling Cloudflare Zone Managed Header ID: %s", h.ID))
+ h.Enabled = false
+ requestHeaders = append(requestHeaders, h)
+ }
+ responseHeaders := make([]cloudflare.ManagedHeader, 0, len(managedHeaders.ManagedResponseHeaders))
+ for _, h := range managedHeaders.ManagedResponseHeaders {
+ tflog.Info(ctx, fmt.Sprintf("Disabling Cloudflare Zone Managed Header ID: %s", h.ID))
+ h.Enabled = false
+ responseHeaders = append(responseHeaders, h)
+ }
+
+ _, err = client.UpdateZoneManagedHeaders(context.Background(), cloudflare.UpdateManagedHeadersParams{
+ ManagedHeaders: cloudflare.ManagedHeaders{
+ ManagedRequestHeaders: requestHeaders,
+ ManagedResponseHeaders: responseHeaders,
+ },
+ ZoneID: zoneID,
+ })
+ if err != nil {
+ tflog.Error(ctx, fmt.Sprintf("Failed to disable Cloudflare Zone Managed Headers: %s", err))
+ }
+
+ return nil
+}
+
+func TestAccCloudflareManagedHeaders(t *testing.T) {
+ t.Parallel()
+
+ rnd := generateRandomResourceName()
+ zoneID := os.Getenv("CLOUDFLARE_ZONE_ID")
+ resourceName := "cloudflare_managed_headers." + rnd
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ ProviderFactories: providerFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccCheckCloudflareManagedHeaders(rnd, zoneID),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "zone_id", zoneID),
+ resource.TestCheckResourceAttr(resourceName, "managed_request_headers.#", "2"),
+ resource.TestCheckResourceAttr(resourceName, "managed_request_headers.0.%", "2"),
+ resource.TestCheckResourceAttr(resourceName, "managed_request_headers.0.id", "add_true_client_ip_headers"),
+ resource.TestCheckResourceAttr(resourceName, "managed_request_headers.0.enabled", "true"),
+ resource.TestCheckResourceAttr(resourceName, "managed_request_headers.1.%", "2"),
+ resource.TestCheckResourceAttr(resourceName, "managed_request_headers.1.id", "add_visitor_location_headers"),
+ resource.TestCheckResourceAttr(resourceName, "managed_request_headers.1.enabled", "true"),
+
+ resource.TestCheckResourceAttr(resourceName, "managed_response_headers.#", "1"),
+ resource.TestCheckResourceAttr(resourceName, "managed_response_headers.0.%", "2"),
+ resource.TestCheckResourceAttr(resourceName, "managed_response_headers.0.id", "add_security_headers"),
+ resource.TestCheckResourceAttr(resourceName, "managed_response_headers.0.enabled", "true"),
+ ),
+ },
+ },
+ })
+}
+
+func testAccCheckCloudflareManagedHeaders(rnd, zoneID string) string {
+ return fmt.Sprintf(`
+ resource "cloudflare_managed_headers" "%[1]s" {
+ zone_id = "%[2]s"
+ managed_request_headers {
+ id = "add_true_client_ip_headers"
+ enabled = true
+ }
+
+ managed_request_headers {
+ id = "add_visitor_location_headers"
+ enabled = true
+ }
+
+ managed_response_headers {
+ id = "add_security_headers"
+ enabled = true
+ }
+ }`, rnd, zoneID)
+}
diff --git a/internal/provider/schema_cloudflare_managed_headers.go b/internal/provider/schema_cloudflare_managed_headers.go
new file mode 100644
index 0000000000..9f9be8cea4
--- /dev/null
+++ b/internal/provider/schema_cloudflare_managed_headers.go
@@ -0,0 +1,51 @@
+package provider
+
+import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+
+func resourceCloudflareManagedHeadersSchema() map[string]*schema.Schema {
+ return map[string]*schema.Schema{
+ "zone_id": {
+ Description: "The zone identifier to target for the resource.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "managed_request_headers": {
+ Description: "The list of managed request headers",
+ Type: schema.TypeSet,
+ Optional: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "id": {
+ Type: schema.TypeString,
+ Required: true,
+ Description: "Unique headers rule identifier.",
+ },
+ "enabled": {
+ Type: schema.TypeBool,
+ Required: true,
+ Description: "Whether the headers rule is active.",
+ },
+ },
+ },
+ },
+ "managed_response_headers": {
+ Description: "The list of managed response headers",
+ Type: schema.TypeSet,
+ Optional: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "id": {
+ Type: schema.TypeString,
+ Required: true,
+ Description: "Unique headers rule identifier.",
+ },
+ "enabled": {
+ Type: schema.TypeBool,
+ Required: true,
+ Description: "Whether the headers rule is active.",
+ },
+ },
+ },
+ },
+ }
+}
diff --git a/templates/resources/managed_headers.md.tmpl b/templates/resources/managed_headers.md.tmpl
new file mode 100644
index 0000000000..514e9f6ada
--- /dev/null
+++ b/templates/resources/managed_headers.md.tmpl
@@ -0,0 +1,23 @@
+---
+page_title: "{{.Name}} {{.Type}} - {{.RenderedProviderName}}"
+subcategory: ""
+description: |-
+{{ .Description | plainmarkdown | trimspace | prefixlines " " }}
+---
+
+# {{.Name}} ({{.Type}})
+
+{{ .Description | trimspace }}
+
+~> You can configure Managed Headers using the dashboard (https://api.cloudflare.com/#managed-headers-api-properties)
+Terraform will override your configuration if it exists.
+
+## Example Usage
+
+{{ tffile (printf "%s%s%s" "examples/resources/" .Name "/resource.tf") }}
+
+{{ .SchemaMarkdown | trimspace }}
+
+## Import
+
+Import is not supported for this resource.