From Token to Packet: Trace an Azure Workload Attack Path

Trace an Azure workload's identity authority, DNS resolution, and network reachability as separate edges, then test the complete path without overstating what one probe proves.

An application needs to read from Azure Storage. The team uses managed identity, puts a Private Endpoint in place, and restricts public access.

Those are sensible choices. They also make the design surprisingly easy to stop questioning.

The identity review checks the permissions. The network review checks private connectivity. Each team can approve its part without answering what happens if the application itself becomes hostile. After all, reading data is what this workload was built to do.

The uncomfortable question isn’t whether those controls exist. It’s whether a compromised process can use them to do more than the business dependency requires.

That is where the review needs to follow a request, rather than collect another approval.

The Mental Model

The common assumption is that a narrowly permitted identity and a private network path add up to a contained workload.

They might. But the identity and network diagrams don’t establish that on their own. The application may select a different identity, resolve a different destination, or have access to more data than its owners intended.

Consider an application on an Azure VM reading a blob through a Blob private endpoint. This is an illustrative design, not a tenant observation. Its authority and its packet path meet at the service:

flowchart LR W[Workload process on VM] -->|requests token as| I[Managed identity] W -->|resolves account FQDN through| D[DNS resolver] D -->|returns destination address| P[Private endpoint IP] W -->|TCP 443 through route and policy| P P --> S[Blob service] I -->|data permission evaluated for request| S

What matters in this diagram is the join. The permission must belong to the principal making the request, and the route must lead to the destination the client actually uses. Otherwise, the review is describing two different things and calling them one boundary.

A permission review and a connectivity test only describe an attack path if they describe the same workload request.

Private connectivity can narrow exposure without narrowing the data available to an authorized process. Managed identity can remove stored credentials without making that process trustworthy. Neither control has failed when a compromised application uses its legitimate access. The architecture may simply have allowed too much.

How It Really Works

“Can access storage” is a poor starting point because it hides the operation. Reading a blob, listing a container, and changing account configuration are different decisions. For this example, use a read-only request against known, non-sensitive test data.

The identity in the diagram might not be the identity in the request

A role assignment is easy to inspect. Establishing that it belongs to the identity the application actually uses takes more care. A user-assigned identity may serve several workloads; a system-assigned identity is tied to its resource. Sharing an identity can make deployment easier while making the incident boundary larger than the application boundary.

For Blob access using Microsoft Entra ID, token acquisition and data authorization are separate steps. The Blob service still has to accept the operation. Inspect the applicable data permissions, inherited assignments, and conditions, then make the request using the workload’s credential selection.

This is why an engineer’s successful az login test can be so unhelpful in a workload review. It proves something about the engineer’s access. The application could still be using another principal entirely.

Follow the name before following the route

The application normally keeps using the service hostname. Private DNS configuration should lead it to the intended private endpoint address. Whether it does depends on the resolver, zone links, forwarding, and any local overrides.

A lookup from a management VM elsewhere in the network can look reassuring and still tell you little about the application’s resolver path. Use the workload’s runtime context and capture the address the client uses, not just the answer from a separate lookup. Caches and proxies are part of the explanation, not troubleshooting details to discard once a private IP appears.

Now test TCP 443 from that same source. Network Watcher’s Connection troubleshoot can help investigate supported sources and destinations, including route, DNS, and network-security issues. It cannot tell you whether the Blob service accepts this identity’s read request. You still need that request.

Be precise about which Azure behaviour you rely on

Microsoft documents the managed identity, Blob authorization, private DNS, and Network Watcher mechanisms linked below. Those documents support the method; they are not evidence that your tenant behaves as intended.

For this example, the target is an Azure Resource Manager-deployed Storage account’s Blob service over HTTPS/TCP 443. A real test record needs the Azure region, VM and Storage SKUs, runtime/SDK versions, and resolver configuration. There is no deployed lab or measured result behind this example.

When inspecting conditional RBAC assignments through the Role Assignments REST API, use 2022-04-01 or a later supported version and examine relevant parent scopes and role definitions. For Network Watcher, check regional enablement, supported sources, diagnostic prerequisites, and required RBAC actions. Its documented agentless experience is preview. Don’t build an incident dependency around an assumed feature.

The workload’s Blob read permission is separate from the investigator’s assignment-inspection and diagnostic permissions. No Storage or Network Watcher REST request or log query is supplied here; pin those API versions and telemetry schemas when an implementation depends on them.

Real-World Impact

This changes what a design review is prepared to accept. A working application is evidence that its dependency works; it is not evidence that the dependency is appropriately bounded.

Suppose the test blob can be read, but the connection goes to a public address when the design calls for private access. The request succeeded. The intended path did not. Calling that a pass would hide a DNS or client-routing assumption that needs investigation.

The opposite result is just as easy to misread. Private connectivity works, but Blob access is denied. That isn’t proof of containment until you know why. The test could be using the wrong identity, asking for the wrong operation, or hitting a permission that hasn’t propagated.

Keep the explanation attached to the path. A small record is more useful here than a control inventory:

FieldWhat to record
ClaimThis process, using this principal, can read this test blob through this private endpoint
Source and identityRuntime/compute resource, principal ID, credential selection
DestinationService hostname, resolver used, DNS answer, client-used address, TCP port
AuthorityRole definition, assignment scope, conditions, requested data operation
ResultTimestamp, request outcome/error, network diagnostic result, evidence reference
After a changeExact change, comparable retest, remaining paths and unanswered questions

This is a template, not a populated evidence pack. Use it in an authorized environment with non-sensitive test data; do not store bearer tokens.

An unexpected destination disproves the claimed private path. A denied read means the operation didn’t succeed in that test; it does not, without an explanation, establish a durable security boundary.

For the before/after test, choose a second, non-sensitive blob that this workload should not read. The required read should succeed and the prohibited read should fail for an understood authorization reason. Repeat both after a scoped permission change. If both fail, you’ve broken the dependency; you haven’t demonstrated useful containment. If both succeed, the proposed boundary hasn’t held. These are expected outcomes for a test design, not reported measurements.

“Access stopped” is a poor success criterion when the application stopped with it. The defensible outcome is more specific: the required dependency still works, and the unnecessary access tested no longer does.

Gotchas and Edge Cases

A path test can give a very convincing answer to the wrong question. Source context, timing, and operational dependencies are where that usually becomes visible.

A test from the host can miss the application

Containers, proxies, and different resolver settings can make the application behave differently from a shell on its VM. Test inside the process’s actual context where practical. Where that isn’t possible, document the difference instead of quietly treating the host as equivalent.

Removing a permission is not the same as ending access immediately

After an authorization change, repeat the operation and account for service-specific propagation and caching. Existing connections and fresh connections may behave differently too. A diagram showing an edge removed is a change record; the retest is what tells you whether the intended outcome has taken effect.

Tighter boundaries have operating costs

A private path introduces DNS and connectivity dependencies. Narrower roles need maintenance when the application changes. Neither is a reason to abandon them, but both need owners.

There is also a temptation to grant broad investigator permissions so the diagnostics run without friction. That creates another powerful identity to govern. Prefer the permissions the diagnostic actually needs, with a pre-agreed escalation path for incidents, rather than solving investigation inconvenience with unrelated write authority.

Best Practices

Start with a dependency whose compromise would matter. Work through its authority and connectivity together, using non-sensitive test data in an authorized, preferably disposable environment. Name the test owner and cleanup requirements, including any temporary permissions.

Bring the identity and network evidence into the same review. Where the results disagree, leave the question open until someone can explain it. A failed connection with broad permissions still deserves an authority review; a reachable service with a denied read still deserves scrutiny of the other permitted operations.

The review should produce something the next change can invalidate. If an identity assignment, resolver path, route, or firewall rule changes, revisit the affected dependency. A “verified” diagram that survives every infrastructure change without a retest isn’t assurance. It’s an old diagram.

There is no complete-estate assurance claim in one VM-to-Blob test. What you have is a defensible answer for one path, plus a method to investigate the next.

🍺
Brewed Insight: Before signing off a workload boundary, ask for the request that crosses it. Who made it, where did it go, what did it do, and what stopped it after the change? Separate approvals aren’t a substitute for that answer.

Learn More