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
- README - Provider setup
- Configuration - Resources
- API - Data sources
- Patterns - Use cases
- Provider docs: https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs