Cloudflare and Terraform integration illustration

Up and Running with the Cloudflare Terraform Provider

The Cloudflare Terraform provider enables you to manage your Cloudflare resources using Infrastructure as Code principles. By storing your configuration in version control, you gain auditable changes, easy rollbacks, and the ability to use the same refined processes you already have for your cloud infrastructure.

Authentication and Setup

Modern authentication with the Cloudflare provider uses API tokens instead of the legacy API keys. API tokens are more secure and can be scoped to specific resources and permissions.

Creating an API Token

  1. Go to your Cloudflare dashboard → My Profile → API Tokens
  2. Click “Create Token”
  3. Use a preset template or create a custom token with specific permissions
  4. Copy the generated token securely

Provider Configuration

The recommended approach is to use environment variables for security:

export CLOUDFLARE_API_TOKEN="your-api-token-here"
terraform {
  required_version = "1.10.6"
  required_providers {
    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = "~> 5.0"
    }
  }
}

provider "cloudflare" {
  # API token will be read from CLOUDFLARE_API_TOKEN environment variable
}

Managing DNS and Zones

Rather than creating individual resource blocks for each DNS record or zone setting, we define our configuration as variables and use for_each to create resources. Throughout the examples below, you’ll see this pattern repeatedly used. This approach helps keep configurations consistent, reduce duplication, and make scaling changes easier. While you can split variables into dedicated resources when it makes sense, managing them in this way provides a cleaner, more maintainable infrastructure-as-code workflow.

Zones

Create and manage DNS zones:

resource "cloudflare_zone" "example" {
  account = {
    id = var.cloudflare_account_id
  }
  zone = "example.com"
  type = "full"
}

Zone Settings

Configure zone settings using a variable-driven approach:

variable "zone_settings" {
  description = "Cloudflare zone settings to configure"
  type        = map(any)
  default = {
    "automatic_https_rewrites" = "on"
    "ssl"                      = "full"
    "always_use_https"         = "on"
    "min_tls_version"          = "1.2"
    "browser_check"            = "on"
    "brotli"                   = "on"
    "rocket_loader"            = "on"
  }
}

resource "cloudflare_zone_setting" "settings" {
  for_each = var.zone_settings

  zone_id    = cloudflare_zone.example.id
  setting_id = each.key
  value      = each.value
}

DNS Records

Manage DNS records with various types:

variable "cloudflare_dns_records" {
  description = "DNS records to create in Cloudflare"
  type = map(object({
    type     = string
    name     = string
    content  = string
    ttl      = optional(number, 1)
    proxied  = optional(bool, false)
    priority = optional(number)
  }))
  default = {
    "root" = {
      type    = "CNAME"
      name    = "example.com"
      content = "site.pages.dev"
      ttl     = 1
      proxied = true
    }
    "www" = {
      type    = "CNAME"
      name    = "www"
      content = "site.pages.dev"
      ttl     = 1
      proxied = true
    }
    "blog" = {
      type    = "CNAME"
      name    = "blog"
      content = "blog.pages.dev"
      ttl     = 3600
      proxied = false
    }
  }
}

resource "cloudflare_dns_record" "dns_records" {
  for_each = var.cloudflare_dns_records

  zone_id  = cloudflare_zone.example.id
  type     = each.value.type
  name     = each.value.name
  content  = each.value.content
  ttl      = each.value.ttl
  proxied  = each.value.proxied
  priority = each.value.priority
}

Performance and Caching

Page Rules

Cloudflare is retiring Page Rules and moving everything over to the newer, more powerful Rules engine—so instead of relying on Page Rules, you’ll want to start using Cache Rules, Dynamic Redirects, and Configuration Rules for the same jobs (see cloudflare_ruleset below).

resource "cloudflare_page_rule" "static_cache" {
  zone_id  = cloudflare_zone.example.id
  target   = "static.example.com/*"
  priority = 1
  status   = "active"

  actions {
    cache_level    = "cache_everything"
    edge_cache_ttl = 86400
  }
}

Security Configuration with Rulesets

Dynamic Redirects

Configure HTTP to HTTPS redirects and other dynamic redirects using rulesets:

resource "cloudflare_ruleset" "redirects" {
  zone_id = cloudflare_zone.example.id
  kind    = "zone"
  phase   = "http_request_dynamic_redirect"
  name    = "redirects"

  rules = [
    {
      action = "redirect"
      action_parameters = {
        from_value = {
          preserve_query_string = false
          status_code           = 301
          target_url = {
            expression = "wildcard_replace(http.request.full_uri, r\"http://*\", r\"https://$${1}\")"
          }
        }
      }
      description = "Redirect from HTTP to HTTPS"
      enabled     = true
      expression  = "(http.request.full_uri wildcard r\"http://*\")"
    },
    {
      action = "redirect"
      action_parameters = {
        from_value = {
          preserve_query_string = true
          status_code           = 301
          target_url = {
            expression = "wildcard_replace(http.request.full_uri, r\"https://www.*\", r\"https://$${1}\")"
          }
        }
      }
      description = "Redirect www subdomain to apex domain"
      enabled     = true
      expression  = "(http.request.full_uri wildcard r\"https://www.*\")"
    }
  ]
}

Using R2 as a Terraform Backend

Cloudflare R2 provides S3-compatible object storage that can be used as a Terraform backend. This is cost-effective and keeps your infrastructure state close to your Cloudflare resources.

Create the R2 Bucket

resource "cloudflare_r2_bucket" "terraform_state" {
  account_id = var.cloudflare_account_id
  name       = "terraform-state-bucket"
  location   = "WEUR" # or other R2 locations
}

Configure Backend

In your backend.tf:

terraform {
  backend "s3" {
    region   = "WEUR"
    bucket   = "terraform-state-bucket"
    key      = "example/terraform.tfstate"
    endpoint = "https://xxx.r2.cloudflarestorage.com"

    # Configured locally, in CI, or through environment variables:
    # access_key = "your-r2-access-key"
    # secret_key = "your-r2-secret-key"
  }
}

Generate R2 Access Credentials

  1. Go to Cloudflare dashboard → R2 → Manage R2 API Tokens
  2. Create API token with read/write permissions for your bucket
  3. Copy the Access Key ID and Secret Access Key

Common Pitfalls and Gotchas

Default Zone Ruleset

When you encounter the error 'zone' is not a valid value for kind because exceeded maximum number of zone rulesets..., it indicates that Cloudflare has reached the limit for the number of zone-level rulesets in a specific phase; to manage this, you should import and modify the existing default zone ruleset for that phase.

Solution: Import the Existing Ruleset

  1. Get your zone ID from the Cloudflare dashboard

  2. Get the ruleset ID using the Cloudflare API, for example, if you’re trying to deploy a http_request_dynamic_redirect phase:

    curl -X GET \
    -H "Authorization: Bearer $API_TOKEN" \
    -H "Content-Type: application/json" \
    "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/rulesets/phases/http_request_dynamic_redirect/entrypoint"
  3. Import the ruleset before applying:

    tofu import cloudflare_ruleset.redirects <zone-id>/<ruleset-id>

Version 5.x Resource Name Changes

If you’re migrating from provider v4 to v5, be aware of resource name changes:

  • cloudflare_recordcloudflare_dns_record
  • Check the provider documentation for the complete migration guide and up-to-date resource names

Implementation Best Practices

  1. Use API tokens instead of legacy API keys for better security
  2. Store credentials securely and use environment variables if possible to prevent sensitive data getting into your backend configuration as this can leak sensitive credentials if improperly configured.
  3. Version your provider to ensure consistent behaviour
  4. Use variable-driven configuration for managing multiple similar resources
  5. Consider R2 for state storage to keep everything within the Cloudflare ecosystem

Conclusion

The Cloudflare Terraform provider offers comprehensive management of your Cloudflare infrastructure. With modern API token authentication, extensive resource coverage, and excellent integration with existing Terraform workflows, you can confidently manage DNS, security, performance, and edge computing resources as code. Whether you’re just getting started or optimising existing infrastructure, the Cloudflare Terraform provider provides the tools you need to manage your edge infrastructure effectively.

Was this post helpful?

Related articles