# 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+ | ```hcl # 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:** ```bash # 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.) ```hcl 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 ```bash # After terraform apply wrangler d1 migrations apply ``` ### 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 ` 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 /` ### "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 ` (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 - [README](./README.md) - Provider setup - [Configuration](./configuration.md) - Resources - [API](./api.md) - Data sources - [Patterns](./patterns.md) - Use cases - Provider docs: https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs