Skip to content

fix(terraform): plan breaks when cloudflare_custom_domain is unset#882

Merged
ColeMurray merged 2 commits into
mainfrom
fix/custom-domain-followups
Jul 2, 2026
Merged

fix(terraform): plan breaks when cloudflare_custom_domain is unset#882
ColeMurray merged 2 commits into
mainfrom
fix/custom-domain-followups

Conversation

@ColeMurray

@ColeMurray ColeMurray commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Follow-ups to #747 (custom domain support for the Cloudflare web Worker).

Bug: terraform plan fails for every Cloudflare-platform deployment without a custom domain

Terraform's coalesce() errors when all arguments are null or empty strings — coalesce(null, "") is a hard error, not "":

Call to function "coalesce" failed: no non-null, non-empty-string arguments.

The web_custom_domain_enabled local from #747 evaluates trimspace(coalesce(var.cloudflare_custom_domain, "")) whenever web_platform == "cloudflare" (&& short-circuits, so Vercel deployments escape). Both cloudflare_custom_domain and cloudflare_zone_id default to null, so any existing web_platform = "cloudflare" deployment that hasn't opted into a custom domain now fails at plan time. Setting cloudflare_custom_domain = "" explicitly errors the same way.

This slipped past terraform validate because validate treats variables as unknown values — the error only fires on plan/apply with concrete values.

Fix: null-safe normalization locals (var.x == null ? "" : trimspace(var.x)) consumed by the gate, the host, and the cloudflare_workers_custom_domain resource. This also fixes the inconsistency where the gate trimmed the domain but the resource/URL consumed the raw value.

Hard validation instead of an advisory warning

The check block from #747 had the same coalesce problem (it would report an evaluation error rather than its intended message), and as a warning it let a misconfigured domain silently fall back to workers.dev — leaving NEXTAUTH_URL and OAuth callbacks pointing somewhere the operator didn't intend. Replaced with two hard failures:

  • a validation block on cloudflare_custom_domain for hostname shape: must be a bare hostname — rejects scheme, port, path, trailing dot, whitespace, wildcards
  • a terraform_data.cloudflare_custom_domain_gate precondition in checks.tf for the cross-input policy (same pattern as the existing access_control_gate), expressed against the normalized locals as local.web_custom_domain == "" || local.web_custom_domain_enabled: a set domain requires web_platform = "cloudflare" and a non-empty cloudflare_zone_id (also catches the previously-silent domain-set-on-Vercel case)

null, "", and whitespace-only all still mean "not configured".

Single canonical origin

Attaching a custom domain doesn't disable the workers.dev route, so the app was reachable on two origins with NEXTAUTH_URL pointing at only one. The generated wrangler.production.toml now sets workers_dev = false when the custom domain is enabled (true otherwise — existing behavior, now explicit).

Note for deployers already running #747 with a custom domain: after the next apply, the workers.dev URL for the web app stops serving; the custom domain is the single origin.

Docs

  • GETTING_STARTED.md: custom-domain subsection under Step 8 (vars, token permission, OAuth-callback reminder), custom-domain entries in the callback-URL list and redirect-URI troubleshooting, commented vars in the tfvars sample.
  • terraform.tfvars.example: notes that workers.dev is disabled and GitHub/Google OAuth callback URLs must be updated to the new hostname.

Verification

terraform fmt -check and terraform validate pass. Validation and locals exercised end-to-end with terraform plan on a minimal harness under Terraform 1.14.6 (the repo's required floor):

Scenario Result
all unset (defaults) plans cleanly, enabled = false — previously the coalesce error
domain set, web_platform = "vercel" hard error (precondition gate)
domain set, zone id unset hard error (precondition gate)
domain + zone, web_platform = "cloudflare" enabled = true, host = custom domain
https://app.example.com hard error (bare-hostname rule)
domain "" plans cleanly, enabled = false

Summary by CodeRabbit

  • New Features

    • Added support for serving the web app on a Cloudflare custom domain (with the default workers.dev route disabled when enabled).
    • Expanded setup docs with Cloudflare custom-domain DNS, certificate, and OAuth callback guidance.
  • Bug Fixes

    • Improved reliability of custom-domain configuration by treating null/empty/whitespace as unset and validating against a bare hostname format.
    • Added a stricter preflight check that blocks misconfigured plans when the custom domain requires missing Cloudflare zone settings.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 39fa07dc-3726-469f-a0e3-0b6e1f0ada57

📥 Commits

Reviewing files that changed from the base of the PR and between 93c0a3e and d908b8d.

📒 Files selected for processing (2)
  • terraform/environments/production/checks.tf
  • terraform/environments/production/variables.tf

📝 Walkthrough

Walkthrough

This PR updates Cloudflare custom-domain support in the production Terraform environment by tightening input validation, normalizing custom-domain values, enforcing plan-time gating, rewiring Cloudflare worker configuration, and expanding setup and troubleshooting documentation.

Changes

Cloudflare Custom Domain Support

Layer / File(s) Summary
Variable validation rules for cloudflare_custom_domain
terraform/environments/production/variables.tf
Validation for cloudflare_custom_domain now allows null and whitespace-only values, and non-empty values must match a bare-hostname pattern.
Locals normalization and plan gate
terraform/environments/production/locals.tf, terraform/environments/production/checks.tf
Normalized custom-domain and zone-id locals are introduced, web_custom_domain_enabled and web_cloudflare_host use them, and the previous advisory check is replaced with a hard precondition gate.
Cloudflare web app wiring
terraform/environments/production/web-cloudflare.tf
workers_dev is conditionally disabled when a custom domain is enabled, and the custom-domain resource now uses the normalized locals for zone ID and hostname.
Documentation and example updates
docs/GETTING_STARTED.md, terraform/environments/production/terraform.tfvars.example
Setup guidance adds the Cloudflare custom-domain callback URL, optional custom-domain instructions, troubleshooting updates, and expanded tfvars notes.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the primary fix: preventing Terraform plan failures when cloudflare_custom_domain is unset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/custom-domain-followups

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@open-inspect open-inspect Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

PR #882, fix(terraform): plan breaks when cloudflare_custom_domain is unset, by ColeMurray. Reviewed 6 files (+63/-19); the change replaces null-unsafe custom-domain handling with normalized locals, hard validation, canonical workers.dev behavior, and matching docs updates.

Critical Issues

None found.

Suggestions

None blocking. A future hardening improvement could add hostname length validation (per-label and total length), but the current bare-hostname check already prevents the important malformed inputs called out in the PR.

Positive Feedback

The normalized locals remove the Terraform coalesce edge case cleanly and avoid using trimmed values inconsistently across the gate, URL, and custom-domain resource.
Hard validation is a good improvement over the previous advisory check because it prevents silently deploying with OAuth/NEXTAUTH_URL pointed at an unintended origin.
Disabling workers.dev when a custom domain is attached makes the canonical-origin behavior explicit and the docs clearly call out the deployer-facing impact.

Questions

None.

Verdict

Approve. I reviewed the full diff and did not find correctness, security, or maintainability issues that should block merging. I could not run Terraform validation locally because this checkout is on main with unrelated dirty files rather than the PR contents, so this review is based on gh pr diff 882 and surrounding repository context.

Comment thread terraform/environments/production/variables.tf Outdated
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@ColeMurray ColeMurray merged commit 3395133 into main Jul 2, 2026
17 of 18 checks passed
@ColeMurray ColeMurray deleted the fix/custom-domain-followups branch July 2, 2026 06:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant