🚀 Executive Summary

TL;DR: Terraform often attempts to delete and recreate Cloudflare DNS records due to ‘state drift,’ where manual changes in the Cloudflare UI diverge from Terraform’s `tfstate` file. The most professional and safe solution involves using the `terraform import` command to reconcile Terraform’s state with the existing Cloudflare configuration without causing downtime.

🎯 Key Takeaways

  • State drift occurs when manual modifications to Cloudflare DNS records cause a mismatch between the live configuration and Terraform’s `terraform.tfstate` file, leading Terraform to propose destructive changes.
  • The `terraform import` command is the recommended, idempotent method to align Terraform’s state with an existing Cloudflare DNS record, requiring the `resource_address`, `cloudflare_zone_id`, and `cloudflare_record_id`.
  • While manual state file editing offers a quick fix, it is highly risky and can corrupt the `tfstate` file; the ‘delete and recreate’ method guarantees a clean slate but causes unacceptable downtime for production systems.

Cloudflare automation DNS name edit at each run

Tired of Terraform constantly trying to delete and recreate your Cloudflare DNS records on every run? Understand why state drift happens and learn three practical solutions—from a quick fix to the proper, permanent solution—to regain control of your infrastructure.

Stop Fighting Terraform: How to Fix the “Cloudflare DNS Record Replaced on Every Run” Nightmare

I remember it like it was yesterday. It was 2 AM, and we were pushing a “minor” update. The `terraform plan` looked clean, just adding a new CNAME. I typed `apply`, hit enter, and watched in horror as the output showed `cloudflare_record.prod_db must be replaced`. My blood ran cold. The plan had lied. Some well-meaning engineer had manually updated the IP for `prod-db-01.techresolve.com` directly in the Cloudflare UI during an incident last week and, of course, forgot to update the code. For a terrifying minute, our entire production database was unresolvable because Terraform decided its version of reality was the only one that mattered. We’ve all been there, fighting the tool that’s supposed to make our lives easier.

So, Why Does This Keep Happening? The Ghost in the State File

This isn’t a bug; it’s a feature—a very annoying one if you don’t understand it. The root of this problem is something called state drift. Terraform maintains a `terraform.tfstate` file, which is its sacred source of truth. It’s a detailed JSON map of every resource it’s supposed to be managing.

When you run `terraform apply`, Terraform does three things:

  1. It reads your code (your desired state).
  2. It checks the real world (it asks Cloudflare’s API about the current DNS record).
  3. It compares both to its state file (its recorded state).

If someone manually changes a DNS record in the Cloudflare dashboard, the “real world” no longer matches the `tfstate` file. From Terraform’s perspective, the resource has been corrupted and must be “fixed” by destroying the existing one and creating a new one that matches its records. It has no idea you wanted that manual change; it only knows that reality has drifted from its memory.

The Three Fixes: From “Dirty Hack” to “Best Practice”

Alright, you’re stuck in this loop. Your pipeline is failing, and you need to get things stable again. Here are three ways to solve it, each with its own trade-offs.

Solution 1: The “Get Me Home for Dinner” Fix (Manual State Edit)

This is the quick and dirty approach. You’re essentially performing surgery on Terraform’s brain to make it accept the new reality. I do not recommend this for critical production systems, but sometimes you just need to unblock a pipeline fast.

First, you pull the current state down to a local file:

terraform state pull > terraform.tfstate

Next, open that `terraform.tfstate` file in an editor. Find the `cloudflare_record` resource that’s causing the problem. You’ll see the old IP address or CNAME value in there. Carefully change that value to match what’s currently live in Cloudflare. Save the file.

Finally, you push the modified state file back up:

terraform state push terraform.tfstate

Run `terraform plan` again. If you did it right, it should now report “No changes. Your infrastructure matches the configuration.”

Warning: Manually editing the state file is like editing the registry in Windows. One misplaced comma or bracket can corrupt your entire state, leading to a much bigger headache. Always make a backup before you try this.

Solution 2: The “Do It Right” Fix (Terraform Import)

This is the correct, idempotent, and safe way to fix the problem. You’re telling Terraform, “Hey, see that existing DNS record in Cloudflare? I want you to start managing it. Please update your state file to match it.”

The `terraform import` command is your best friend here. The syntax for a Cloudflare record looks like this:

terraform import [resource_address] [cloudflare_zone_id]/[cloudflare_record_id]

Let’s break that down. Imagine your code looks like this:

resource "cloudflare_record" "prod_db" {
  zone_id = var.cloudflare_zone_id
  name    = "prod-db-01"
  value   = "192.0.2.123" # The new IP that was manually set
  type    = "A"
  ttl     = 3600
}

Your `resource_address` is `cloudflare_record.prod_db`. You can find the `zone_id` and the specific `record_id` in the Cloudflare dashboard URL or by using their API. Once you have them, you run the command:

# Example IDs
ZONE_ID="a1b2c3d4e5f6a1b2c3d4e5f6"
RECORD_ID="z9y8x7w6v5u4z9y8x7w6v5u4"

terraform import cloudflare_record.prod_db ${ZONE_ID}/${RECORD_ID}

Terraform will connect to Cloudflare, read the configuration of that exact record, and write it into your `tfstate` file, perfectly aligning your state with reality. A subsequent `terraform plan` will show no changes needed.

Pro Tip: This is the only method you should be using in a professional CI/CD environment. It’s safe, repeatable, and doesn’t involve manually editing sensitive state files.

Solution 3: The “Scorched Earth” Fix (Delete and Recreate)

I hesitate to even call this a solution, but it’s a path people sometimes take out of frustration. This involves telling Terraform to forget the resource, deleting it manually, and then letting Terraform create it from scratch.

USE THIS WITH EXTREME CAUTION. THIS WILL CAUSE DOWNTIME.

The steps are:

  1. Tell Terraform to “forget” about the resource in its state: `terraform state rm cloudflare_record.prod_db`
  2. Go into the Cloudflare UI and manually delete the DNS record. Yes, delete it. Your service is now down.
  3. Run `terraform apply`. Terraform will see the resource is missing and create it fresh based on your code.

This is a terrible idea for anything in production due to the obvious downtime, but for a non-critical dev environment or a personal project, it’s a way to get back to a clean slate without fussing with IDs.

Choosing Your Path

Here’s a quick cheat sheet to help you decide:

Method Pros Cons
Manual State Edit Very fast for a simple value change. Extremely risky; can corrupt state. Not auditable.
Terraform Import Safe, official, idempotent. The professional standard. Requires finding Zone/Record IDs, which can be a minor hassle.
Delete and Recreate Guarantees a clean slate. Conceptually simple. Causes downtime. Not suitable for 99% of use cases.

At the end of the day, state drift is a sign of a breakdown in process. Your infrastructure-as-code repository should be the single source of truth. If people are making manual changes, you need to address the “why” behind it. But when you find yourself in that 2 AM firefight, knowing how to use `terraform import` will save your bacon and let you get back to bed.

Darian Vance - Lead Cloud Architect

Darian Vance

Lead Cloud Architect & DevOps Strategist

With over 12 years in system architecture and automation, Darian specializes in simplifying complex cloud infrastructures. An advocate for open-source solutions, he founded TechResolve to provide engineers with actionable, battle-tested troubleshooting guides and robust software alternatives.


🤖 Frequently Asked Questions

âť“ Why does Terraform repeatedly try to replace my Cloudflare DNS records during `terraform apply`?

This issue, known as ‘state drift,’ occurs when manual changes are made directly in the Cloudflare dashboard, causing the actual DNS record to no longer match the configuration recorded in Terraform’s `terraform.tfstate` file. Terraform then attempts to ‘fix’ this by destroying and recreating the resource to match its internal state.

âť“ How do the different methods for fixing Cloudflare DNS state drift in Terraform compare?

The ‘Manual State Edit’ is a fast but highly risky method that can corrupt your state. The ‘Terraform Import’ command is the professional, safe, and idempotent solution, aligning Terraform’s state with the live resource. The ‘Delete and Recreate’ method guarantees a clean slate but causes significant downtime and is generally unsuitable for production.

âť“ What is a common pitfall when attempting to resolve Terraform state drift for Cloudflare DNS records?

A common and dangerous pitfall is manually editing the `terraform.tfstate` file. This approach is prone to syntax errors, can easily corrupt the entire state, and lacks auditability, potentially leading to more severe infrastructure management problems.

Leave a Reply

Discover more from TechResolve - SaaS Troubleshooting & Software Alternatives

Subscribe now to keep reading and get access to the full archive.

Continue reading