DNS privado híbrido (on-premises ↔ nube)
On-premises resuelve las zonas privadas de la nube (Private Endpoints incluidos) y la nube resuelve el dominio corporativo, sin servidores DNS propios en la nube.
Problema
Con un enlace VPN o dedicado ya hay conectividad IP, pero los nombres no cruzan: on-premises no puede consultar el resolver interno de la nube y los workloads de la nube no conocen corp.example.com. La solución clásica son VMs con DNS en la nube, que hay que parchear y escalar. Los requisitos son: reenvío condicional en las dos direcciones, alta disponibilidad, que los spokes resuelvan sin configuración propia y sin loops de reenvío.
La topología es la misma en todas las nubes; lo que cambia es con qué se implementa cada rol.
Entrada (on-prem → nube)
Azure
DNS Private Resolver: inbound endpoint
IP estática en una subnet /28 dedicada y delegada
Venís de AWS: allá este rol es Route 53 Resolver inbound endpoint.
Venís de GCP: allá este rol es Cloud DNS inbound server policy.
3 gotchas en este rol
GCP
Cloud DNS inbound server policy
enable_inbound_forwarding: una IP de entrada por subnet de la VPC
Venís de Azure: allá este rol es DNS Private Resolver: inbound endpoint. No hay endpoints ni subnets dedicadas: la policy toma una IP de cada subnet de la VPC.
Venís de AWS: allá este rol es Route 53 Resolver inbound endpoint. No hay endpoints que dimensionar ni ENIs por hora: la policy toma una IP de cada subnet de la VPC.
1 gotcha en este rol
Zona privada de la nube
Azure
Azure Private DNS
Zona vinculada al hub; los spokes la resuelven por una regla al inbound endpoint
Venís de AWS: allá este rol es Route 53 private hosted zone. La zona vinculada a la VNet se consulta antes que el ruleset; en Route 53 una regla de forward le gana a la zona del mismo nombre.
Venís de GCP: allá este rol es Cloud DNS private zone.
1 gotcha en este rol
GCP
Cloud DNS private zone
Visible para la VPC hub
Venís de Azure: allá este rol es Azure Private DNS.
Venís de AWS: allá este rol es Route 53 private hosted zone.
1 gotcha en este rol
Salida (nube → on-prem)
Azure
DNS Private Resolver: outbound endpoint + forwarding ruleset
Ruleset linkeado a los spokes (no al hub)
Venís de AWS: allá este rol es Route 53 Resolver outbound endpoint + regla FORWARD.
Venís de GCP: allá este rol es Cloud DNS forwarding zone.
5 gotchas en este rol
- Cada endpoint necesita su propia subnet delegada, y el resolver queda atado a una VNet y una región
- En Azure la zona privada vinculada gana sobre el ruleset; en AWS es al revés
- Linkear el ruleset al hub, con una regla que apunta a su propio inbound endpoint, arma un loop
- Una regla comodín "." tiene que resolver nombres públicos, y 168.63.129.16 no sirve como destino
- DNS Private Resolver no es compatible con Azure Lighthouse ni con ExpressRoute FastPath
GCP
Cloud DNS forwarding zone
forwarding_path = private + Cloud Router anunciando 35.199.192.0/19
Venís de Azure: allá este rol es DNS Private Resolver: outbound endpoint + forwarding ruleset. Las consultas reenviadas salen desde 35.199.192.0/19, no desde IPs de tu VPC: on-premises tiene que permitir ese rango y tener la ruta de vuelta.
Venís de AWS: allá este rol es Route 53 Resolver outbound endpoint + regla FORWARD. Las consultas reenviadas salen desde 35.199.192.0/19, no desde IPs de tu VPC: on-premises tiene que permitir ese rango y tener la ruta de vuelta.
2 gotchas en este rol
Microsoft Azure
Implementación
- Entrada (on-prem → nube)
- DNS Private Resolver: inbound endpointIP estática en una subnet /28 dedicada y delegadaVenís de AWS: allá este rol es Route 53 Resolver inbound endpoint.Venís de GCP: allá este rol es Cloud DNS inbound server policy.
- Zona privada de la nube
- Azure Private DNSZona vinculada al hub; los spokes la resuelven por una regla al inbound endpointVenís de AWS: allá este rol es Route 53 private hosted zone. La zona vinculada a la VNet se consulta antes que el ruleset; en Route 53 una regla de forward le gana a la zona del mismo nombre.Venís de GCP: allá este rol es Cloud DNS private zone.
- Salida (nube → on-prem)
- DNS Private Resolver: outbound endpoint + forwarding rulesetRuleset linkeado a los spokes (no al hub)Venís de AWS: allá este rol es Route 53 Resolver outbound endpoint + regla FORWARD.Venís de GCP: allá este rol es Cloud DNS forwarding zone.
Modelo de costo
No escala a cero
Costo fijo aunque no haya tráfico:
- Inbound endpoint (por hora)
- Outbound endpoint (por hora)
- Forwarding ruleset (por mes)
Los endpoints y el ruleset se cobran aunque no haya consultas, más un cargo por consulta. Sigue siendo más barato que mantener dos VMs de DNS con alta disponibilidad.
Piso mensual sin tráfico
USD 363/ mes
Con la configuración por default del módulo Terraform. Sirve para comparar órdenes de magnitud, no para presupuestar.
- DNS Private Resolver — inbound endpoint × 1 · 180 USD/mesUSD 180
- DNS Private Resolver — outbound endpoint × 1 · 180 USD/mesUSD 180
- DNS Private Resolver — forwarding ruleset × 1 · 2.5 USD/mesUSD 2,50
- Zona DNS privada × 1 · 0.5 USD/mesUSD 0,50
Precio de lista en eastus al 2026-10-24, en USD, sin descuentos ni impuestos.
Letra chica · 5 gotchas
Cada endpoint necesita su propia subnet delegada, y el resolver queda atado a una VNet y una región
Inbound y outbound no pueden compartir subnet: cada uno va en una dedicada de /28 a /24, delegada a Microsoft.Network/dnsResolvers y sin ningún otro recurso. Hay un solo resolver por VNet, los rulesets solo se linkean a VNets de la misma región y no funciona con subnets IPv6 ni con VNets que tienen virtual network encryption. Reservá las dos /28 en el diseño del hub antes de que el espacio se llene.
En Azure la zona privada vinculada gana sobre el ruleset; en AWS es al revés
Con el DNS de Azure en la VNet, primero se consultan las Private DNS zones vinculadas y recién si no hay match se evalúan las reglas del ruleset (gana el sufijo más largo). En Route 53 Resolver, una regla de forward le gana a una private hosted zone con el mismo nombre. Si migrás un diseño de "reenviar todo corp.example.com a on-prem" con una zona privada del mismo nombre, el comportamiento se invierte.
Linkear el ruleset al hub, con una regla que apunta a su propio inbound endpoint, arma un loop
El diseño típico manda la zona privada desde los spokes al inbound endpoint del hub. Si ese mismo ruleset también se linkea a la VNet del hub, las consultas del hub vuelven al hub y se arma un loop. Por eso el módulo linkea solo spokes. Si los workloads del hub necesitan resolver corp.example.com, usá un segundo ruleset sin la regla al inbound endpoint.
Una regla comodín "." tiene que resolver nombres públicos, y 168.63.129.16 no sirve como destino
Si reenviás todo con ".", el DNS destino tiene que resolver nombres públicos: varios servicios de Azure dependen de eso. Algunos dominios reservados de Azure se ignoran igual en la regla comodín. Además, 168.63.129.16 no se acepta como destino de una regla, y Microsoft desaconseja el comodín en una VNet con un gateway de ExpressRoute porque puede romper la conectividad de gestión.
DNS Private Resolver no es compatible con Azure Lighthouse ni con ExpressRoute FastPath
Si un partner administra el tenant del cliente con Lighthouse, no puede operar el resolver por esa vía: hace falta acceso directo en el tenant. Tampoco soporta FastPath de ExpressRoute ni linkear rulesets entre tenants. Para un MSP es una decisión de diseño de día uno, no un detalle de implementación.
Terraform
main.tf
# DNS privado híbrido — Azure
#
# DNS Private Resolver en el hub: inbound endpoint para on-premises, outbound
# endpoint + forwarding ruleset para corp.example.com, y una regla que manda la
# zona privada al inbound endpoint para que los spokes la resuelvan sin links.
locals {
# Las reglas usan nombres terminados en punto.
onprem_fqdn = "${trimsuffix(var.onprem_domain, ".")}."
zone_fqdn = "${trimsuffix(var.private_zone_name, ".")}."
}
resource "azurerm_resource_group" "this" {
name = "rg-${var.name}"
location = var.location
tags = var.tags
}
resource "azurerm_virtual_network" "hub" {
name = "vnet-${var.name}"
resource_group_name = azurerm_resource_group.this.name
location = azurerm_resource_group.this.location
address_space = [var.hub_address_space]
tags = var.tags
}
# Una subnet por endpoint, delegada y sin otros recursos (gotcha: resolver-dedicated-subnets).
resource "azurerm_subnet" "inbound" {
name = "snet-dns-inbound"
resource_group_name = azurerm_resource_group.this.name
virtual_network_name = azurerm_virtual_network.hub.name
address_prefixes = [var.inbound_subnet_prefix]
delegation {
name = "dns-resolver"
service_delegation {
name = "Microsoft.Network/dnsResolvers"
actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"]
}
}
}
resource "azurerm_subnet" "outbound" {
name = "snet-dns-outbound"
resource_group_name = azurerm_resource_group.this.name
virtual_network_name = azurerm_virtual_network.hub.name
address_prefixes = [var.outbound_subnet_prefix]
delegation {
name = "dns-resolver"
service_delegation {
name = "Microsoft.Network/dnsResolvers"
actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"]
}
}
}
resource "azurerm_network_security_group" "resolver" {
name = "nsg-${var.name}-dns"
resource_group_name = azurerm_resource_group.this.name
location = azurerm_resource_group.this.location
tags = var.tags
# UDP y TCP: las respuestas grandes caen a TCP.
security_rule {
name = "dns-from-onprem"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "*"
source_address_prefixes = var.onprem_cidrs
source_port_range = "*"
destination_address_prefix = var.inbound_subnet_prefix
destination_port_range = "53"
}
}
resource "azurerm_subnet_network_security_group_association" "inbound" {
subnet_id = azurerm_subnet.inbound.id
network_security_group_id = azurerm_network_security_group.resolver.id
}
resource "azurerm_subnet_network_security_group_association" "outbound" {
subnet_id = azurerm_subnet.outbound.id
network_security_group_id = azurerm_network_security_group.resolver.id
}
resource "azurerm_private_dns_resolver" "this" {
name = "dnspr-${var.name}"
resource_group_name = azurerm_resource_group.this.name
location = azurerm_resource_group.this.location
virtual_network_id = azurerm_virtual_network.hub.id
tags = var.tags
}
# IP estática: es la que se carga en los forwarders condicionales on-premises.
resource "azurerm_private_dns_resolver_inbound_endpoint" "this" {
name = "in-${var.name}"
private_dns_resolver_id = azurerm_private_dns_resolver.this.id
location = azurerm_private_dns_resolver.this.location
tags = var.tags
ip_configurations {
subnet_id = azurerm_subnet.inbound.id
private_ip_allocation_method = "Static"
private_ip_address = cidrhost(var.inbound_subnet_prefix, 4)
}
}
resource "azurerm_private_dns_resolver_outbound_endpoint" "this" {
name = "out-${var.name}"
private_dns_resolver_id = azurerm_private_dns_resolver.this.id
location = azurerm_private_dns_resolver.this.location
subnet_id = azurerm_subnet.outbound.id
tags = var.tags
}
# --- Zona privada (vinculada solo al hub; el inbound endpoint la resuelve) ------
resource "azurerm_private_dns_zone" "cloud" {
name = var.private_zone_name
resource_group_name = azurerm_resource_group.this.name
tags = var.tags
}
resource "azurerm_private_dns_zone_virtual_network_link" "hub" {
name = "hub"
private_dns_zone_id = azurerm_private_dns_zone.cloud.id
virtual_network_id = azurerm_virtual_network.hub.id
registration_enabled = false
tags = var.tags
}
# --- Reglas de reenvío -----------------------------------------------------------
resource "azurerm_private_dns_resolver_dns_forwarding_ruleset" "this" {
name = "dnsfrs-${var.name}"
resource_group_name = azurerm_resource_group.this.name
location = azurerm_resource_group.this.location
private_dns_resolver_outbound_endpoint_ids = [azurerm_private_dns_resolver_outbound_endpoint.this.id]
tags = var.tags
}
resource "azurerm_private_dns_resolver_forwarding_rule" "onprem" {
name = "onprem"
dns_forwarding_ruleset_id = azurerm_private_dns_resolver_dns_forwarding_ruleset.this.id
domain_name = local.onprem_fqdn
enabled = true
dynamic "target_dns_servers" {
for_each = var.onprem_dns_servers
content {
ip_address = target_dns_servers.value
port = 53
}
}
}
# Los spokes resuelven la zona privada vía el inbound endpoint del hub.
resource "azurerm_private_dns_resolver_forwarding_rule" "cloud_zone" {
name = "cloud-zone"
dns_forwarding_ruleset_id = azurerm_private_dns_resolver_dns_forwarding_ruleset.this.id
domain_name = local.zone_fqdn
enabled = true
target_dns_servers {
ip_address = azurerm_private_dns_resolver_inbound_endpoint.this.ip_configurations[0].private_ip_address
port = 53
}
}
# Solo spokes: linkear el hub, con una regla que apunta a su propio inbound
# endpoint, genera un loop (gotcha: ruleset-hub-loop).
resource "azurerm_private_dns_resolver_virtual_network_link" "spoke" {
for_each = var.spoke_vnet_ids
name = "spoke-${each.key}"
dns_forwarding_ruleset_id = azurerm_private_dns_resolver_dns_forwarding_ruleset.this.id
virtual_network_id = each.value
}variables.tf
variable "name" {
description = "Prefijo para nombrar los recursos (ej. \"dns-hub\")."
type = string
validation {
condition = can(regex("^[a-z0-9-]{3,20}$", var.name))
error_message = "name: 3-20 caracteres, minúsculas, números o guiones."
}
}
variable "location" {
description = "Región del hub. El resolver y los ruleset links solo funcionan con VNets de la misma región."
type = string
default = "eastus2"
}
variable "hub_address_space" {
description = "Espacio de direcciones de la VNet hub."
type = string
default = "10.10.0.0/16"
}
variable "inbound_subnet_prefix" {
description = "Subnet dedicada al inbound endpoint (/28 a /24)."
type = string
default = "10.10.0.0/28"
validation {
condition = tonumber(split("/", var.inbound_subnet_prefix)[1]) >= 24 && tonumber(split("/", var.inbound_subnet_prefix)[1]) <= 28
error_message = "inbound_subnet_prefix: entre /28 y /24 (gotcha: resolver-dedicated-subnets)."
}
}
variable "outbound_subnet_prefix" {
description = "Subnet dedicada al outbound endpoint (/28 a /24)."
type = string
default = "10.10.0.16/28"
validation {
condition = tonumber(split("/", var.outbound_subnet_prefix)[1]) >= 24 && tonumber(split("/", var.outbound_subnet_prefix)[1]) <= 28
error_message = "outbound_subnet_prefix: entre /28 y /24 (gotcha: resolver-dedicated-subnets)."
}
}
variable "private_zone_name" {
description = "Zona privada de Azure que on-premises tiene que resolver (puede ser una privatelink.*)."
type = string
default = "azure.example.internal"
}
variable "onprem_domain" {
description = "Dominio corporativo que se reenvía a on-premises."
type = string
default = "corp.example.com"
}
variable "onprem_dns_servers" {
description = "IPs de los DNS on-premises (alcanzables por VPN/ExpressRoute)."
type = list(string)
validation {
condition = length(var.onprem_dns_servers) >= 1 && length(var.onprem_dns_servers) <= 6 && !contains(var.onprem_dns_servers, "168.63.129.16")
error_message = "onprem_dns_servers: 1 a 6 IPs, y 168.63.129.16 no se acepta como destino de una regla."
}
}
variable "onprem_cidrs" {
description = "Rangos on-premises que consultan el inbound endpoint."
type = list(string)
}
variable "spoke_vnet_ids" {
description = "VNets spoke (misma región) que usan el ruleset. No incluyas el hub: el ruleset reenvía la zona privada al inbound endpoint del hub."
type = map(string)
default = {}
}
variable "tags" {
description = "Tags comunes."
type = map(string)
default = {}
}outputs.tf
output "inbound_endpoint_ip" {
description = "IP para los forwarders condicionales on-premises (zona privada → esta IP)."
value = azurerm_private_dns_resolver_inbound_endpoint.this.ip_configurations[0].private_ip_address
}
output "hub_vnet_id" {
description = "VNet hub (a peerear con los spokes y conectar a on-premises)."
value = azurerm_virtual_network.hub.id
}
output "ruleset_id" {
description = "Forwarding ruleset para linkear más spokes de la misma región."
value = azurerm_private_dns_resolver_dns_forwarding_ruleset.this.id
}versions.tf
terraform {
required_version = ">= 1.7.0"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 5.8"
}
}
}
provider "azurerm" {
features {}
# subscription_id se toma de ARM_SUBSCRIPTION_ID.
}Google Cloud
Implementación
- Entrada (on-prem → nube)
- Cloud DNS inbound server policyenable_inbound_forwarding: una IP de entrada por subnet de la VPCVenís de Azure: allá este rol es DNS Private Resolver: inbound endpoint. No hay endpoints ni subnets dedicadas: la policy toma una IP de cada subnet de la VPC.Venís de AWS: allá este rol es Route 53 Resolver inbound endpoint. No hay endpoints que dimensionar ni ENIs por hora: la policy toma una IP de cada subnet de la VPC.
- Zona privada de la nube
- Cloud DNS private zoneVisible para la VPC hubVenís de Azure: allá este rol es Azure Private DNS.Venís de AWS: allá este rol es Route 53 private hosted zone.
- Salida (nube → on-prem)
- Cloud DNS forwarding zoneforwarding_path = private + Cloud Router anunciando 35.199.192.0/19Venís de Azure: allá este rol es DNS Private Resolver: outbound endpoint + forwarding ruleset. Las consultas reenviadas salen desde 35.199.192.0/19, no desde IPs de tu VPC: on-premises tiene que permitir ese rango y tener la ruta de vuelta.Venís de AWS: allá este rol es Route 53 Resolver outbound endpoint + regla FORWARD. Las consultas reenviadas salen desde 35.199.192.0/19, no desde IPs de tu VPC: on-premises tiene que permitir ese rango y tener la ruta de vuelta.
Modelo de costo
Escala a cero parcial
Costo fijo aunque no haya tráfico:
- Zonas administradas (privada y de forwarding), por mes
No hay cargo por hora de endpoints. Pagás cada zona por mes, se use o no, y las consultas por millón. El Cloud Router y la VPN se pagan aparte y suelen existir ya por la conectividad.
Piso mensual sin tráfico
USD 0,40/ mes
Con la configuración por default del módulo Terraform. Sirve para comparar órdenes de magnitud, no para presupuestar.
- Zona administrada de Cloud DNS (privada o de forwarding) × 2 · 0.2 USD/mesUSD 0,40
Precio de lista en us-east1 al 2026-10-06, en USD, sin descuentos ni impuestos.
Algún precio se cargó a mano desde la página oficial: la API de esa nube pide key.
Letra chica · 4 gotchas
Con routing estándar, un destino que no es RFC 1918 se consulta por internet
El default de una forwarding zone decide la ruta según la IP del destino: RFC 1918 va por la VPC, cualquier otra va por internet. Si tu DNS on-premises usa IPs públicas reutilizadas internamente o rangos no RFC 1918, las consultas salen a internet y no llegan. forwarding_path = "private" fuerza siempre la VPC.
Hacia targets Type 1 y 2 las consultas salen desde 35.199.192.0/19; con Type 2, on-premises necesita ruta
Cloud DNS usa el rango 35.199.192.0/19 para todos los clientes, y es el origen de las consultas a targets Type 1 y Type 2 (IP interna de VPC y on-premises por VPN o Interconnect). Los targets Type 3 usan los rangos de Google Public DNS, y en Type 4 el rango depende de las IPs a las que resuelve el nombre. Para un target Type 2, on-premises necesita una ruta a 35.199.192.0/19 cuyo next hop esté en la misma VPC, por un túnel Cloud VPN o un attachment de Interconnect. Con BGP eso es un anuncio de ruta custom en la sesión BGP del Cloud Router.
La inbound policy toma una IP de cada subnet, no una por región ni dos por endpoint
No hay endpoints que dimensionar: la policy reserva una IP interna del rango primario de cada subnet de la VPC (menos las de propósito especial), así que una VPC con muchas subnets tiene muchas IPs de entrada. Lo que cuenta para la respuesta es la región del túnel o attachment por donde llegó la consulta, no la de la subnet de la IP. Poné la policy en la VPC que tiene la VPN o el Interconnect.
VPC Network Peering no comparte las zonas privadas; hacen falta peering zones
VPC Network Peering no comparte automáticamente la información de DNS, así que una VPC peereada no resuelve por sí sola las zonas de la VPC hub. En hub-and-spoke, cada spoke necesita una DNS peering zone hacia el hub, y el DNS peering transitivo llega a un solo salto. Es la diferencia con Azure, donde un ruleset se linkea a VNets que ni siquiera están peereadas.
Terraform
main.tf
# DNS privado híbrido — GCP
#
# Inbound server policy (on-premises consulta las IPs que toma de cada subnet),
# forwarding zone privada para corp.example.com y el Cloud Router anunciando
# 35.199.192.0/19, el rango desde el que Cloud DNS envía las consultas reenviadas.
locals {
# Origen de toda consulta que Cloud DNS reenvía: on-premises necesita la ruta de vuelta.
cloud_dns_source_range = "35.199.192.0/19"
onprem_fqdn = "${trimsuffix(var.onprem_domain, ".")}."
zone_fqdn = "${trimsuffix(var.private_zone_name, ".")}."
}
resource "google_dns_policy" "inbound" {
project = var.project_id
name = "${var.name}-inbound"
description = "Permite que on-premises consulte las zonas privadas de esta VPC"
enable_inbound_forwarding = true
enable_logging = true
networks {
network_url = var.network_id
}
}
resource "google_dns_managed_zone" "onprem" {
#checkov:skip=CKV_GCP_16:DNSSEC no aplica a una forwarding zone privada (las respuestas las da on-premises).
project = var.project_id
name = "${var.name}-onprem"
dns_name = local.onprem_fqdn
description = "Reenvía ${var.onprem_domain} a los DNS on-premises"
visibility = "private"
private_visibility_config {
networks {
network_url = var.network_id
}
}
forwarding_config {
dynamic "target_name_servers" {
for_each = var.onprem_dns_servers
content {
ipv4_address = target_name_servers.value
# Siempre por la VPC, aunque el destino no sea RFC 1918 (gotcha: forwarding-path-private).
forwarding_path = "private"
}
}
}
}
resource "google_dns_managed_zone" "cloud" {
#checkov:skip=CKV_GCP_16:DNSSEC no aplica a zonas privadas en Cloud DNS.
project = var.project_id
name = "${var.name}-cloud"
dns_name = local.zone_fqdn
description = "Zona privada resoluble desde on-premises vía la inbound policy"
visibility = "private"
private_visibility_config {
networks {
network_url = var.network_id
}
}
}
# Sin este anuncio, on-premises recibe la consulta pero la respuesta no vuelve
# (gotcha: return-route-35-199-192-0-19).
resource "google_compute_router" "hybrid" {
project = var.project_id
name = "${var.name}-router"
region = var.region
network = var.network_id
bgp {
asn = var.router_asn
advertise_mode = "CUSTOM"
advertised_groups = ["ALL_SUBNETS"]
advertised_ip_ranges {
range = local.cloud_dns_source_range
description = "Origen de las consultas reenviadas por Cloud DNS"
}
}
}variables.tf
variable "project_id" {
description = "Proyecto host de la VPC hub."
type = string
}
variable "region" {
description = "Región del Cloud Router y de la VPN/Interconnect."
type = string
default = "us-central1"
}
variable "name" {
description = "Prefijo para nombrar los recursos (ej. \"dns-hub\")."
type = string
validation {
condition = can(regex("^[a-z][a-z0-9-]{2,19}$", var.name))
error_message = "name: 3-20 caracteres, empieza con letra, minúsculas, números o guiones."
}
}
variable "network_id" {
description = "ID de la VPC hub (projects/<p>/global/networks/<n>), la que tiene la VPN o el Interconnect."
type = string
}
variable "onprem_dns_servers" {
description = "IPs de los DNS on-premises."
type = list(string)
validation {
condition = length(var.onprem_dns_servers) >= 1
error_message = "onprem_dns_servers: al menos uno."
}
}
variable "onprem_domain" {
description = "Dominio corporativo que se reenvía a on-premises."
type = string
default = "corp.example.com"
}
variable "private_zone_name" {
description = "Zona privada que on-premises tiene que resolver."
type = string
default = "gcp.example.internal"
}
variable "router_asn" {
description = "ASN privado del Cloud Router que habla BGP con on-premises."
type = number
default = 64514
}outputs.tf
output "cloud_dns_source_range" {
description = "Rango a permitir (UDP y TCP 53) en el firewall on-premises y a rutear de vuelta por la VPN/Interconnect."
value = local.cloud_dns_source_range
}
output "inbound_forwarder_hint" {
description = "Las IPs de entrada se ven después del apply con este comando."
value = "gcloud compute addresses list --project ${var.project_id} --filter='purpose = \"DNS_RESOLVER\"'"
}versions.tf
terraform {
required_version = ">= 1.7.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 8.6"
}
}
}
provider "google" {
project = var.project_id
region = var.region
}