Ask any enterprise team standing up Microsoft Foundry in production and you'll hear the same non-negotiable: the agent can't run on the open internet. The moment it touches proprietary data, internal APIs, or regulated workloads, security wants it inside the company's own virtual network — behind private endpoints, a central firewall, and controlled DNS. That configuration — a Foundry Standard Agent injected into a Bring-Your-Own (BYO) VNet — is the topology most large organizations actually ship to production. It's also where first deployments quietly stall.
The reason is counterintuitive. Deploying the agent is, in the end, just a Bicep or Terraform template — the easy 20%. The hard 80% is the network design you lock in before you run it, and it's unforgiving in two ways the template isn't. Several of the choices are irreversible: outbound network injection and the agent subnet can't be changed after deployment, and subnets can't be resized in place — a wrong call on day one means a redeploy, not a tweak. And the real lead time is organizational, not technical: firewall change requests, IP allocation from central IPAM, NSG approvals, and Private DNS ownership in a hub-and-spoke model each take days to weeks to clear. Discover a requirement late and you're blocked on a ticket, not a template.
This guide is written to prevent that. It's for the people who own the groundwork — the platform / landing-zone architect and the network security team, with the AI platform lead who runs the deployment as the second reader. The goal is simple: that you can walk into your first planning meeting already knowing every subnet, CIDR, DNS zone, NSG rule, and firewall FQDN you need to request. Get the subnet layout, the Private DNS design, and the firewall allow-list right, and the template becomes the formality it should be.
Every enterprise conversation I have about Foundry Standard Agents in a Bring-Your-Own (BYO) VNet opens with the same handful of questions. They're the right questions — and each one is a design decision, not a troubleshooting step:
- How many subnets do I need, and what does each one look like?
- What CIDR should I reserve for the agent subnet?
- Which FQDNs must I allow on my central firewall?
- Where do the Private DNS zones live?
- And how do I prove it's all correct before the first agent call?
The sections below answer each one as a decision you make up front. Get the subnet layout, the Private DNS design, and the firewall allow-list right, and the template becomes the formality it should be.
Scope: Microsoft Foundry Standard setup (account + project + capability host) injected into a customer VNet, optionally fronted by API Management, optionally behind a hub-and-spoke Azure Firewall with central Private DNS.
Who is this for?
| Reader | What they own | What they get from this guide? |
| Cloud platform / landing-zone architect | Subnets, IPAM, Private DNS, hub-and-spoke topology | The exact subnet, CIDR, and DNS design to request and provision |
| Network security lead | NSGs, Azure Firewall policy, UDRs | The firewall allow-list and NSG rule set to approve in one pass |
| AI platform lead / solution architect | The Foundry deployment itself |
A complete, precise ask to hand the network team on day one |
How to use this guide: the pre-deploy checklist in section 7 is the deliverable. Sections 1–6 are the reasoning behind each checkbox — read them once, then work from the checklist.
1. Co-locate everything in one region
There is no hard platform requirement forcing all the pieces of a Foundry Standard deployment to live in the same Azure region. But once you inject the agent runtime into your VNet, spreading resources across regions costs you two things you don't want to pay for:
- Cross-region data-transfer charges — if Foundry is in region A but Cosmos, Search, Storage, or your model endpoint sits in region B, every agent call traverses inter-region VNet peering, which is billed per GB in both directions. Keep everything in one region and that traffic stays local — no inter-region peering, no per-GB data-transfer bill, and a materially lower cloud spend.
- Latency — agent runs are chatty (thread reads, tool calls, vector lookups, model calls). Adding 40–100 ms per hop across regions is felt immediately in user-facing responses.
Rule of thumb: the Foundry account, project, capability host, model deployments, dependent stores (Cosmos, Storage, Search), APIM (if used), the VNet, and the jumpbox should all live in the same region. Use paired regions only for DR — not for day-1 architecture.
2. Subnet structure — plan the layout before you touch a template
Before you copy the table — your SKU and landing zone change the shape of it. The layout below is the recommended pattern for production workload (Premium APIM + a jumpbox), but two choices materially move subnets in or out of it:
- APIM SKU drives whether you need one subnet or two. Standard v2 uses VNet integration for outbound and a separate Private Endpoint for inbound — two distinct subnets with two distinct roles (apim-outbound-subnet plus the inbound PE on pe-subnet). Premium (classic) supports full VNet injection, where a single delegated subnet carries both ingress and egress — you get higher throughput, multi‑region, availability zones, and one less subnet to plan for, at a higher price point. Pick the SKU first; the subnet count follows.
- The jumpbox subnet is landing‑zone dependent. If your enterprise landing zone already gives operators direct line of sight into the spoke — ExpressRoute, a site‑to‑site VPN — you don't need a dedicated jumpbox-subnet here at all. Add it when you're onboarding a partner team, remote contractors, or anyone who doesn't already have private routing into the VNet and needs a controlled entry point for nslookup / portal validation.
The most common mistake I see is treating the VNet as a single flat subnet. Foundry Standard + APIM has hard constraints that push you to at least four purpose-built subnets:
|
Subnet |
Purpose |
Delegation |
Notes |
|
agent-subnet |
Foundry capability-host injects NICs here for the agent runtime |
Microsoft.App/environments (required) |
Must be empty of Private Endpoints — delegation and PE NICs cannot coexist |
|
pe-subnet |
Private Endpoints for Cosmos, Storage, Search, Foundry account, and APIM inbound |
None |
PE subnets cannot be delegated |
|
apim-outbound-subnet |
APIM VNet integration — the egress path from APIM to your private backends |
Microsoft.Web/serverFarms (required) |
Dedicated to a single APIM instance; cannot be shared |
|
jumpbox-subnet |
VM(s) for RDP/SSH access, nslookup validation, private portal browsing |
None |
Keep it separate from pe-subnet — different NSG posture, different UDR needs, and you don't want VM churn eating PE address space |
What about extra workloads in the same VNet? The table above is the Foundry + APIM baseline. If you host anything else alongside — MCP servers, custom APIs, background workers — give each its own dedicated subnet. It cannot share agent-subnet (exclusive to one Foundry account) or pe-subnet (can't be delegated), and its delegation depends on the host: Container Apps → Microsoft.App/environments (a separate environment from Foundry's), Container Instances → Microsoft.ContainerInstance/containerGroups, App Service / Functions with VNet integration → Microsoft.Web/serverFarms, AKS / plain VMs → no delegation.
Recommended APIM topology — Premium v2, PE for inbound + integration for outbound
APIM has several supported networking modes, but for a production Foundry setup where you need to isolate both the inbound gateway and outbound calls to private backends, the pattern that gives you the cleanest topology is:
- SKU: API Management Premium v2. Premium v2 gives you availability zones, higher throughput, workspaces, dedicated compute, and the highest scale ceiling — things you want on the production path for an AI Gateway sitting in front of Foundry. Standard v2 supports the same networking model, but Premium v2 is what you deploy when this API surface is a first-class production dependency.
- Inbound = Private Endpoint on pe-subnet. Create a private endpoint for APIM against the Microsoft.ApiManagement/service Gateway sub-resource. This registers into privatelink.azure-api.net automatically and gives you a private inbound IP without any subnet delegation. Because PE subnets can't be delegated anyway, this is exactly where APIM's inbound belongs — next to your other PEs.
- Outbound = VNet integration into apim-outbound-subnet. Enable Premium v2 VNet integration so APIM's calls to your backends leave through a dedicated subnet delegated to Microsoft.Web/serverFarms. From here, APIM reaches Foundry, Cosmos, an internal HR API, etc. via their PEs — traffic never touches the internet.
Why the delegations matter
- Microsoft.App/environments on the agent subnet formally hands the subnet over to the Container Apps managed environment fabric that Foundry's capability-host runs on. Delegation grants the platform permission to provision and manage its own infrastructure inside the subnet on your behalf (NICs, load balancer, health probes, required routing policies) and makes the subnet exclusive to that service — no PEs, no VMs, no other workloads can share it. Without the delegation, capabilityHosts PUT fails subnet validation.
- Microsoft.Web/serverFarms on the APIM outbound subnet is required for Premium v2 (or Standard v2) VNet integration. The subnet is dedicated — a single APIM instance owns it and it cannot be shared with any other Azure resource. Also make sure the Microsoft.Web resource provider is registered in the subscription.
- Delegations are not fungible: you cannot repurpose the agent subnet as a PE subnet later, because a subnet cannot simultaneously host delegated services and Private Endpoints. And per official Foundry guidance, outbound network injection cannot be changed after deployment — pick the agent subnet carefully because moving it later means redeploying Foundry.
Agent subnet sizing. The Foundry Agent Service networking doc is explicit on this: the recommended size of the delegated agent subnet is /24 (256 addresses), precisely because the subnet is delegated to Microsoft.App/environments and the Container Apps runtime consumes IPs as it scales out. /27 is the API‑enforced hard floor and is fine for smaller, non‑production deployments — PoCs, dev/test, or low‑traffic internal agents where you know the scale ceiling. Treat `/27` as a lower bound the platform accepts, not a size to deploy production with.
On top of that, you cannot resize a subnet in place — if you run out, moving is destructive. Provision /24 from day one; it costs you nothing extra and removes an entire class of future scaling incidents.
APIM outbound subnet sizing. Premium v2 VNet integration requires a minimum of /27 and recommends /24 for scale-out headroom — same rationale, err on the generous side.
3. Hub-and-spoke with Azure Firewall — model the traffic before you write rules
If you sit behind a centralized Azure Firewall with a UDR forcing 0.0.0.0/0 through it, every hostname the runtime touches must be in your application rule collection. Before deploying, it helps to model the traffic in two buckets:
- In-VNet, service-to-service traffic — Foundry runtime → Cosmos / Storage / Search / your model endpoint / APIM. Because you're deploying with private endpoints in front of every one of those services, this traffic is outbound to a private IP inside the VNet, not outbound to a public FQDN. It also does not traverse the firewall by default — the /32 system route Azure injects for each PE beats your 0.0.0.0/0 UDR, so PE traffic goes straight to the PE NIC. Your NSG rules should reflect that: allow agent-subnet → pe-subnet on 443, and you're done for the data plane. No firewall application rule needed for it.
- Outbound to Microsoft-managed control-plane FQDNs — Entra token acquisition, managed identity, container image pulls, evaluations telemetry, etc. This is what the firewall application rule collection is really for.
Source addresses to whitelist on the firewall
- agent-subnet — the runtime originates almost every control-plane call from here.
- jumpbox-subnet — if you provisioned a dedicated jumpbox-subnet to reach the platform for `nslookup`, portal browsing, and troubleshooting.
FQDNs — start with the documented baseline
The official Foundry private-link doc publishes this minimum allow-list for Foundry with virtual network injection:
|
Scenario |
FQDNs / Service Tag |
Why |
|
Agents |
*.identity.azure.net, login.microsoftonline.com, *.login.microsoftonline.com, *.login.microsoft.com (or the AzureActiveDirectory service tag) |
Required for the Azure Container App delegation that hosts the agent runtime — Entra + managed identity |
|
Evaluations & Traces |
AzureMachineLearning service tag, settings.sdk.monitor.azure.com |
Evaluators catalog + shipping results to Application Insights Prefer the service tag over a raw `*.blob.core.windows.net` wildcard — it scopes egress to just the Microsoft‑managed storage endpoints Foundry actually needs, without opening every blob account in Azure. |
Start there. If your setup is only Foundry + PEs + a firewall, that list is usually enough.
FQDNs — additions you'll typically need in practice
If you're also running APIM (Standard v2 outbound integration), pulling container images, or using Application Insights end-to-end, expect to add:
|
Category |
FQDNs |
When you need it |
|
Azure Resource Manager |
management.azure.com, *.management.azure.com |
Runtime ARM calls (role assignment reads, resource metadata) |
|
Microsoft Graph |
graph.microsoft.com |
Directory / user context in some agent scenarios |
|
Container image pulls |
mcr.microsoft.com, *.data.mcr.microsoft.com, *.azurecr.io |
Cap-host pulls its runtime image |
|
APIM internal telemetry |
*.prod.warm.ingest.monitor.core.windows.net, *.prod.hot.ingest.monitor.core.windows.net, *.prod.microsoftmetrics.com, azureprofiler.trafficmanager.net |
Only if firewall Deny logs show APIM's outbound subnet trying to reach Geneva/monitoring endpoints — in most Premium v2 setups this stays on the Microsoft backbone and never hits the firewall |
|
Application Insights ingestion |
*.in.applicationinsights.azure.com, *.monitor.azure.com |
If your app or APIM sends telemetry to App Insights |
Note : Wildcards are the pragmatic starting point; PEs on the premium SKUs are the hardening step for regulated / high‑sensitivity environments where a broad IP pool on the firewall is a compliance finding. Prefer premium SKUs with dedicated Private Endpoints over wide wildcards on the firewall. Several services on this list only expose public multi‑tenant endpoints on their lower tiers, which is why you end up whitelisting broad wildcards like `*.data.mcr.microsoft.com`, `*.monitor.azure.com`, or `*.in.applicationinsights.azure.com` — each one opens a large pool of shared Microsoft IPs to your egress. The premium tiers give you a way out:
Why *.blob.core.windows.net/AzureMachineLearning Service Tag shows up even when you don't use blob storage: several Microsoft-managed services (App Insights, Foundry evaluators, Container Apps image cache) round-trip through their own storage accounts. The official allow-list already includes it for Evaluations & Traces.
Design for iteration
Enable Azure Firewall diagnostic logs from day one and target a Log Analytics workspace. You'll want to filter Deny events by source subnet + target FQDN to close any remaining gaps in your allow-list — but you'll be iterating on a short delta, not building the list from scratch.
4. Private DNS — the highest-value decision to get right first
More BYO-VNet Foundry incidents trace back to DNS than to any other single component — which is exactly why it's worth designing first. Two rules will save you most of them:
Rule 1: Never have duplicate Private DNS zones
If privatelink.blob.core.windows.net exists in both your hub and your spoke, VNet resolution becomes non-deterministic — you'll intermittently resolve to the wrong private IP (or worse, to 0.0.0.0). Pick one home for each zone and delete duplicates.
Rule 2: In a hub-and-spoke, PDZs belong in the hub
- Create each of the seven privatelink.* zones once, in the hub.
- Set every spoke VNet's DNS servers to the firewall private IP (Azure Firewall with DNS Proxy enabled). Spoke query → firewall DNS proxy → Azure DNS (168.63.129.16) resolved in the hub context → hub-linked PDZ → returns the PE IP. No PDZ link on the spoke is needed for this to work.
The seven zones Foundry Standard needs: privatelink.services.ai.azure.com, privatelink.openai.azure.com, privatelink.cognitiveservices.azure.com, privatelink.search.windows.net, privatelink.blob.core.windows.net, privatelink.documents.azure.com, privatelink.azure-api.net
Force traffic through the firewall: VNet peering alone will not send spoke egress through the hub firewall — Azure routes prefer the peering directly. You need a UDR on every spoke subnet with a 0.0.0.0/0 route pointing at the firewall private IP. Without the UDR, your FQDN allow-list is enforced on nothing.
5. Network Security Groups — the minimum rule set
NSGs are the layer where a single missing outbound rule silently blocks the data plane — everything provisions cleanly, but nothing talks. Think of NSGs in the same two-bucket model from section 3 — service-to-service inside the VNet, and outbound control plane.
- Allow outbound HTTPS (TCP 443) from agent-subnet and apim-outbound-subnet to the pe-subnet CIDR (or, if you prefer, to the relevant service tags — AzureCosmosDB, Storage.<region>, AzureCognitiveSearch, AzureAD). This covers the runtime's calls to Cosmos, Storage, Search, and the Foundry / model endpoints via their private endpoints.
- Allow APIM's outbound dependencies — at minimum outbound TCP 443 to the Storage and AzureKeyVault service tags from apim-outbound-subnet, per the APIM outbound integration NSG reference. Missing these is the classic "APIM went 'Failed' in the portal" symptom.
- Allow RDP/SSH from the firewall subnet (not from the internet) on the jumpbox-subnet, and use firewall DNAT to expose it — no public IPs on VMs.
- Default-deny outbound to internet on the agent, PE, and APIM subnets; let only the firewall route escape via UDR. This ensures every FQDN call goes through your allow-list, not around it.
6. Validate before you write a single line of agent code
Before you call agents.create() from your app, prove the network is right. From your jumpbox:
1. DNS resolves privately from the spoke
nslookup <foundry-account>.services.ai.azure.com
nslookup <search-name>.search.windows.net
nslookup <cosmos-name>.documents.azure.com
nslookup <apim-name>.azure-api.net
Each should return a 10.x / 192.168.x IP (your PE), not a public IP.
If the jumpbox resolves privately but the agent subnet does not, your PDZ VNet links or your UDR are wrong. The agent subnet must resolve the same private IPs as the jumpbox — otherwise the runtime will egress to public IPs and get 403'd by your PE-only firewall on Cosmos.
The same source-IP principle applies to firewall rules: whatever subnets you validated from must be listed as sourceAddresses in the application rule collection, alongside every FQDN they need to reach.
7. A pre-deploy checklist
Walk this list before you kick off the deployment — every item is cheaper to fix now than after capabilityHosts PUT is in flight.
- Agent subnet delegated to Microsoft.App/environments — sized /24 per Microsoft's official recommendation (/27 is the API-enforced hard floor, not a target).
- Decided whether agent tools run behind the VNet — and picked the matching template (standard vs. tools-behind-VNet) accordingly.
- APIM inbound is via a Private Endpoint on pe-subnet and APIM outbound is via VNet integration into a dedicated apim-outbound-subnet delegated to Microsoft.Web/serverFarms, sized /24 (min /27).
- The privatelink.* PDZs (including privatelink.azure-api.net) exist once (in the hub, if hub-and-spoke) and are linked to the hub VNet only; spokes resolve them via the firewall DNS proxy.
- Spoke VNet DNS = firewall private IP; firewall policy has DNS proxy enabled.
- A UDR on every spoke subnet forces 0.0.0.0/0 → firewall.
- Firewall application rules include the documented Foundry baseline (§3) plus additions for your extras (container pulls, App Insights), with the agent and jumpbox subnets as source addresses. (Only add apim-outbound-subnet if Deny logs show traffic from it.)
- NSGs allow outbound 443 from agent + APIM outbound subnets to the PE subnet CIDR (or relevant service tags), and APIM's Storage / AzureKeyVault outbound rules per the APIM docs.
- nslookup from both jumpbox and agent subnet returns private IPs for Foundry, Cosmos, Storage, Search, APIM.
Closing thought — decide first, deploy second
Foundry Standard on a BYO VNet isn't harder than any other network-isolated Azure workload. It's just the intersection of five product surfaces — Foundry, APIM, Cosmos, Search, and Storage — each with its own private-networking quirks. What makes it feel hard is deploying first and discovering the constraints second.
Reverse that order and it's straightforward. Before you run a single template:
- Lock the subnet layout and delegations — the agent subnet is irreversible, so size it /24 from day one.
- Design Private DNS once, in the hub , and force spoke egress through the firewall with UDRs.
- Assemble the firewall allow-list and NSG rules as a change request your network team can approve in one pass.
- Validate DNS from both the jumpbox and the agent subnet before any agent code exists.
- Run the whole thing from the pre-deploy checklist — that list is the deliverable; everything above is the reasoning behind it.
Get those right and the template is a formality. Get them wrong and you're redeploying. That's the whole reason to think network-first.
Where to go next
- Official Microsoft sample templates (Bicep + Terraform) for the exact setup described here — the microsoft-foundry/foundry-samples repo, infrastructure/ folder: https://github.com/microsoft-foundry/foundry-samples/tree/main/infrastructure
- 15-private-network-standard-agent-setup — Standard Agent with end-to-end network isolation (tools not behind the VNet).
- 16-private-network-standard-agent-apim-setup — the APIM-fronted topology.
- 19-private-network-agent-tools — use this when agent tools must also stay inside the VNet.
- Foundry Agent Service limits, supported tools, regions, and troubleshooting — what's allowed, what isn't, which tools and models are supported, and how to handle limit/quota errors end to end: https://learn.microsoft.com/en-us/azure/foundry/agents/concepts/limits-quotas-regions
- Set up private networking for Foundry Agent Service (Bicep / Terraform / azd walkthrough) and the companion Deep dive into Foundry Agent Service networking for subnet sizing and IP allocation: How to configure network isolation for Microsoft Foundry - Microsoft Foundry | Microsoft Learn, https://learn.microsoft.com/en-us/azure/foundry/agents/how-to/virtual-networks