DEV Community

Cover image for DNS Validation: From 15 Steps to Zero
Glenn Gray
Glenn Gray

Posted on • Originally published at graycloudarch.com

DNS Validation: From 15 Steps to Zero

Originally published on graycloudarch.com.


You know what's the worst part of launching a new site?

SSL certificate validation.

Not creating the cert—that's one click in AWS ACM. It's the validation dance:

  1. AWS gives you a CNAME record: _abc123extremely-long-string-here.graycloudarch.com
  2. The value is equally ridiculous: _xyz789another-massive-string.acm-validations.aws.
  3. You copy it (pray you don't miss a character)
  4. Switch to Cloudflare (or Route 53, or wherever)
  5. Paste it in
  6. Wait 5-10 minutes
  7. Refresh AWS console
  8. Still pending...
  9. Refresh again
  10. Finally validated!

Now do it again for www.graycloudarch.com.

And then repeat the whole thing for your second domain.

This is "DNS hell."

There's a Better Way

Terraform can read AWS validation records and create them in Cloudflare automatically.

Zero copy-paste. Zero browser tab switching. Zero waiting and refreshing.

Here's the whole thing:

# Request certificate
resource "aws_acm_certificate" "site" {
  domain_name       = "graycloudarch.com"
  validation_method = "DNS"
  subject_alternative_names = ["www.graycloudarch.com"]
}

# Create validation records in Cloudflare
resource "cloudflare_record" "cert_validation" {
  for_each = {
    for dvo in aws_acm_certificate.site.domain_validation_options :
    dvo.domain_name => {
      name   = dvo.resource_record_name
      value  = dvo.resource_record_value
      type   = dvo.resource_record_type
    }
  }

  zone_id = data.cloudflare_zone.site.id
  name    = each.value.name
  value   = each.value.value
  type    = each.value.type
  proxied = false  # Critical - ACM validation breaks with proxy
}

# Wait for validation
resource "aws_acm_certificate_validation" "site" {
  certificate_arn = aws_acm_certificate.site.arn
  validation_record_fqdns = [
    for record in cloudflare_record.cert_validation : record.hostname
  ]
}
Enter fullscreen mode Exit fullscreen mode

Run terraform apply. Go make coffee. Come back to a validated certificate.

The Magic: for_each

The key is this part:

for_each = {
  for dvo in aws_acm_certificate.site.domain_validation_options :
  dvo.domain_name => { name = dvo.resource_record_name, ... }
}
Enter fullscreen mode Exit fullscreen mode

AWS generates validation records dynamically (one for apex domain, one for www). Terraform reads them, loops over them, and creates each one in Cloudflare.

You never see the records. You never copy anything. It just works.

What I Screwed Up

First time I ran this, ACM validation timed out after 30 minutes.

The problem:

proxied = true  # Wrong!
Enter fullscreen mode Exit fullscreen mode

Cloudflare's proxy rewrites DNS responses. ACM's validation servers hit Cloudflare's IP instead of seeing your validation record.

The fix:

proxied = false  # Correct
Enter fullscreen mode Exit fullscreen mode

DNS-only mode. No proxy. ACM validation works.

Cost me 30 minutes of debugging. Now it's in code so I never hit it again.

Why This Matters

I'm running two brands: graycloudarch.com and cloudpatterns.io.

Manual approach: 15 steps per domain = 30 steps total. 30 minutes minimum. High chance of typos.

Terraform approach: One terraform apply. 5 minutes to write the code (once), 10 minutes for AWS to validate. Then copy-paste the pattern for the second domain.

When I launch my third brand (and I will), it'll take 5 minutes and one terraform apply.

That's the bet: upfront automation for long-term velocity.

The Part People Miss

Most Terraform tutorials stop at requesting the certificate. They don't show you the validation loop or the waiting resource.

Without aws_acm_certificate_validation, Terraform exits immediately after creating the cert. It's still "Pending Validation" in AWS. When you try to use it in CloudFront, it fails.

You'd have to run terraform apply again later, after manually checking that validation completed.

That's not automation—that's just documentation.

The waiting resource makes it truly hands-off.

Scaling It

Adding a second domain is 10 lines of code:

resource "aws_acm_certificate" "cloudpatterns" {
  domain_name       = "cloudpatterns.io"
  validation_method = "DNS"
  subject_alternative_names = ["www.cloudpatterns.io"]
}

resource "cloudflare_record" "cloudpatterns_validation" {
  for_each = { /* same pattern */ }
  # ...
}

resource "aws_acm_certificate_validation" "cloudpatterns" {
  # ...
}
Enter fullscreen mode Exit fullscreen mode

Same pattern, different names. No clicking. No switching between consoles. No remembering which validation record goes where.

The Real Win

It's not the time savings (though 30 minutes per deployment adds up).

It's the mental overhead.

Manual DNS configuration requires focus. "Did I copy the whole string? Did I add the trailing dot? Is it DNS-only mode?"

Terraform requires running one command. That's it.

I get my focus back. I can write this blog post while Terraform validates certificates.

Want the full code? It's not open source (yet), but if you're building something similar and want to talk through it, reach out.

Or if you just want to tell me I'm overthinking this and should've clicked through Cloudflare like a normal person, that's cool too.

Top comments (0)