Private Endpoints — Database (SQL)
Overview
Each environment’s Azure SQL Server is reachable only over a private
endpoint — there is no public access path. The private endpoint lives in the
spoke VNet’s PE subnet and registers an A record in the
privatelink.database.windows.net private DNS zone, so apps, VMs, and VPN
clients resolve sql-eshars-{env}-cus.database.windows.net to the private IP.
This is created by the workload-env root, once per environment (dev, qa,
regression, training, uat, prod). The SQL private DNS zone itself is owned by
the workload-network root (it is intentionally not a global zone — see
below).
Resources
Defined in infrastructure/workload-env/main.tf (azurerm_private_endpoint.sql):
| Attribute | Value |
|---|---|
| PE name | pe-sql-sql-eshars-{env}-cus (pe-sql-${local.sql_name}) |
| Connection name | pe-sql-sql-eshars-{env}-cus-psc |
| Resource group | rg-eshars-data-{tier}-cus (the env’s data RG) |
| Subnet | snet-pe-eshars-{tier}-cus (PE subnet in the spoke VNet) |
| Target | module.sql_server.id |
subresource_names | ["sqlServer"] |
is_manual_connection | false |
| DNS zone group | default → privatelink.database.windows.net |
The SQL Server module sets public_network_access_enabled = false, so the
private endpoint is the only ingress.
DNS Configuration
The private endpoint joins a private_dns_zone_group that points at the SQL
private DNS zone:
| Zone | privatelink.database.windows.net |
|---|---|
| Owned by | workload-network root (azurerm_private_dns_zone.sql) |
| Resource group | rg-eshars-{tier}-network-cus (spoke network RG) |
| Scope | Per tier (nonprod / prod) |
Unlike the blob and vaultcore zones — which are global, region-less zones
in rg-eshars-global-{tier} (owned by control-plane-dns-zones) — the SQL
zone deliberately lives with the spoke network. workload-env reads it via a
data "azurerm_private_dns_zone" "sql" lookup in local.rg_network.
The spoke VNet is linked to this zone so anything in the spoke resolves the SQL
FQDN to the PE IP. VPN clients resolve it through the DNS forwarder
(vm-eshars-dns-{tier}-cus) → see
INFRA-0009.
Application DNS Alias Layer
Applications do not connect to the raw SQL FQDN. workload-env also creates
a per-environment custom zone db.{env}.eshars.internal (the private-dns
module) with CNAME aliases:
eshars-primary.db.{env}.eshars.internaleshars-reporting.db.{env}.eshars.internal
These CNAME to the SQL server FQDN, which then resolves via the private endpoint
to the private IP. Connection strings use the alias, so the underlying server
can be swapped without a config redeploy. CNAME targets are managed
operationally; Terraform uses lifecycle { ignore_changes } and does not own
them. See Application Infrastructure.
Testing Connectivity
From a host inside the spoke (e.g. the ops VM) or over the VPN:
# Should resolve to a 10.x private IP, not a public onenslookup sql-eshars-dev-cus.database.windows.net
# Resolve via the app alias (CNAME -> SQL FQDN -> private IP)nslookup eshars-primary.db.dev.eshars.internal
# TCP reachability to SQLTest-NetConnection sql-eshars-dev-cus.database.windows.net -Port 1433If the FQDN resolves to a public IP, the PE’s private_dns_zone_group link or
the VNet-to-zone link is missing.
Related
- Private Endpoints — Storage — blob PE (same pattern, global zone)
- Application Infrastructure —
workload-envroot and the DB alias layer - Workload Network — spoke VNet, PE subnet, SQL private DNS zone
- INFRA-0009: DNS Forwarder & P2S VPN Routing