Files
dotfiles/.agents/skills/cloudflare-deploy/references/terraform/gotchas.md
2026-03-17 16:53:22 -07:00

5.1 KiB

Terraform Troubleshooting & Best Practices

Common issues, security considerations, and best practices.

State Drift Issues

Some resources have known state drift. Add lifecycle blocks to prevent perpetual diffs:

Resource Drift Attributes Workaround
cloudflare_pages_project deployment_configs.* ignore_changes = [deployment_configs]
cloudflare_workers_script secrets returned as REDACTED ignore_changes = [secret_text_binding]
cloudflare_load_balancer adaptive_routing, random_steering ignore_changes = [adaptive_routing, random_steering]
cloudflare_workers_kv special chars in keys (< 5.16.0) Upgrade to 5.16.0+
# Example: Ignore secret drift
resource "cloudflare_workers_script" "api" {
  account_id = var.account_id
  name = "api-worker"
  content = file("worker.js")
  secret_text_binding { name = "API_KEY"; text = var.api_key }
  
  lifecycle {
    ignore_changes = [secret_text_binding]
  }
}

v5 Breaking Changes

Provider v5 is current (auto-generated from OpenAPI). v4→v5 has breaking changes:

Resource Renames:

v4 Resource v5 Resource Notes
cloudflare_record cloudflare_dns_record
cloudflare_worker_script cloudflare_workers_script Note: plural
cloudflare_worker_* cloudflare_workers_* All worker resources
cloudflare_access_* cloudflare_zero_trust_* Access → Zero Trust

Attribute Changes:

v4 Attribute v5 Attribute Resources
zone name zone
account_id account.id zone (object syntax)
key key_name KV
location_hint location R2

State Migration:

# Rename resources in state after v5 upgrade
terraform state mv cloudflare_record.example cloudflare_dns_record.example
terraform state mv cloudflare_worker_script.api cloudflare_workers_script.api

Resource-Specific Gotchas

R2 Location Case Sensitivity

Problem: Terraform creates R2 bucket but fails on subsequent applies
Cause: Location must be UPPERCASE
Solution: Use WNAM, ENAM, WEUR, EEUR, APAC (not wnam, enam, etc.)

resource "cloudflare_r2_bucket" "assets" {
  account_id = var.account_id
  name = "assets"
  location = "WNAM"  # UPPERCASE required
}

KV Special Characters (< 5.16.0)

Problem: Keys with +, #, % cause encoding issues
Cause: URL encoding bug in provider < 5.16.0
Solution: Upgrade to 5.16.0+ or avoid special chars in keys

D1 Migrations

Problem: Terraform creates database but schema is empty
Cause: Terraform only creates D1 resource, not schema
Solution: Run migrations via wrangler after Terraform apply

# After terraform apply
wrangler d1 migrations apply <db-name>

Worker Script Size Limit

Problem: Worker deployment fails with "script too large"
Cause: Worker script + dependencies exceed 10 MB limit
Solution: Use code splitting, external dependencies, or minification

Pages Project Drift

Problem: Pages project shows perpetual diff on deployment_configs
Cause: Cloudflare API adds default values not in Terraform state
Solution: Add lifecycle ignore block (see State Drift table above)

Common Errors

"Error: couldn't find resource"

Cause: Resource was deleted outside Terraform
Solution: Import resource back into state with terraform import cloudflare_zone.example <zone-id> or remove from state with terraform state rm cloudflare_zone.example

"409 Conflict on worker deployment"

Cause: Worker being deployed by both Terraform and wrangler simultaneously
Solution: Choose one deployment method; if using Terraform, remove wrangler deployments

"DNS record already exists"

Cause: Existing DNS record not imported into Terraform state
Solution: Find record ID in Cloudflare dashboard and import with terraform import cloudflare_dns_record.example <zone-id>/<record-id>

"Invalid provider configuration"

Cause: API token missing, invalid, or lacking required permissions
Solution: Set CLOUDFLARE_API_TOKEN environment variable or check token permissions in dashboard

"State locking errors"

Cause: Multiple concurrent Terraform runs or stale lock from crashed process
Solution: Remove stale lock with terraform force-unlock <lock-id> (use with caution)

Limits

Resource Limit Notes
API token rate limit Varies by plan Use api_client_logging = true to debug
Worker script size 10 MB Includes all dependencies
KV keys per namespace Unlimited Pay per operation
R2 storage Unlimited Pay per GB
D1 databases 50,000 per account Free tier: 10
Pages projects 500 per account 100 for free accounts
DNS records 3,500 per zone Free plan

See Also