Cloud DNS Zones & Records
Public/private managed zones with record sets, DNSSEC, forwarding and peering configs.
Verification
Plan-validatedPassed: module logic verified on a mocked plan — inputs, validation rules, conditional creation and outputs resolve (no real provider, no cloud).
Conformance
- Static validation (fmt · validate · tflint)
- Security scan clean (Checkov)
- Plan tests (mocked: validation rules · outputs)
Provenance
- SHA-256 checksum
- Signature (pending)
Functional
- Live test pending (no cloud run yet)
Last verified 2026-06-28 · how we verify
Documentation
gcp-cloud-dns
A Cloud DNS managed zone with record sets — private by default
(resolvable only from the VPC networks you bind), with optional DNS
forwarding and peering for hybrid resolution, and DNSSEC signing
plus query logging turned on for public zones. Works with Terraform and
OpenTofu (>= 1.6), Google provider >= 7.0, < 8.0.
Status: static-validated, live-test pending. Ships under live-test quarantine — validated with
tofu fmt,tofu validate, andtflint. Real apply → verify → destroy against a GCP project is pending a cloud sandbox.
Secure defaults
- Private visibility by default — a zone is only resolvable from VPC networks (or GKE clusters) you explicitly bind. A plan-time precondition rejects a private zone with no network binding and no forwarding/peering, so you never ship a zone that resolves nowhere.
- DNSSEC on for public zones — signing enabled with NSEC3 authenticated denial of existence (the privacy-preserving mode that does not enumerate the zone).
- Query logging on for public zones — an audit trail of resolution.
force_destroy = false—destroywill not silently wipe a zone that still has records; you must opt in.- Forwarding vs peering are mutually exclusive, and forwarding/peering zones cannot also host record sets — both enforced as preconditions.
DNSSEC and query logging apply to public zones only and are ignored on private zones (which GCP cannot sign).
Usage
module "internal_zone" {
source = "./gcp-cloud-dns"
project_id = "my-project-123456"
name = "internal-example"
dns_name = "internal.example.com."
visibility = "private"
private_visibility_networks = [
"projects/my-project-123456/global/networks/default",
]
record_sets = {
"api/A" = {
name = "api.internal.example.com."
type = "A"
rrdatas = ["10.0.0.10"]
}
}
}
Public zone with DNSSEC + logging (defaults), delegate the parent at your
registrar to module.zone.name_servers:
module "public_zone" {
source = "./gcp-cloud-dns"
project_id = "my-project-123456"
name = "example-com"
dns_name = "example.com."
visibility = "public"
}
Hybrid forwarding to an on-prem resolver (private zone):
forwarding_targets = [
{ ipv4_address = "10.10.0.2", forwarding_path = "private" },
]
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
project_id | string | — | Host project (required) |
name | string | — | Managed zone name, DNS-1035 label (required) |
dns_name | string | — | Authoritative suffix, trailing dot, e.g. example.com. (required) |
visibility | string | private | private or public |
private_visibility_networks | list(string) | [] | VPC self-links the private zone resolves from |
private_visibility_gke_clusters | list(string) | [] | GKE cluster resource names to also bind |
dnssec_enabled | bool | true | DNSSEC signing (public zones only) |
dnssec_non_existence | string | nsec3 | nsec3 or nsec |
cloud_logging_enabled | bool | true | Query logging (public zones only) |
forwarding_targets | list(object) | [] | { ipv4_address, forwarding_path } outbound forwarders |
peering_target_network | string | null | VPC self-link to peer resolution order with |
record_sets | map(object) | {} | Keyed records: { name, type, ttl, rrdatas } |
force_destroy | bool | false | Allow destroy with records present |
labels | map(string) | {} | Zone labels |
Outputs
zone_id, zone_name, dns_name, name_servers, visibility,
dnssec_enabled, record_set_names.
Requirements
- Terraform or OpenTofu
>= 1.6 hashicorp/google>= 7.0, < 8.0
License
Commercial — LicenseRef-IaCBazaar-Commercial.