majesticlabs-dev

cloudflare-coder

This skill guides provisioning Cloudflare infrastructure with OpenTofu/Terraform. Use when managing zones, DNS records, WAF rules, SSL settings, Page Rules, or cache configuration.

majesticlabs-dev 49 10 Updated 6mo ago

Resources

1
GitHub

Install

npx skillscat add majesticlabs-dev/majestic-marketplace/cloudflare-coder

Install via the SkillsCat registry.

About this skill

The skill provides instructions for provisioning Cloudflare infrastructure using OpenTofu or Terraform, enabling management of zones, DNS records, WAF rules, SSL settings, page rules, and cache configuration. It solves the need to automate and version‑control cloud networking and security settings. Use it when setting up or updating a Cloudflare‑protected domain through code.

SKILL.md

Provider Setup

terraform {
  required_providers {
    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = "~> 4.0"
    }
  }
}

provider "cloudflare" {
  # Uses CLOUDFLARE_API_TOKEN env var
}

Authentication

export CLOUDFLARE_API_TOKEN="your-api-token"
# Or with 1Password
CLOUDFLARE_API_TOKEN=op://Infrastructure/Cloudflare/api_token

See references/cloudflare-api-permissions.md for token permissions and legacy API key setup.

Data Sources

Get Zone by Name

data "cloudflare_zone" "main" {
  name = "example.com"
}

Get Account ID

data "cloudflare_accounts" "main" {
  name = "My Account"
}

output "account_id" {
  value = data.cloudflare_accounts.main.accounts[0].id
}

DNS Records

Basic Records

resource "cloudflare_record" "root" {
  zone_id = data.cloudflare_zone.main.id
  name    = "@"
  type    = "A"
  content = var.server_ip
  ttl     = 1  # 1 = automatic
  proxied = true
}

resource "cloudflare_record" "www" {
  zone_id = data.cloudflare_zone.main.id
  name    = "www"
  type    = "CNAME"
  content = "@"
  ttl     = 1
  proxied = true
}

resource "cloudflare_record" "mx_primary" {
  zone_id  = data.cloudflare_zone.main.id
  name     = "@"
  type     = "MX"
  content  = "mail.example.com"
  ttl      = 3600
  priority = 10
  proxied  = false  # MX cannot be proxied
}

DNS Records with for_each

locals {
  dns_records = {
    api = {
      type    = "A"
      content = var.api_server_ip
      proxied = true
    }
    staging = {
      type    = "A"
      content = var.staging_server_ip
      proxied = true
    }
    mail = {
      type    = "A"
      content = var.mail_server_ip
      proxied = false
    }
  }
}

resource "cloudflare_record" "subdomain" {
  for_each = local.dns_records

  zone_id = data.cloudflare_zone.main.id
  name    = each.key
  type    = each.value.type
  content = each.value.content
  ttl     = 1
  proxied = each.value.proxied
}

SSL/TLS Settings

Zone SSL Settings

resource "cloudflare_zone_settings_override" "ssl" {
  zone_id = data.cloudflare_zone.main.id

  settings {
    ssl                      = "strict"
    min_tls_version          = "1.2"
    always_use_https         = "on"
    automatic_https_rewrites = "on"
    tls_1_3                  = "on"
    opportunistic_encryption = "on"

    security_header {
      enabled            = true
      max_age            = 31536000
      include_subdomains = true
      preload            = true
      nosniff            = true
    }
  }
}

Origin Certificates

resource "cloudflare_origin_ca_certificate" "origin" {
  csr                = tls_cert_request.origin.cert_request_pem
  hostnames          = ["example.com", "*.example.com"]
  request_type       = "origin-rsa"
  requested_validity = 5475  # 15 years (max)
}

resource "tls_private_key" "origin" {
  algorithm = "RSA"
  rsa_bits  = 2048
}

resource "tls_cert_request" "origin" {
  private_key_pem = tls_private_key.origin.private_key_pem

  subject {
    common_name  = "example.com"
    organization = "Example Inc"
  }
}

output "origin_certificate" {
  value     = cloudflare_origin_ca_certificate.origin.certificate
  sensitive = true
}

Cache Rules

Modern Cache Rules (Rulesets)

resource "cloudflare_ruleset" "cache" {
  zone_id     = data.cloudflare_zone.main.id
  name        = "Cache Rules"
  description = "Caching configuration"
  kind        = "zone"
  phase       = "http_request_cache_settings"

  rules {
    action = "set_cache_settings"
    action_parameters {
      cache = true
      edge_ttl {
        mode    = "override_origin"
        default = 2592000  # 30 days
      }
      browser_ttl {
        mode    = "override_origin"
        default = 86400  # 1 day
      }
    }
    expression  = "(http.request.uri.path.extension in {\"css\" \"js\" \"jpg\" \"jpeg\" \"png\" \"gif\" \"svg\" \"woff\" \"woff2\"})"
    description = "Cache static assets"
    enabled     = true
  }

  rules {
    action = "set_cache_settings"
    action_parameters {
      cache = false
    }
    expression  = "(starts_with(http.request.uri.path, \"/api/\"))"
    description = "Bypass cache for API"
    enabled     = true
  }

}

Redirect Rules

resource "cloudflare_ruleset" "redirects" {
  zone_id     = data.cloudflare_zone.main.id
  name        = "Redirects"
  description = "URL redirects"
  kind        = "zone"
  phase       = "http_request_dynamic_redirect"

  rules {
    action = "redirect"
    action_parameters {
      from_value {
        status_code = 301
        target_url {
          expression = "concat(\"https://example.com\", http.request.uri.path)"
        }
        preserve_query_string = true
      }
    }
    expression  = "(http.host eq \"www.example.com\")"
    description = "Redirect www to apex"
    enabled     = true
  }

  rules {
    action = "redirect"
    action_parameters {
      from_value {
        status_code = 301
        target_url {
          value = "https://example.com/new-page"
        }
      }
    }
    expression  = "(http.request.uri.path eq \"/old-page\")"
    description = "Redirect old page to new"
    enabled     = true
  }
}

Zone Settings

Performance Settings

resource "cloudflare_zone_settings_override" "performance" {
  zone_id = data.cloudflare_zone.main.id

  settings {
    brotli        = "on"
    early_hints   = "on"
    http2         = "on"
    http3         = "on"
    zero_rtt      = "on"
    rocket_loader = "on"

    minify {
      css  = "on"
      html = "on"
      js   = "on"
    }
  }
}

Security Settings

resource "cloudflare_zone_settings_override" "security" {
  zone_id = data.cloudflare_zone.main.id

  settings {
    security_level      = "medium"  # off, essentially_off, low, medium, high, under_attack
    challenge_ttl       = 1800
    browser_check       = "on"
    email_obfuscation   = "on"
    server_side_exclude = "on"
    hotlink_protection  = "on"
    ip_geolocation      = "on"
  }
}

Workers Routes

resource "cloudflare_worker_route" "api" {
  zone_id     = data.cloudflare_zone.main.id
  pattern     = "example.com/api/*"
  script_name = cloudflare_worker_script.api.name
}

resource "cloudflare_worker_script" "api" {
  account_id = var.cloudflare_account_id
  name       = "api-worker"
  content    = file("${path.module}/workers/api.js")
  module     = true

  plain_text_binding {
    name = "ENVIRONMENT"
    text = var.environment
  }

  secret_text_binding {
    name = "API_KEY"
    text = var.api_key
  }
}

References