Skip to content

CloudRunbook | Practical Cloud Engineering

Private Endpoints + DNS baseline: stop outages before they happen

6 min read

A practical baseline for Azure Private Endpoints and DNS: ownership, zone design, resolver routing, and onboarding patterns that prevent midnight outages.

Applies to
Azure Private LinkPrivate EndpointsPrivate DNS ZonesAzure DNS Private ResolverHub/Spoke
Effort
1–2 days
Risk
Medium–High
Rollback
Plan required
Last reviewed
26 Jul 2026
TL;DR
  • Put every Private DNS zone in one platform subscription and guard it like any other shared service.
  • Use Azure DNS Private Resolver + hub-only links; automation handles zone linking when subscriptions are vended.
  • Day 0: deploy zones/resolver, run linking automation, publish the request workflow. Day 30: add policy/monitoring, integrate with ops. Quarterly: audit links vs inventory and renew rollbacks.
  • Terraform/Bicep only—portal deployments create outages at 02:00.
  • KQL + Policy catch drift; playbooks and rollbacks exist per service.
Before you start
  • Access: Private DNS Zone Contributor on the platform DNS resource group, and Network Contributor on the vNets you’re linking.
  • Tooling: Azure CLI 2.60+, Terraform 1.7+ with azurerm 3.100+.
  • Know your TTLs: DNS changes are not instant and rollback is not instant either. Record the TTLs on the zones you’re touching before you start.
  • Blast radius: removing a zone link breaks name resolution for every workload in that vNet, usually silently.

Why Private Endpoints break at 02:00

Private Endpoints without governance are outage factories. The moment someone links a zone to the wrong vNet, deletes the record, or forgets reverse lookups, apps stop resolving. A baseline keeps ownership, automation, and rollback in the platform team instead of relying on every workload engineer remembering the DNS handshake.

What good looks like

  • Zones in one place – all Azure Private DNS zones owned by platform, tagged, versioned.
  • Hub-based resolver – Azure DNS Private Resolver (inbound/outbound) deployed to the hub, workloads linked centrally.
  • Automated linking – subscription vending calls Terraform/Bicep modules to link spokes and register records.
  • Policy guardrails – Azure Policy ensures Private Endpoints only land in approved subnets and zones are linked.
  • Monitoring + rollback – scripts compare expected vs actual links, alerts fire on deletion, playbooks exist per service.

Baseline decision points

  1. Zone ownership – Platform subscription is the default; avoids sprawl and simplifies approvals.
  2. Linking pattern – Hub-only links + resolver vs direct workload links. Hub pattern reduces churn and RBAC bloat.
  3. Per-service vs shared zones – Usually one zone per Azure service (privatelink.<service>.windows.net); easier for lifecycle and rollback.
  4. Automation tooling – Terraform/Bicep modules invoked by vending or change pipeline. No portal.
  5. Resolver strategy – Azure DNS Private Resolver with inbound/outbound endpoints in hub VNets; no custom DNS VMs.
  6. Subscription vending hook – Decide when the linking script runs (during networking module or Private Endpoint request).
  7. Monitoring + drift – How you detect missing links, stale records, or unauthorised zones. Use KQL and automation.

Signal vs noise

  • Enable now: Zones, resolver, linking automation, policies for subnet restrictions, monitoring scripts, request workflow.
  • Enable at Day 30: Reverse lookup zones and advanced alerting once the baseline is stable.
  • Probably never: Letting workloads own their own zones, manual linking (“just this once”), or building custom DNS servers because “it feels familiar.”

Phased rollout

  • Day 0 baseline – Deploy zones/resolver, build Terraform module for linking, document the request workflow, integrate with vending.
  • Day 30 hardening – Enable policy guardrails, add reverse lookup zones, wire drift detection into alerting, start logging.
  • Quarterly review – Compare expected vs actual links, rotate resolver credentials, test rollback playbooks, update service catalogue.

Runbook: Private Endpoints + DNS baseline

  1. Define ownership and landing zones for DNS resources

    Put private DNS zones, the Azure DNS Private Resolver, and automation identities in a platform subscription. Document the RBAC split: platform controls records; workloads request via automation or service management.

  2. Catalogue the services you allow via Private Link

    List the Azure services (e.g. Key Vault, Storage, SQL, Container Registry) that are permitted. This drives which private zones you deploy. Keep the list under version control so policy and automation stay aligned.

  3. Deploy standard private DNS zones with IaC

    Create the per-service zones ahead of time. Use consistent naming and tag them with owner + lifecycle information. This Terraform snippet keeps the catalogue consistent.

    HCL
    variable "zones" {
      type    = list(string)
      default = [
        "privatelink.vaultcore.azure.net",
        "privatelink.blob.core.windows.net",
        "privatelink.database.windows.net"
      ]
    }
    
    resource "azurerm_private_dns_zone" "this" {
      for_each = toset(var.zones)
    
      name                = each.value
      resource_group_name = "rg-platform-dns"
      tags = {
        owner   = "platform-dns"
        purpose = "PrivateLink"
      }
    }
  4. Provision Azure DNS Private Resolver in the hub

    Deploy inbound/outbound endpoints in the hub vNet. Configure forwarding rules to the private zones. This keeps spoke networks lightweight and avoids custom DNS servers.

  5. Automate vNet linking and zone registration

    Script or pipeline the linking of workload vNets to the relevant zones when a subscription onboards. Avoid manual linking; it inevitably drifts. Store metadata so you can generate reports of which vNets are linked to which zones.

  6. Create a Private Endpoint request workflow

    Provide a template (Terraform module, ARM/Bicep snippet, or portal checklist) that workloads must follow. Include parameters for approval, target subnet, zone linking, and rollback. Make the workflow part of your change process.

  7. Enforce policy guardrails

    Use Azure Policy to prevent random private endpoints in forbidden subnets, enforce Private DNS zone registration, and audit any endpoint without an RBAC-approved owner. This keeps requests visible.

  8. Add monitoring and drift detection

    Run scheduled scripts (Logic App/Azure Automation) to compare declared zone links against actual ones. Alert when zones are unlinked or when records are missing. Combine with activity log alerts for zone deletions.

  9. Feed the pattern into subscription vending

    When a new subscription is created, ensure the standard networking template includes the DNS forwarding settings and zone link automation. Workloads should inherit the ability to resolve private endpoints without extra steps.

Policy guardrails

Use Bicep to prevent rogue Private Endpoints and ensure zone linking happens. Replace policy IDs with your own definitions.

BICEP
targetScope = 'managementGroup'

@description('Region for the assignment identity. Not where policy applies.')
param location string = 'uksouth'

// Resolve real definition IDs first, e.g.:
//   az policy definition list --query "[?contains(displayName,'<search term>')].{name:displayName,id:id}" -o table
resource initiative 'Microsoft.Authorization/policySetDefinitions@2023-04-01' = {
  name: 'private-link-guardrails'
  properties: {
    displayName: 'Private Link Guardrails'
    description: 'Keep private endpoints in approved subnets and auto-link zones.'
    policyType: 'Custom'
    policyDefinitions: [
      {
        policyDefinitionReferenceId: 'allowed-subnets'
        policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/<allowed-subnets-id>'
      }
      {
        policyDefinitionReferenceId: 'deploy-zone-link'
        policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/<deploy-zone-link-id>'
      }
    ]
  }
}

resource assignment 'Microsoft.Authorization/policyAssignments@2023-04-01' = {
  name: 'private-link-guardrails'
  // Required: the initiative contains a deployIfNotExists effect, which cannot
  // remediate without an identity that holds rights at the assigned scope.
  location: location
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    displayName: 'Private Link Guardrails'
    policyDefinitionId: initiative.id
  }
}

resource remediationRights 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(managementGroup().id, assignment.id, 'Contributor')
  properties: {
    // Contributor — narrow this to the least-privileged role your definitions need.
    roleDefinitionId: subscriptionResourceId(
      'Microsoft.Authorization/roleDefinitions',
      'b24988ac-6180-42a0-ab88-20f7382dd24c'
    )
    principalId: assignment.identity.principalId
    principalType: 'ServicePrincipal'
  }
}

Validation checks

Drift detection here is a state question, not a log question: a zone that was never linked to a vNet generates no events at all, so no amount of log querying will surface it. Use Azure Resource Graph to read the actual links, then diff against what your IaC declares.

KUSTO
resources
| where type =~ 'microsoft.network/privatednszones/virtualnetworklinks'
| project zone = tostring(split(id, '/')[8]),
          link = name,
          vnet = tostring(properties.virtualNetwork.id),
          registrationEnabled = tobool(properties.registrationEnabled),
          subscriptionId
| order by zone asc, vnet asc

To spot the opposite problem — zones with no links at all, usually left behind by a retired subscription:

KUSTO
resources
| where type =~ 'microsoft.network/privatednszones'
| extend linkCount = toint(properties.numberOfVirtualNetworkLinks)
| where linkCount == 0
| project subscriptionId, resourceGroup, zone = name

Logs still earn their place for the destructive events. This alerts on links being removed or zones deleted, which is the failure mode that pages you at 02:00:

KQL
AzureActivity
| where TimeGenerated > ago(1d)
| where OperationNameValue has "PRIVATEDNSZONES"
| where OperationNameValue has "DELETE"
| where ActivityStatusValue =~ "Success"
| project TimeGenerated, Caller, OperationNameValue, _ResourceId
| order by TimeGenerated desc
  • All approved private DNS zones exist in the platform subscription with correct tags.

  • Azure DNS Private Resolver endpoints are deployed, healthy, and documented.

  • Every workload vNet is linked to the required zones via automation (no manual leftovers).

  • Private Endpoint requests use the standard workflow and land in approved subnets.

  • Azure Policy assignments audit or deny unsupported Private Link usage.

  • Monitoring alerts fire if a zone link is removed or a zone is deleted.

  • Subscription vending pipelines automatically grant workloads DNS resolution from day zero.

  • Rollback procedures for each service (Key Vault, Storage, SQL, etc.) are written and tested.

Common pitfalls

Pitfall: portal-only deployment

If each team creates zones and endpoints in the portal, you will never regain control. Use IaC or service catalogue items.

Pitfall: mixed ownership

Putting some zones in workload subs and others in platform subs is a support nightmare. Pick one model and document it.

Pitfall: no reverse lookup or resolver routing

Logs and diagnostics often need reverse DNS. Ignoring it makes investigations painful.

Pitfall: hub DNS not ready

If the resolver isn’t in place when workloads arrive, they bake in custom DNS workarounds that are hard to remove later.

Pitfall: no monitoring

Zone deletions and link removals go unnoticed until production fails. Monitor and alert on drift.

Rollback / back-out plan

  • To remove a Private Endpoint: delete the endpoint, clear the DNS A record, and run the automation that restores public connectivity if needed. Expect short outages.
  • To revert resolver changes: disable forwarding rules, but note that cached results may linger until TTLs expire. Communicate clearly with workloads before flipping.
  • To undo zone links: remove the link and flush DNS on affected hosts. Document why the link was removed and ensure alternatives (public endpoints or other regions) exist.

Rollback is never “instant” because DNS caching and dependency on Private Link may require maintenance windows. Keep playbooks per service.