Terraform Cloudflare DNS Module Design Prompt
Design a Cloudflare provider module that manages a zone and many DNS records via a for_each map while guarding apex and NS records from accidental deletion.
- Target user
- Platform engineers managing Cloudflare DNS at scale
- Difficulty
- Advanced
- Tools
- Claude, ChatGPT, Cursor
The prompt
You are a senior platform engineer designing a reusable Terraform module for Cloudflare DNS using the `cloudflare/cloudflare` provider (>= 4.30). It must manage a zone and a large, changing set of records through a single `records` map with `for_each`, while making apex and delegation records hard to delete by accident.
Work through these steps in order:
1. Configure the provider with an API token (`CLOUDFLARE_API_TOKEN`) scoped to Zone.DNS edit only. Manage the zone with `cloudflare_zone` (or accept a `zone_id` var for pre-existing zones).
2. Model a typed `records` variable as `map(object({ name, type, value/content, ttl, proxied, priority }))`. Iterate with `for_each = var.records` keyed by a stable identifier like `"<name>|<type>"` so adding/removing one record never shifts others.
3. Handle the `proxied` flag correctly: `proxied = true` forces `ttl = 1` (automatic). Validate that `MX`/`TXT`/`NS` records are never proxied, and that CNAME-at-apex uses CNAME flattening.
4. Protect critical records. Apply `lifecycle { prevent_destroy = true }` to the apex `A`/`AAAA`/`CNAME` and any `NS` records, since deleting them causes an immediate outage or NXDOMAIN. Because `prevent_destroy` cannot be conditional per `for_each` instance, split protected records into a separate hardened resource block or module.
5. Warn about DESTRUCTIVE operations: removing a record from the map DELETES it in Cloudflare (traffic loss); changing `type` forces destroy/recreate with a resolution gap; and changing zone name servers or the zone itself can de-delegate the domain entirely.
6. Recommend `terraform plan` gated in CI with a required review on any line containing `will be destroyed`, and importing existing records rather than recreating.
Input template:
```
Zone: <example.com> (existing zone_id or create new?)
Record count / churn: <e.g. ~200 records, weekly changes>
Records needing proxy (orange cloud): <www, api, ...>
Protected records: <apex A, NS, MX>
Token scope confirmed: <yes/no>
```
Run this prompt with AI
Test it, get an AI-improved version, or compare models — live in the Prompt Workspace. No copy-paste.
Why this prompt works
DNS is a single point of failure: one deleted apex record is an instant outage, and for_each over a map makes it trivially easy to drop a record by editing a variable. This prompt separates the bulk, low-risk records (managed fluidly through a map) from the small set of load-bearing apex and delegation records (hardened with prevent_destroy). It also nails the Cloudflare-specific gotchas around the proxied flag and TTL that break naive configs.
How to use it
Give the model your zone, approximate record count and churn, which hostnames need the orange-cloud proxy, and which records are load-bearing. Ask for two resource blocks: a hardened cloudflare_record set for apex/NS/MX with prevent_destroy, and a bulk for_each block for everything else. Wire a CI check that fails on unreviewed destroys.
Useful commands
# Scoped token (Zone.DNS:Edit) - never a global API key
export CLOUDFLARE_API_TOKEN=...
# Look up the zone id
curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
"https://api.cloudflare.com/client/v4/zones?name=example.com" | jq '.result[].id'
# Plan and fail CI on any destructive DNS change
terraform plan -no-color -out=tfplan
terraform show -json tfplan \
| jq -e '[.resource_changes[] | select(.change.actions | index("delete"))] | length == 0'
# Import an existing record instead of recreating it (v4: <zone_id>/<record_id>)
terraform import 'cloudflare_record.bulk["www|CNAME"]' <zone_id>/<record_id>
# List records to reconcile drift
curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?per_page=100" \
| jq '.result[] | {name, type, content, proxied}'
Patterns
Bulk records via a stably keyed for_each map:
variable "records" {
type = map(object({
name = string
type = string
content = string
ttl = optional(number, 1)
proxied = optional(bool, false)
priority = optional(number)
}))
}
resource "cloudflare_record" "bulk" {
for_each = var.records
zone_id = var.zone_id
name = each.value.name
type = each.value.type
content = each.value.content
proxied = each.value.proxied
# proxied records must use automatic TTL (1)
ttl = each.value.proxied ? 1 : each.value.ttl
priority = each.value.type == "MX" ? each.value.priority : null
}
Hardened apex and delegation records that cannot be destroyed by a map edit:
resource "cloudflare_record" "apex" {
zone_id = var.zone_id
name = "@"
type = "A"
content = var.apex_ip
proxied = true
ttl = 1
lifecycle {
prevent_destroy = true
}
}
resource "cloudflare_record" "mx" {
for_each = toset(["mx1.example-mail.com", "mx2.example-mail.com"])
zone_id = var.zone_id
name = "@"
type = "MX"
content = each.value
priority = 10
proxied = false # MX must never be proxied
ttl = 3600
} Related prompts
-
Terraform GitHub Org Management Module Prompt
Manage a GitHub organization as code with repos, teams, memberships, and branch protection via for_each, importing existing repos safely.
-
Terraform Azure Naming Convention Module Prompt
Design a Terraform naming/abbreviation module for Azure that produces compliant, length-limited, globally-unique resource names with deterministic outputs.
-
Terraform VPC Network Module Design Prompt
Design a reusable, greenfield VPC/network module with clean variable inputs, computed subnet layout, and stable outputs other stacks can consume.
-
Terraform Blue Green Deployment Pattern Prompt
Implement blue/green infrastructure deployments in Terraform with parallel resource sets, create_before_destroy, and weighted DNS or target-group cutover with rollback.
More Terraform prompts & error guides
Browse every Terraform prompt and troubleshooting guide in one place.
Reading prompts? Get all 500 in one free PDF
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.