Skip to content

Private Endpoints — Storage & Key Vault

Overview

Each environment’s storage account (blob) and Key Vault are reachable only over private endpoints — public network access is disabled on both. The endpoints live in the spoke VNet’s PE subnet and register A records in the relevant privatelink.* private DNS zones, so apps, VMs, and VPN clients resolve them to private IPs.

Both are created by the workload-env root, once per environment (dev, qa, regression, training, uat, prod). The blob and vaultcore zones are global, region-less zones owned by the control-plane-dns-zones root.

Blob (Storage Account)

Defined in infrastructure/workload-env/main.tf (azurerm_private_endpoint.storage_blob):

AttributeValue
PE namepe-blob-saesharsapp{env}cus (pe-blob-${local.storage_name})
Connection namepe-blob-saesharsapp{env}cus-psc
Resource grouprg-eshars-storage-{tier}-cus (the env’s storage RG)
Subnetsnet-pe-eshars-{tier}-cus (PE subnet in the spoke VNet)
Targetmodule.storage.id
subresource_names["blob"]
DNS zone groupdefaultprivatelink.blob.core.windows.net

The storage account is CMK-encrypted (per-env key in the purge-protected scaffold KV) and has public access disabled — the blob PE is the only ingress.

Key Vault

The Key Vault private endpoint is created inside the key-vault module (infrastructure/modules/key-vault/main.tf, azurerm_private_endpoint.this), wired up by the workload-env module call:

AttributeValue
PE namepe-kv-eshars-{env}-cus (pe-${var.name})
Connection namepe-kv-eshars-{env}-cus-psc
Subnetsnet-pe-eshars-{tier}-cus (passed in as pe_subnet_id)
Targetthe env Key Vault
subresource_names["vault"]
DNS zone groupdefaultprivatelink.vaultcore.azure.net

The module sets public_network_access_enabled = false. The CI/CD service principal is granted Key Vault Secrets Officer so tofu apply can still write secrets through the private endpoint.

DNS Configuration

ZoneOwned byResource groupScope
privatelink.blob.core.windows.netcontrol-plane-dns-zonesrg-eshars-global-{tier}Global, region-less
privatelink.vaultcore.azure.netcontrol-plane-dns-zonesrg-eshars-global-{tier}Global, region-less

workload-env reads both via data "azurerm_private_dns_zone" lookups in var.global_rg (rg-eshars-global-{tier}). This differs from the SQL zone, which lives with the spoke network — see Private Endpoints — Database.

The global zones are linked to the hub VNet (and the spoke via peering), so hub-linked VNets and VPN clients resolve the blob and vault FQDNs to the private endpoint IPs. VPN resolution goes through the DNS forwarder (vm-eshars-dns-{tier}-cus) → see INFRA-0009.

Testing Connectivity

From a host inside the spoke or over the VPN:

Terminal window
# Each should resolve to a 10.x private IP, not a public one
nslookup saesharsappdevcus.blob.core.windows.net
nslookup kv-eshars-dev-cus.vault.azure.net

If a FQDN resolves to a public IP, the PE’s private_dns_zone_group link or the VNet-to-zone link is missing.