<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Azure Private Endpoint on Brewed in the Cloud by Chris Hailes</title><link>https://blog.brewedinthecloud.com/tags/azure-private-endpoint/</link><description>Recent content in Azure Private Endpoint on Brewed in the Cloud by Chris Hailes</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Thu, 11 Jun 2026 00:00:00 +1000</lastBuildDate><atom:link href="https://blog.brewedinthecloud.com/tags/azure-private-endpoint/rss.xml" rel="self" type="application/rss+xml"/><item><title>Private Endpoint Doesn’t Save You from DNS</title><link>https://blog.brewedinthecloud.com/p/pe-attacks-dns-vector/</link><pubDate>Thu, 11 Jun 2026 00:00:00 +1000</pubDate><guid>https://blog.brewedinthecloud.com/p/pe-attacks-dns-vector/</guid><description>&lt;p&gt;When teams talk about securing Azure PaaS with Private Endpoint, the conversation usually settles on the network path.&lt;/p&gt;
&lt;p&gt;The service now has a private IP. Traffic stays off the internet. Exposure is reduced. Everyone breathes a bit easier.&lt;/p&gt;
&lt;p&gt;That is not wrong. It is just incomplete.&lt;/p&gt;
&lt;p&gt;A Private Endpoint changes how traffic reaches a service, but it does not, by itself, guarantee that the client is reaching the right service. In most real deployments, that decision still rides on DNS. The path may be private, but the destination is still selected by name resolution.&lt;/p&gt;
&lt;p&gt;That changes something you design, deploy, and operate straight away: &lt;strong&gt;a Private Endpoint gives you a private transport path, not destination integrity&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="the-mental-model"&gt;The Mental Model
&lt;/h2&gt;&lt;p&gt;The common assumption is simple:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“We replaced the public endpoint with a private one, so the connectivity risk is mostly solved.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That assumption holds only if you treat connectivity as a pure routing problem. Most workloads do not connect to abstract network paths. They connect to service names. If the wrong system answers the naming question, the network can still do exactly what it was configured to do while the application reaches the wrong destination, fails unexpectedly, or inherits trust from systems its owners never intended to rely on.&lt;/p&gt;
&lt;p&gt;Treating Private Endpoint as the security boundary while treating DNS as background plumbing is a category error.&lt;/p&gt;
&lt;p&gt;A private IP narrows exposure. It does not remove the need to trust the path that tells clients where that IP should be.&lt;/p&gt;
&lt;h2 id="how-it-really-works"&gt;How It Really Works
&lt;/h2&gt;&lt;p&gt;Private Endpoint consumption feels clean because the application often keeps using a familiar service name. DNS simply returns a private address instead of a public one, and the application connects as normal.&lt;/p&gt;
&lt;p&gt;That convenience hides an important dependency chain:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The client must query the intended resolver.&lt;/li&gt;
&lt;li&gt;That resolver must use the intended zones and records.&lt;/li&gt;
&lt;li&gt;The returned answer must identify the intended Private Endpoint.&lt;/li&gt;
&lt;li&gt;No intermediate control should override that answer unexpectedly.&lt;/li&gt;
&lt;li&gt;The service-layer connection must still validate against the expected service identity.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The critical point is that Private Endpoint secures reachability to a private destination. DNS still participates in deciding which destination that is.&lt;/p&gt;
&lt;p&gt;That distinction matters because different DNS failures lead to different outcomes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;some failures break access entirely&lt;/li&gt;
&lt;li&gt;some send clients to an unexpected private target&lt;/li&gt;
&lt;li&gt;some change which administrative domain effectively controls service targeting&lt;/li&gt;
&lt;li&gt;some create confusing partial outages that look like network or platform faults&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not all of those outcomes are compromise. That is exactly why this gets missed. Engineers often reserve the word “security” for direct unauthorised access, when in practice loss of destination control is already a security-relevant condition.&lt;/p&gt;
&lt;h2 id="a-simple-trust-map"&gt;A Simple Trust Map
&lt;/h2&gt;&lt;div class="mermaid"&gt;flowchart LR
A[Application workload] --&gt; B[Configured DNS resolver]
B --&gt; C[Private DNS authority or delegated resolution path]
C --&gt; D[Private Endpoint IP]
D --&gt; E[Azure service via Private Link]
X[DNS admin influence]
Y[Resolver path drift]
Z[Incorrect or stale record]
X -. changes authority .-&gt; C
Y -. changes dependency .-&gt; B
Z -. changes target selection .-&gt; D
&lt;/div&gt;
&lt;p&gt;This is the part many designs flatten into “internal DNS”.&lt;/p&gt;
&lt;p&gt;From a threat-model perspective, that is too coarse. The question is not just whether the answer is private. The question is &lt;strong&gt;who can influence the answer, and therefore influence where trusted traffic goes&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="where-the-attack-surface-actually-appears"&gt;Where the Attack Surface Actually Appears
&lt;/h2&gt;&lt;h3 id="private-dns-becomes-part-of-the-trust-boundary"&gt;Private DNS becomes part of the trust boundary
&lt;/h3&gt;&lt;p&gt;Once a workload depends on private DNS records to reach a Private Endpoint-backed service, those records stop being neutral support objects. They become part of the application’s effective trust boundary.&lt;/p&gt;
&lt;p&gt;That is the shift many teams miss.&lt;/p&gt;
&lt;p&gt;A private DNS zone, a linked resolution path, or an override record may look operationally harmless because none of them move packets by themselves. But they can still influence service targeting. In a Private Endpoint architecture, that means they can influence which private destination a workload treats as authoritative.&lt;/p&gt;
&lt;p&gt;That does not mean every DNS issue enables interception or data theft. Often it does not. But it absolutely can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;deny access to a required service&lt;/li&gt;
&lt;li&gt;redirect clients towards an unintended private destination&lt;/li&gt;
&lt;li&gt;shift effective control of service targeting into another administrative domain&lt;/li&gt;
&lt;li&gt;create persistent failure modes that survive application redeployments and network reviews&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is enough to treat DNS as attack surface rather than plumbing.&lt;/p&gt;
&lt;h3 id="shared-dns-paths-broaden-trust-beyond-the-vnet"&gt;Shared DNS paths broaden trust beyond the VNet
&lt;/h3&gt;&lt;p&gt;This is where the comforting subnet diagram starts to mislead.&lt;/p&gt;
&lt;p&gt;A workload may sit in an isolated landing zone, use a locked-down subnet, and connect only to Private Endpoints. On paper, that looks tightly contained. But if name resolution depends on shared DNS services, central platform teams, hybrid resolvers, or external administrative paths, then the workload’s connectivity trust extends beyond its own network boundary.&lt;/p&gt;
&lt;p&gt;In other words, the VNet does not fully describe who influences where that workload goes.&lt;/p&gt;
&lt;p&gt;That matters in larger Azure estates because teams often assume Private Endpoint localises risk to the consuming network. In practice, DNS can reintroduce shared dependencies in ways the network design no longer makes obvious.&lt;/p&gt;
&lt;h3 id="dns-risk-is-persistent-not-a-day-0-problem"&gt;DNS risk is persistent, not a day-0 problem
&lt;/h3&gt;&lt;p&gt;Private Endpoint risk is often discussed as a provisioning concern: the endpoint is created, the record exists, the application connects, done.&lt;/p&gt;
&lt;p&gt;That is the wrong lifecycle.&lt;/p&gt;
&lt;p&gt;DNS is mutable by design. Records change. Zones move. Links are added. Resolver paths are centralised, migrated, inherited, or quietly overridden. Organisationally, ownership drifts even when infrastructure diagrams do not.&lt;/p&gt;
&lt;p&gt;That means the DNS risk attached to a Private Endpoint is not limited to initial deployment quality. It persists as long as the workload depends on that resolution path. An attacker, careless change, or platform-side drift does not need to touch the application subnet or the Private Endpoint NIC to change runtime behaviour.&lt;/p&gt;
&lt;h2 id="real-world-impact"&gt;Real-World Impact
&lt;/h2&gt;&lt;h3 id="design-review-needs-a-different-question"&gt;Design review needs a different question
&lt;/h3&gt;&lt;p&gt;During design review, the question is no longer just:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Does this workload use a Private Endpoint?”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It also needs to be:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Who can change where this workload believes that service lives?”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That is a materially different architecture question.&lt;/p&gt;
&lt;p&gt;It forces you to examine DNS ownership, zone authority, resolver dependencies, and shared platform controls as part of application connectivity trust. If those controls sit outside the workload’s intended trust boundary, then part of the workload’s security posture sits outside that boundary too.&lt;/p&gt;
&lt;h3 id="shared-services-can-quietly-centralise-influence"&gt;Shared services can quietly centralise influence
&lt;/h3&gt;&lt;p&gt;Centralised DNS often looks like operational maturity. Sometimes it is. But it also centralises the ability to change service targeting for many workloads at once.&lt;/p&gt;
&lt;p&gt;That does not automatically make centralisation wrong. It does mean the blast radius is architectural, not just administrative.&lt;/p&gt;
&lt;p&gt;If a central DNS path influences resolution for Private Endpoint-backed services across multiple landing zones, then one change path may affect systems that otherwise look isolated in network diagrams. For technical decision makers, that should change how DNS authority is classified. For operators, it should change which dependencies get examined first when failures appear inconsistent.&lt;/p&gt;
&lt;h3 id="incident-response-assumptions-need-tightening"&gt;Incident response assumptions need tightening
&lt;/h3&gt;&lt;p&gt;In incident triage, “it resolved to a private IP” should not be treated as proof that the workload reached the intended service.&lt;/p&gt;
&lt;p&gt;That assumption is too weak for Private Endpoint architectures.&lt;/p&gt;
&lt;p&gt;A private answer may still be stale, unintended, inherited from the wrong resolution path, or aligned to the wrong environment. If teams use “private IP observed” as a proxy for “trustworthy destination confirmed”, they will misdiagnose issues and overlook one of the most important control dependencies in the path.&lt;/p&gt;
&lt;p&gt;This is where the article’s thesis stops being theoretical. It changes how you investigate outages, dependency failures, and unexplained behaviour in supposedly isolated environments.&lt;/p&gt;
&lt;h2 id="a-behavioural-example"&gt;A Behavioural Example
&lt;/h2&gt;&lt;p&gt;Consider an application that retrieves secrets from Azure Key Vault through a Private Endpoint.&lt;/p&gt;
&lt;p&gt;The application team has done the expected things. Public network access is restricted. The consuming subnet can reach the Private Endpoint. Egress is tightly controlled. On a network diagram, the design looks sound.&lt;/p&gt;
&lt;p&gt;But the vault name resolves through a shared DNS path outside the application team’s direct control.&lt;/p&gt;
&lt;p&gt;A later change in that path does not need to alter routes, firewall rules, or the Private Endpoint itself to affect the application. It only needs to alter where the application believes the vault lives. The likely outcome is not dramatic movie-style interception. More often it is failed access, wrong-environment targeting, or a dependency outage that presents like an Azure networking issue even though the transport path is doing exactly what it was told.&lt;/p&gt;
&lt;p&gt;That is the operational trap: &lt;strong&gt;the application team can lose effective control of service targeting without any visible network change in its own subscription&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="gotchas--edge-cases"&gt;Gotchas &amp;amp; Edge Cases
&lt;/h2&gt;&lt;h3 id="private-is-not-the-same-as-correct"&gt;“Private” is not the same as “correct”
&lt;/h3&gt;&lt;p&gt;A private IP answer only tells you something about address scope. It does not prove the answer came from the intended authority or points to the intended service instance.&lt;/p&gt;
&lt;h3 id="dns-influence-is-easy-to-hide-inside-legitimate-administration"&gt;DNS influence is easy to hide inside legitimate administration
&lt;/h3&gt;&lt;p&gt;A malicious route change often looks suspicious. A DNS record change may look like normal infrastructure maintenance, especially in shared services environments.&lt;/p&gt;
&lt;h3 id="network-isolation-can-disguise-naming-dependency"&gt;Network isolation can disguise naming dependency
&lt;/h3&gt;&lt;p&gt;The more locked down the packet path becomes, the easier it is for teams to assume the remaining dependencies are benign. In Private Endpoint architectures, that assumption gets dangerous quickly.&lt;/p&gt;
&lt;h3 id="service-layer-controls-still-matter"&gt;Service-layer controls still matter
&lt;/h3&gt;&lt;p&gt;DNS influence does not automatically equal successful compromise. Authentication, authorisation, and certificate validation still shape the final outcome. But they do not make DNS irrelevant; they define how far a bad DNS outcome can propagate.&lt;/p&gt;
&lt;h2 id="best-practices"&gt;Best Practices
&lt;/h2&gt;&lt;h3 id="treat-dns-authority-as-part-of-connectivity-security"&gt;Treat DNS authority as part of connectivity security
&lt;/h3&gt;&lt;p&gt;If DNS determines which private destination a workload reaches, then DNS authority is not ancillary. It is part of the workload’s effective connectivity security model.&lt;/p&gt;
&lt;h3 id="review-change-authority-not-just-network-reachability"&gt;Review change authority, not just network reachability
&lt;/h3&gt;&lt;p&gt;Packet path reviews are not enough. You also need to understand who can change resolver behaviour, zone data, or other name-resolution dependencies that alter target selection.&lt;/p&gt;
&lt;h3 id="classify-shared-dns-services-by-blast-radius"&gt;Classify shared DNS services by blast radius
&lt;/h3&gt;&lt;p&gt;A shared resolver or private DNS authority serving multiple Private Endpoint consumers is a multi-system trust dependency. Treat it accordingly.&lt;/p&gt;
&lt;h3 id="separate-private-transport-from-trusted-destination-in-your-thinking"&gt;Separate private transport from trusted destination in your thinking
&lt;/h3&gt;&lt;p&gt;A packet can stay on a private path and still fail to reach the intended service. Those are different properties, and mature designs treat them separately.&lt;/p&gt;
&lt;div class="insight"&gt;
&lt;div class="insight-icon"&gt;🍺&lt;/div&gt;
&lt;div class="insight-content"&gt;
&lt;strong&gt;Brewed Insight:&lt;/strong&gt; Private Endpoint reduces exposure, but DNS still decides where trust lands. If you model Private Endpoint as the boundary and DNS as plumbing, you are securing the road while outsourcing the destination.
&lt;/div&gt;
&lt;/div&gt;
&lt;style&gt;
.insight {
display: flex;
align-items: center;
background-color: #0089e41c;
border-left: 10px solid #D69A2D;
padding: 10px;
margin: 20px 0;
border-radius: 4px;
}
.insight-icon {
font-size: 24px;
margin-right: 10px;
}
.insight-content {
flex: 1;
}
&lt;/style&gt;&lt;h2 id="learn-more"&gt;Learn More
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/azure/private-link/private-endpoint-overview" target="_blank" rel="noopener"
&gt;What is Azure Private Endpoint?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/azure/private-link/" target="_blank" rel="noopener"
&gt;Azure Private Link documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/azure/dns/" target="_blank" rel="noopener"
&gt;Azure DNS documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/azure/dns/private-dns-overview" target="_blank" rel="noopener"
&gt;Azure Private DNS documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Private Endpoint Control Planes: Private Reachability Isn’t Trusted Reachability</title><link>https://blog.brewedinthecloud.com/p/pe-attacks-control-plane-risks/</link><pubDate>Tue, 09 Jun 2026 00:00:00 +1000</pubDate><guid>https://blog.brewedinthecloud.com/p/pe-attacks-control-plane-risks/</guid><description>&lt;p&gt;You can take a sensitive service off the public internet, pin it behind a Private Endpoint, and come away feeling like the trust problem is mostly solved.&lt;/p&gt;
&lt;p&gt;That feeling is exactly where the trouble starts.&lt;/p&gt;
&lt;p&gt;Private Endpoints can reduce exposure. They can narrow the network paths that reach a service. But they do not prove that the environments consuming that service privately are legitimate, or that the administrative chain which created that private path deserves trust.&lt;/p&gt;
&lt;p&gt;The real design error is not exposing a service privately. It is assuming that any environment able to consume it privately has therefore earned trust.&lt;/p&gt;
&lt;p&gt;That is a control-plane problem, not a networking success story.&lt;/p&gt;
&lt;h2 id="the-mental-model"&gt;The Mental Model
&lt;/h2&gt;&lt;p&gt;The common assumption is simple:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If a service is only reachable through a Private Endpoint, then access is effectively trusted because it is no longer public.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That assumption compresses two different questions into one:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;how traffic reaches the service;&lt;/li&gt;
&lt;li&gt;whether the thing reaching it should be trusted.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Private Endpoints only answer the first one.&lt;/p&gt;
&lt;p&gt;They change the path. They do not validate the consumer, the administrators who created the path, or the approval decisions that allowed it to exist. The private IP is not the trust boundary. It is the output of a series of management decisions.&lt;/p&gt;
&lt;p&gt;So the useful shift in mental model is this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;private connectivity is a routing property&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;trusted connectivity is an authority decision&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Private Endpoints affect the first one, not the second&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The moment a team treats private reachability as evidence that a consumer environment has been legitimately trusted, they stop inspecting the layer where trust was actually granted.&lt;/p&gt;
&lt;h2 id="how-it-really-works"&gt;How It Really Works
&lt;/h2&gt;&lt;p&gt;Private Endpoint is usually discussed as if it were a networking feature with some management overhead attached.&lt;/p&gt;
&lt;p&gt;That framing is backwards.&lt;/p&gt;
&lt;p&gt;It is a control-plane object that creates a private data-plane path.&lt;/p&gt;
&lt;p&gt;That path exists only because a chain of administrative decisions makes it exist:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a Private Endpoint resource is created;&lt;/li&gt;
&lt;li&gt;it is placed in a subnet within a virtual network;&lt;/li&gt;
&lt;li&gt;it references a specific Private Link-capable subresource;&lt;/li&gt;
&lt;li&gt;a connection request is raised and allowed through the target service’s approval model;&lt;/li&gt;
&lt;li&gt;DNS then enables clients to use that private path.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By the time packets flow, the trust decisions have already happened.&lt;/p&gt;
&lt;h3 id="approval-is-a-trust-decision"&gt;Approval is a trust decision
&lt;/h3&gt;&lt;p&gt;Approval is often treated as a minor workflow event. It is not. It is the point at which a service owner, delegated operator, or service-specific approval path allows a particular consumer environment to establish private reachability.&lt;/p&gt;
&lt;p&gt;That is a trust decision.&lt;/p&gt;
&lt;p&gt;It is also one of the reasons Private Endpoint can be over-trusted architecturally. The assurance conveyed by a successful private connection is not perfectly uniform across Azure services or ownership models. Teams generalise from one service pattern to another, see a working private path, and assume it carries the same trust meaning everywhere. It does not.&lt;/p&gt;
&lt;p&gt;If your architecture treats “connected privately” as if it implied a consistent trust posture across services, you are relying on a certainty the platform does not actually provide.&lt;/p&gt;
&lt;h3 id="the-endpoint-and-the-service-do-not-share-the-same-ownership-story"&gt;The endpoint and the service do not share the same ownership story
&lt;/h3&gt;&lt;p&gt;A Private Endpoint is a separate Azure resource. It can live in a different resource group, subscription, or operating domain from the service it connects to.&lt;/p&gt;
&lt;p&gt;That is not just an implementation detail. It means the protected service and the private path to it can have different lifecycle owners, different administrative scopes, and different review habits.&lt;/p&gt;
&lt;p&gt;This is the point many reviews miss: hardening the service does not tell you enough if someone else can still instantiate and retain private reachability to it.&lt;/p&gt;
&lt;h3 id="administrative-separation-is-not-trusted-separation"&gt;Administrative separation is not trusted separation
&lt;/h3&gt;&lt;p&gt;Subscriptions and resource groups are useful operational boundaries. They are not evidence of trustworthy separation by themselves.&lt;/p&gt;
&lt;p&gt;If the same administrators, inherited permissions, or approval practices can still bridge those scopes through management actions, then the boundary is structural, not trustworthy.&lt;/p&gt;
&lt;p&gt;This is the design mistake that shows up repeatedly in Azure estates: teams see separate subscriptions and infer separate trust. In reality, they have reviewed layout, not authority.&lt;/p&gt;
&lt;h2 id="trust-flow-not-just-packet-flow"&gt;Trust Flow, Not Just Packet Flow
&lt;/h2&gt;&lt;p&gt;The control-plane risk is easier to see when the trust relationships are explicit.&lt;/p&gt;
&lt;div class="mermaid"&gt;flowchart LR
A[Identity with Private Endpoint management access] --&gt; B[Create or modify Private Endpoint]
B --&gt; C[Consumer VNet and subnet placement]
B --&gt; D[Connection request to target resource]
D --&gt; E[Service-specific approval path]
E --&gt; F[Private connectivity established]
F --&gt; G[Private DNS mapping]
G --&gt; H[Client workload uses private path]
I[Platform admin] --&gt; C
J[DNS admin] --&gt; G
&lt;/div&gt;
&lt;p&gt;The interesting part is not where the packets flow. It is where trust gets asserted:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the identity allowed to create the endpoint;&lt;/li&gt;
&lt;li&gt;the team controlling the subnet where it lands;&lt;/li&gt;
&lt;li&gt;the approval path for the target resource;&lt;/li&gt;
&lt;li&gt;the DNS authority that makes the path usable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If those actors sit in different places, with different review standards, then “internal-only access” is really the product of several loosely connected trust decisions.&lt;/p&gt;
&lt;h2 id="real-world-impact"&gt;Real-World Impact
&lt;/h2&gt;&lt;p&gt;This changes how you should interpret private connectivity in production.&lt;/p&gt;
&lt;h3 id="private-access-does-not-prove-the-consumer-was-ever-properly-vouched-for"&gt;Private access does not prove the consumer was ever properly vouched for
&lt;/h3&gt;&lt;p&gt;A service may be reachable only over Private Endpoint and still be exposed to environments that were never meaningfully validated as legitimate consumers.&lt;/p&gt;
&lt;p&gt;That is the central operational mistake: teams use private reachability as a proxy for consumer legitimacy.&lt;/p&gt;
&lt;p&gt;The path is private. The trust judgement behind it may be weak, inherited, stale, or simply assumed.&lt;/p&gt;
&lt;h3 id="service-owners-can-lose-control-of-access-without-changing-the-service"&gt;Service owners can lose control of access without changing the service
&lt;/h3&gt;&lt;p&gt;If endpoint creation, subnet placement, approval, and DNS are controlled by different operating groups, then access can expand without the target service configuration changing in any obvious way.&lt;/p&gt;
&lt;p&gt;That means a service can look hardened while the practical conditions of who can reach it are being altered elsewhere.&lt;/p&gt;
&lt;h3 id="drift-preserves-trust-long-after-intent-disappears"&gt;Drift preserves trust long after intent disappears
&lt;/h3&gt;&lt;p&gt;A stale Private Endpoint is not just a clean-up problem. It can preserve private access from an environment that has been retired, repurposed, handed to another team, or no longer deserves the implied trust of a live private path.&lt;/p&gt;
&lt;p&gt;Most control-plane risk does not arrive dramatically. It lingers.&lt;/p&gt;
&lt;h3 id="how-would-this-change-something-i-design-deploy-or-operate"&gt;How would this change something I design, deploy, or operate?
&lt;/h3&gt;&lt;p&gt;It should change your review posture.&lt;/p&gt;
&lt;p&gt;Do not stop at “is this service private?” Ask which identities, teams, and approval paths can create or preserve private reachability to it. If the answer depends on administrative sprawl, inherited control, or assumptions about what “internal” means, then the architecture is trusting more than it intends.&lt;/p&gt;
&lt;h2 id="abuse-scenarios-that-matter"&gt;Abuse Scenarios That Matter
&lt;/h2&gt;&lt;p&gt;The risk is not endpoint creation in isolation. The risk appears when endpoint creation, placement into a reachable network, service approval, and usable name resolution combine into a private path that looks legitimate simply because it works.&lt;/p&gt;
&lt;h3 id="a-consumer-environment-gains-a-private-path-it-was-never-meant-to-have"&gt;A consumer environment gains a private path it was never meant to have
&lt;/h3&gt;&lt;p&gt;An operator creates a Private Endpoint into a subnet already reachable by workloads they control. The target service connection is then approved through a weak or overly routine process, and the necessary DNS conditions are in place for those workloads to resolve the service privately.&lt;/p&gt;
&lt;p&gt;Nothing has become public. But a new trusted-looking path now exists from an environment nobody properly vouched for.&lt;/p&gt;
&lt;h3 id="approval-becomes-habit-instead-of-judgement"&gt;Approval becomes habit instead of judgement
&lt;/h3&gt;&lt;p&gt;A team responsible for approving Private Endpoint connections sees requests from “internal” subscriptions and treats them as normal by default. The endpoint is private, the naming looks familiar, and the approval goes through without a real question being asked about whether that consumer environment should have reachability at all.&lt;/p&gt;
&lt;p&gt;That is not a tooling problem. It is trust being inferred from private placement.&lt;/p&gt;
&lt;h3 id="old-environments-retain-private-access-by-inertia"&gt;Old environments retain private access by inertia
&lt;/h3&gt;&lt;p&gt;An application is migrated or retired, but the Private Endpoint remains, DNS still resolves, and the consumer network is still reachable. The original service owner assumes the old path was cleaned up. The platform team assumes the service owner still needs it.&lt;/p&gt;
&lt;p&gt;No one is actively deciding to trust the path. The trust simply never got revoked.&lt;/p&gt;
&lt;h2 id="implementation-example"&gt;Implementation Example
&lt;/h2&gt;&lt;p&gt;This is not a deployment guide, but the resource shape is useful because it exposes where the control-plane split actually lives.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bicep" data-lang="bicep"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;pe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Microsoft.Network/privateEndpoints@2023-09-01&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;pe-app-sql-prod&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;resourceGroup&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nv"&gt;location&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;subnet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-network-prod/providers/Microsoft.Network/virtualNetworks/vnet-hub-prod/subnets/snet-private-endpoints&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;privateLinkServiceConnections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;sqlConnection&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;privateLinkServiceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/rg-data-prod/providers/Microsoft.Sql/servers/sql-prod-01&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;groupIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;sqlServer&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;requestMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Prod application connectivity&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The useful part is not the syntax. It is what the object tells you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the endpoint lives in its own management scope;&lt;/li&gt;
&lt;li&gt;the subnet is controlled separately;&lt;/li&gt;
&lt;li&gt;the target service may sit under different ownership;&lt;/li&gt;
&lt;li&gt;the connection request is a trust event before it is a traffic path.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is why “the service is private” is not enough. The service may be private while the authority to create private reachability sits in a different part of the estate entirely.&lt;/p&gt;
&lt;p&gt;You can also inspect Private Endpoint connection state directly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;az network private-endpoint-connection list &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="se"&gt;&lt;/span&gt; --id &lt;span class="s2"&gt;&amp;#34;/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/rg-data-prod/providers/Microsoft.Sql/servers/sql-prod-01&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="se"&gt;&lt;/span&gt; --query &lt;span class="s2"&gt;&amp;#34;[].{name:name,status:properties.privateLinkServiceConnectionState.status,description:properties.privateLinkServiceConnectionState.description}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The command matters less than the discipline behind it. If you are not looking at which consumers have private connectivity to a service, you are trusting historical approvals more than current intent.&lt;/p&gt;
&lt;h2 id="gotchas--edge-cases"&gt;Gotchas &amp;amp; Edge Cases
&lt;/h2&gt;&lt;h3 id="working-connectivity-can-disguise-poor-trust-decisions"&gt;Working connectivity can disguise poor trust decisions
&lt;/h3&gt;&lt;p&gt;Once a name resolves privately and the application works, the path starts to feel legitimate by default. But functional connectivity is not proof that the consumer environment was ever supposed to be trusted.&lt;/p&gt;
&lt;h3 id="inconsistent-service-behaviour-creates-false-assumptions"&gt;Inconsistent service behaviour creates false assumptions
&lt;/h3&gt;&lt;p&gt;Private Endpoint approval and ownership patterns vary across Azure services. The risk is not just operational complexity. It is that teams assume a consistent trust model where none exists.&lt;/p&gt;
&lt;h3 id="persistence-is-often-the-bigger-problem-than-deletion"&gt;Persistence is often the bigger problem than deletion
&lt;/h3&gt;&lt;p&gt;Accidental deletion creates incidents, so it gets attention. Quietly persistent connectivity is often more dangerous because it preserves trust without forcing anyone to rejustify it.&lt;/p&gt;
&lt;h2 id="best-practices"&gt;Best Practices
&lt;/h2&gt;&lt;p&gt;This is not a role design or privileged workflow post, so I will stay out of RBAC mechanics. The practical design implications are still clear.&lt;/p&gt;
&lt;h3 id="treat-private-endpoint-creation-as-a-security-relevant-action"&gt;Treat Private Endpoint creation as a security-relevant action
&lt;/h3&gt;&lt;p&gt;Creating a Private Endpoint can extend private reachability to a sensitive service without changing the service configuration itself. That alone makes it worth treating as more than routine network plumbing.&lt;/p&gt;
&lt;h3 id="review-private-consumers-not-just-protected-services"&gt;Review private consumers, not just protected services
&lt;/h3&gt;&lt;p&gt;A service review is incomplete if it asks only how the service is protected and never asks who can privately attach to it.&lt;/p&gt;
&lt;h3 id="treat-subscription-boundaries-as-operational-structure-not-proof-of-trust"&gt;Treat subscription boundaries as operational structure, not proof of trust
&lt;/h3&gt;&lt;p&gt;If the same management actors can still bridge those boundaries, then the separation may be useful administratively while meaningless from a trust perspective.&lt;/p&gt;
&lt;h3 id="make-one-question-unavoidable-in-design-review"&gt;Make one question unavoidable in design review
&lt;/h3&gt;&lt;p&gt;For any sensitive service exposed through Private Endpoint, ask:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Which identities, teams, and approval paths can create, approve, or preserve private reachability to this service, and why should that imply trust in the consuming environment?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If that question is difficult to answer, the trust model is weaker than the network diagram suggests.&lt;/p&gt;
&lt;h3 id="design-lifecycle-ownership-into-the-path-itself"&gt;Design lifecycle ownership into the path itself
&lt;/h3&gt;&lt;p&gt;Private connectivity should have a clear owner, reason for existence, and review trigger. Otherwise, it becomes part of the environment’s inherited internal attack surface.&lt;/p&gt;
&lt;div class="insight"&gt;
&lt;div class="insight-icon"&gt;🍺&lt;/div&gt;
&lt;div class="insight-content"&gt;
&lt;strong&gt;Brewed Insight:&lt;/strong&gt; Private Endpoints are good at making traffic private. They are useless as proof that the private consumer has earned trust. Once private reachability gets mistaken for trusted reachability, the control plane becomes the part of the attack surface nobody is watching.
&lt;/div&gt;
&lt;/div&gt;
&lt;style&gt;
.insight {
display: flex;
align-items: center;
background-color: #0089e41c;
border-left: 10px solid #D69A2D;
padding: 10px;
margin: 20px 0;
border-radius: 4px;
}
.insight-icon {
font-size: 24px;
margin-right: 10px;
}
.insight-content {
flex: 1;
}
&lt;/style&gt;&lt;h2 id="learn-more"&gt;Learn More
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/azure/private-link/private-endpoint-overview" target="_blank" rel="noopener"
&gt;Azure Private Endpoint documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/azure/private-link/private-link-overview" target="_blank" rel="noopener"
&gt;What is Azure Private Link?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/azure/private-link/manage-private-endpoint" target="_blank" rel="noopener"
&gt;Manage a private endpoint connection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/azure/azure-resource-manager/management/overview" target="_blank" rel="noopener"
&gt;Azure resource management overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/azure/private-link/security-considerations" target="_blank" rel="noopener"
&gt;Private Link security considerations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Why “Private” Does Not Mean “Secure”</title><link>https://blog.brewedinthecloud.com/p/pe-attacks-private-does-not-mean-secure/</link><pubDate>Thu, 04 Jun 2026 00:00:00 +1000</pubDate><guid>https://blog.brewedinthecloud.com/p/pe-attacks-private-does-not-mean-secure/</guid><description>&lt;p&gt;Private Endpoint has a branding problem.&lt;/p&gt;
&lt;p&gt;The word &lt;em&gt;private&lt;/em&gt; lands in architecture discussions like a shortcut for &lt;em&gt;safe&lt;/em&gt;. Public access is removed, the service gets a private IP, and somewhere between design review and production rollout, reduced exposure gets mistaken for established trust.&lt;/p&gt;
&lt;p&gt;That is the mistake.&lt;/p&gt;
&lt;p&gt;A private endpoint is useful. Often very useful. But if your security posture improves mainly because you made something harder to reach from the internet, you have changed exposure, not proven safety. You have narrowed the path, not validated the caller, the dependency chain, or the operational control around it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Private Endpoint reduces exposure; it does not create trust.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That is the lens for this post.&lt;/p&gt;
&lt;h2 id="the-mental-model"&gt;The Mental Model
&lt;/h2&gt;&lt;p&gt;The failure usually starts with a quiet assumption:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If a resource is reachable only over private connectivity, it is now a trusted internal service.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That assumption sounds reasonable because private addressing still carries a lot of inherited meaning. It feels contained. Managed. Ours.&lt;/p&gt;
&lt;p&gt;In Azure, that is a dangerous shortcut.&lt;/p&gt;
&lt;p&gt;Private Endpoint changes &lt;strong&gt;where traffic can come from&lt;/strong&gt;. It does not answer whether the source is legitimate, whether the network path is well governed, whether DNS is correct, or whether the service is being reached by something already compromised.&lt;/p&gt;
&lt;p&gt;The deeper problem is not technical misunderstanding. It is classification.&lt;/p&gt;
&lt;p&gt;When teams treat Private Endpoint as a &lt;strong&gt;security control in its own right&lt;/strong&gt;, they start making downstream design decisions as though network privateness is evidence of trust. That is where architecture drifts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;internal paths become loosely trusted&lt;/li&gt;
&lt;li&gt;segmentation becomes harder to justify&lt;/li&gt;
&lt;li&gt;DNS gets treated as plumbing instead of a security dependency&lt;/li&gt;
&lt;li&gt;control-plane permissions become more powerful than the design admits&lt;/li&gt;
&lt;li&gt;monitoring gets weaker because “private” has already been mentally filed under “safe”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is why this is more than semantics. Misclassifying the control changes the system built around it.&lt;/p&gt;
&lt;h2 id="how-it-really-works"&gt;How It Really Works
&lt;/h2&gt;&lt;p&gt;Private Endpoint gives an Azure service a private IP in your virtual network through Azure Private Link. That changes the &lt;strong&gt;reachability path&lt;/strong&gt; to the service.&lt;/p&gt;
&lt;p&gt;It does not make the service inherently trustworthy.
It does not validate the caller.
It does not harden the data path on its own.
It does not replace authentication, authorisation, encryption, governance, or operational verification.&lt;/p&gt;
&lt;p&gt;What it does do is narrow the set of places from which the service can be reached.&lt;/p&gt;
&lt;p&gt;That matters. Exposure reduction is real. Internet-wide scanning, casual probing, and broad unsolicited connectivity become less relevant when access is forced through private network paths. That is a legitimate security improvement.&lt;/p&gt;
&lt;p&gt;But it is still only one kind of improvement.&lt;/p&gt;
&lt;p&gt;The mistake is turning this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Fewer things can reach the service&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;into this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Things that can reach the service are now trustworthy&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Those are completely different claims.&lt;/p&gt;
&lt;p&gt;A more accurate model is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Private Endpoint is an exposure-reduction mechanism&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It is not a trust mechanism&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It is not a security boundary&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It should not be treated as a compensating control for weak architecture elsewhere&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last point matters most. The architectural mistake is not deploying Private Endpoint. The mistake is using it to justify trust decisions it was never designed to support.&lt;/p&gt;
&lt;h2 id="private-endpoint-as-part-of-the-attack-surface"&gt;Private Endpoint as Part of the Attack Surface
&lt;/h2&gt;&lt;p&gt;If you are threat modelling properly, a Private Endpoint should be treated as part of the &lt;strong&gt;internal attack surface&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;That means the question is not “is this service private now?”&lt;/p&gt;
&lt;p&gt;It is closer to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;which networks can route to it?&lt;/li&gt;
&lt;li&gt;which systems can resolve its name?&lt;/li&gt;
&lt;li&gt;which workloads can reach it after compromise?&lt;/li&gt;
&lt;li&gt;which teams or automation can create, approve, or modify the connection path?&lt;/li&gt;
&lt;li&gt;which changes to DNS or endpoint lifecycle would silently alter trust assumptions?&lt;/li&gt;
&lt;li&gt;which telemetry would tell us the endpoint is being used in a way we did not intend?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is a very different posture from “public access disabled, job done”.&lt;/p&gt;
&lt;h3 id="the-dependency-chain-is-the-story"&gt;The dependency chain is the story
&lt;/h3&gt;&lt;div class="mermaid"&gt;flowchart LR
A[Workload or User] --&gt; B[DNS Resolution]
B --&gt; C[Routing to Private IP]
C --&gt; D[Private Endpoint]
D --&gt; E[Private Link Mapping]
E --&gt; F[Azure Service]
G[Peering / Hybrid Connectivity] --&gt; C
H[RBAC / Policy / Approval Workflow] --&gt; D
I[Private DNS Zone Links / Forwarders] --&gt; B
J[Monitoring / Logs / Alerts] --&gt; D
style D fill:#d9edf7,stroke:#31708f,stroke-width:2px
style B fill:#fcf8e3,stroke:#8a6d3b,stroke-width:1px
style H fill:#f2dede,stroke:#a94442,stroke-width:1px
style I fill:#f2dede,stroke:#a94442,stroke-width:1px
&lt;/div&gt;
&lt;p&gt;Private Endpoint is only one component in a chain that includes name resolution, network pathing, control-plane permissions, and ongoing operational ownership.&lt;/p&gt;
&lt;p&gt;An attacker does not need to defeat Private Link itself. They need one viable path through the surrounding system.&lt;/p&gt;
&lt;p&gt;That is why calling Private Endpoint a security control is too generous. It encourages the wrong mental model about where assurance actually comes from.&lt;/p&gt;
&lt;h2 id="real-world-impact"&gt;Real-World Impact
&lt;/h2&gt;&lt;p&gt;This changes design and operations in some concrete ways.&lt;/p&gt;
&lt;h2 id="1-a-compromised-internal-workload-can-still-use-the-private-path"&gt;1. A compromised internal workload can still use the private path
&lt;/h2&gt;&lt;p&gt;A storage account or Key Vault behind a Private Endpoint is still reachable by any compromised workload that can route and resolve to it.&lt;/p&gt;
&lt;p&gt;If you mentally classified the endpoint as “secure because private”, you are more likely to have under-invested in segmentation, workload isolation, or internal traffic scrutiny. The attacker does not need public ingress. They just need to land somewhere that already sits inside the allowed path.&lt;/p&gt;
&lt;p&gt;This is the most common failure of the “private equals safe” assumption: the service is no longer internet-facing, but it is still entirely usable from the wrong place for the wrong reason.&lt;/p&gt;
&lt;p&gt;That should change how you design. A Private Endpoint should be treated as a &lt;strong&gt;sensitive internal destination&lt;/strong&gt;, not a trustworthy one.&lt;/p&gt;
&lt;h2 id="2-dns-becomes-a-security-dependency-not-just-a-networking-detail"&gt;2. DNS becomes a security dependency, not just a networking detail
&lt;/h2&gt;&lt;p&gt;Private Endpoint increases your reliance on private DNS zones, forwarding rules, resolver paths, and link governance.&lt;/p&gt;
&lt;p&gt;That is not background plumbing. It is part of the trust path.&lt;/p&gt;
&lt;p&gt;If DNS ownership is fragmented, forwarding is inconsistent, or zone links are changed without proper control, the architecture can drift without most teams noticing. The service still looks private. The assumptions around how it is resolved and reached may no longer be true.&lt;/p&gt;
&lt;p&gt;This should change how you operate. Name resolution for private services needs explicit ownership, change discipline, and verification. If your design depends on DNS but your operating model treats it as somebody else’s problem, you do not have a complete security design.&lt;/p&gt;
&lt;h2 id="3-control-plane-permissions-can-silently-reshape-trust"&gt;3. Control-plane permissions can silently reshape trust
&lt;/h2&gt;&lt;p&gt;The endpoint itself is a data-plane object, but the trust around it is heavily influenced by control-plane actions.&lt;/p&gt;
&lt;p&gt;Who can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;create a private endpoint&lt;/li&gt;
&lt;li&gt;approve a connection&lt;/li&gt;
&lt;li&gt;link or unlink private DNS zones&lt;/li&gt;
&lt;li&gt;alter policy exemptions&lt;/li&gt;
&lt;li&gt;move workloads into reachable network segments&lt;/li&gt;
&lt;li&gt;change routing or forwarding behaviour&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those permissions matter because they can alter who reaches what, and how, without changing the application code or the service itself.&lt;/p&gt;
&lt;p&gt;If Private Endpoint is treated as a security control, teams often stop short of asking who can mutate the control surface around it. That creates false assurance. The path is private, but the governance over the path may still be loose.&lt;/p&gt;
&lt;p&gt;This should change what you review in design governance. The security question is not just “does this use Private Endpoint?” but “who can change the path, resolution, and approval model around it?”&lt;/p&gt;
&lt;h2 id="how-would-this-change-something-i-design-deploy-or-operate"&gt;How would this change something I design, deploy, or operate?
&lt;/h2&gt;&lt;p&gt;It changes the classification.&lt;/p&gt;
&lt;p&gt;I would not treat Private Endpoint as a control that establishes trust. I would treat it as an &lt;strong&gt;exposure-reduction mechanism&lt;/strong&gt; that still sits inside a broader system of trust decisions.&lt;/p&gt;
&lt;p&gt;That means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;designing internal reachability as something to constrain, not automatically trust&lt;/li&gt;
&lt;li&gt;treating private DNS as part of the security architecture&lt;/li&gt;
&lt;li&gt;reviewing control-plane permissions as part of endpoint risk&lt;/li&gt;
&lt;li&gt;monitoring private-only service access as potentially high-signal, not low-risk&lt;/li&gt;
&lt;li&gt;refusing to let “it’s private” close a design discussion that should still include governance and failure modes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the design review ends at “public access disabled”, it has ended too early.&lt;/p&gt;
&lt;h2 id="implementation-example-verify-the-dependency-surface-not-just-the-endpoint"&gt;Implementation Example: Verify the Dependency Surface, Not Just the Endpoint
&lt;/h2&gt;&lt;p&gt;The useful operational question is rarely “does a Private Endpoint exist?”&lt;/p&gt;
&lt;p&gt;It is more often “what trust and dependency surface now exists because it does?”&lt;/p&gt;
&lt;p&gt;A simple example is enumerating endpoint state and DNS relationships so they can be reviewed as part of operational governance rather than assumed to be correct.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# List private endpoints in a subscription with key governance context&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;az network private-endpoint list &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="se"&gt;&lt;/span&gt; --query &lt;span class="s2"&gt;&amp;#34;[].{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; name:name,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; resourceGroup:resourceGroup,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; location:location,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; subnet:id,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; connections:privateLinkServiceConnections[].privateLinkServiceId,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; nics:networkInterfaces[].id,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; provisioningState:provisioningState
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; }&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="se"&gt;&lt;/span&gt; -o table
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# List Private DNS zone links for review&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;az network private-dns link vnet list &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="se"&gt;&lt;/span&gt; --resource-group &amp;lt;dns-rg&amp;gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="se"&gt;&lt;/span&gt; --zone-name &amp;lt;private-zone-name&amp;gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="se"&gt;&lt;/span&gt; -o table
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This is not presented as a deployment step. It is a reminder that the operational surface includes more than the endpoint object itself. If you cannot quickly identify which endpoints exist, which networks they live in, and how DNS is linked around them, you are relying on privateness more than you are governing it.&lt;/p&gt;
&lt;h2 id="gotchas--edge-cases"&gt;Gotchas &amp;amp; Edge Cases
&lt;/h2&gt;&lt;h3 id="private-only-can-reduce-visibility-if-teams-are-not-deliberate"&gt;Private-only can reduce visibility if teams are not deliberate
&lt;/h3&gt;&lt;p&gt;Some organisations monitor internet-facing services heavily and assume private paths are lower risk. That can create a blind spot where the more sensitive destination gets less scrutiny simply because it is no longer public.&lt;/p&gt;
&lt;h3 id="internal-often-spans-more-than-one-trust-domain"&gt;“Internal” often spans more than one trust domain
&lt;/h3&gt;&lt;p&gt;Peering, hub-and-spoke topologies, hybrid connectivity, and shared platform services can all widen effective reachability. If your architecture talks about “the internal network” as though it were one trust zone, you are already abstracting away the problem.&lt;/p&gt;
&lt;h3 id="exposure-reduction-can-still-be-the-right-call"&gt;Exposure reduction can still be the right call
&lt;/h3&gt;&lt;p&gt;None of this is an argument against Private Endpoint. Reducing public exposure is often sensible and worthwhile. The warning is about over-claiming what that decision buys you.&lt;/p&gt;
&lt;h2 id="best-practices"&gt;Best Practices
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Treat Private Endpoint as &lt;strong&gt;exposure reduction&lt;/strong&gt;, not trust establishment&lt;/li&gt;
&lt;li&gt;Model private DNS as a security dependency with explicit ownership&lt;/li&gt;
&lt;li&gt;Review which internal networks and workloads can actually reach private endpoints&lt;/li&gt;
&lt;li&gt;Include control-plane permissions and approval paths in the threat model&lt;/li&gt;
&lt;li&gt;Monitor access to private-only services as part of your internal attack surface&lt;/li&gt;
&lt;li&gt;Challenge any design language that treats “private” as equivalent to “trusted”&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="insight"&gt;
&lt;div class="insight-icon"&gt;🍺&lt;/div&gt;
&lt;div class="insight-content"&gt;
&lt;strong&gt;Brewed Insight:&lt;/strong&gt; Private Endpoint is valuable precisely because it reduces exposure. The trouble starts when that benefit gets inflated into a trust claim. Once “private” becomes shorthand for “safe”, the rest of the architecture starts inheriting confidence it never earned.
&lt;/div&gt;
&lt;/div&gt;
&lt;style&gt;
.insight {
display: flex;
align-items: center;
background-color: #0089e41c;
border-left: 10px solid #D69A2D;
padding: 10px;
margin: 20px 0;
border-radius: 4px;
}
.insight-icon {
font-size: 24px;
margin-right: 10px;
}
.insight-content {
flex: 1;
}
&lt;/style&gt;&lt;h2 id="learn-more"&gt;Learn More
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/azure/private-link/private-endpoint-overview" target="_blank" rel="noopener"
&gt;What is Azure Private Endpoint?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/azure/private-link/private-link-overview" target="_blank" rel="noopener"
&gt;What is Azure Private Link?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/azure/dns/private-dns-overview" target="_blank" rel="noopener"
&gt;Azure Private DNS documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/azure/architecture/reference-architectures/hybrid-networking/hub-spoke" target="_blank" rel="noopener"
&gt;Azure architecture guidance for hub-spoke network topology&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>