> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aviatrix.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Secure Microsoft Foundry Agents with a Standalone Gateway

> Apply Aviatrix AgentGuard containment to Microsoft Foundry agent workloads in BYOVNet mode using a standalone spoke gateway — no Aviatrix Enterprise Transit required. Covers spoke gateway insertion, UDR enforcement, ACA runtime exclusions, and selective TLS inspection.

When complete, all outbound traffic from the Microsoft Foundry delegated agent
subnet will route through a standalone Aviatrix spoke gateway before leaving the
VNet. Default-deny DCF policy permits only the Azure Container Apps runtime
requirements and declared tool destinations, with selective optional TLS
decryption on tool-call traffic and explicit bypass for Azure control-plane
paths.

This guide covers the standalone topology: a single Aviatrix spoke gateway
deployed directly inside the Microsoft Foundry VNet, with no Aviatrix Enterprise
Transit required. It is the minimum viable Aviatrix AgentGuard footprint for
Microsoft Foundry egress security and a natural on-ramp — the standalone spoke
gateway can be attached to an Enterprise Transit later as your network footprint
grows, at which point you can add East-West containment across the transit
fabric. If you already have Enterprise Transit in place, see <a href={"/docs/enterprise/" + "10.1" +
"/guides/security/egress/ai-agent-containment-azure-foundry-transit"}>Microsoft
Foundry: With Transit</a>.

For the business case and architecture overview, see <a href={"/docs/enterprise/" + "10.1" +
"/solutions/security-for-ai/ai-agent-workload-security"}>AI Agent Workload
Containment</a>.

## Prerequisites

* Aviatrix Controller 8.2 or later
* <a href={"/docs/enterprise/" + "10.1" + "/concepts-architectures/components/controlplane/copilot-overview"}>CoPilot</a>
  4.31 or later
* <a href={"/docs/enterprise/" + "10.1" + "/guides/security/dcf/dcf-enable-feature"}>DCF enabled</a>
  with **Log Enrichment** turned on
* A <a href={"/docs/enterprise/" + "10.1" +
  "/guides/platform-administration/cloud-account-create"}>cloud account
  onboarded in CoPilot</a> for the Azure subscription hosting the Microsoft
  Foundry workload
* Microsoft Foundry deployed in **BYOVNet mode** (Standard Setup with private
  networking); the managed VNet deployment has no user-accessible enforcement
  point

## How it works

Microsoft Foundry BYOVNet mode places agent compute (Azure Container App
compute) into a subnet you provide, delegated to `Microsoft.App/environments`.
All outbound tool-call traffic from that subnet routes through Azure's default
path unless a User Defined Route (UDR) redirects it. Adding a UDR that sends
`0.0.0.0/0` to the Aviatrix spoke gateway's private IP puts all agent egress
in-path for DCF enforcement.

The spoke gateway in this topology is not connected to an Aviatrix Transit. It
enforces internet egress containment directly: the agent can only reach
destinations declared in its DCF allow-list, and everything else is denied and
logged. East-West isolation between the Microsoft Foundry VNet and other
workload VNets is handled by Azure VNet peering policy and is outside the scope
of this guide; connect the spoke to an Enterprise Transit to bring East-West
containment under Aviatrix policy.

Internal resource traffic (connections to Azure Storage, Cosmos DB, AI Search,
and Key Vault) routes through private endpoints inside the VNet and stays on RFC
1918 addresses. This traffic never traverses the spoke gateway.

DCF policy uses two mechanisms to preserve ACA runtime compatibility. Azure
Container Apps platform traffic (container registry pulls, managed identity
token acquisition, ACA infrastructure calls) is permitted with
`DECRYPT_NOT_ALLOWED` via rules scoped to Azure Service Tag SmartGroups; the
exclusion set stays accurate automatically as Microsoft updates underlying IP
ranges. Tool-call traffic to approved destinations is permitted with optional
`DECRYPT_ALLOWED`, giving DCF URL-path visibility for supply-chain risk
scenarios.

<Warning>
  The subnet delegation to `Microsoft.App/environments` must be in place
  **before** the Microsoft Foundry project is deployed. The Microsoft Foundry
  capability host binds to the subnet at creation time and cannot be changed
  afterward. However, the UDR applied to that subnet can be updated at any time,
  including by the Aviatrix controller during spoke gateway HA failover.
</Warning>

## Procedure

<Tabs>
  <Tab title="CoPilot UI">
    <Steps>
      <Step title="Prepare the agent subnet">
        Before deploying the Microsoft Foundry project, ensure the delegated agent
        subnet exists in the target VNet.

        1. In the Azure Portal, navigate to your VNet and select **Subnets**.

        2. Create a subnet with the following configuration:

           | Field                    | Value                                                           |
           | ------------------------ | --------------------------------------------------------------- |
           | **Name**                 | A name for the agent subnet, for example `foundry-agent-subnet` |
           | **Subnet address range** | A /24 CIDR within your VNet range, for example `10.0.1.0/24`    |
           | **Subnet delegation**    | `Microsoft.App/environments`                                    |

        3. Confirm the subnet shows delegation to `Microsoft.App/environments` before
           proceeding.

        <Note>
          Use a /24 CIDR for production workloads. A /26 is the minimum for 50
          concurrent agent sessions. The recommended size provides headroom for platform
          upgrades and scaling events that temporarily increase IP consumption.
        </Note>
      </Step>

      <Step title="Deploy an Aviatrix spoke gateway in the cluster VPC">
        An Aviatrix spoke gateway in the same VPC as your Kubernetes cluster is the
        enforcement point where the Aviatrix controller applies containment policy to
        MCP server egress traffic.

        1. In CoPilot, navigate to **Cloud Fabric > Gateways > Spoke Gateways** and
           click **+ Spoke Gateway**.

        2. Provide the following details:

           | Parameter         | Value                                                                           |
           | ----------------- | ------------------------------------------------------------------------------- |
           | **Name**          | A name for the gateway, for example `mcp-containment-spoke`                     |
           | **Cloud**         | The cloud provider hosting the Kubernetes cluster                               |
           | **Account**       | The cloud account for the cluster                                               |
           | **Region**        | The region where the cluster runs                                               |
           | **VPC/VNet**      | The VPC or VNet where the cluster nodes run — this must match the cluster's VPC |
           | **Instance Size** | Select an instance size appropriate for your environment                        |

        3. In the **Instances** section, select a subnet in the cluster VPC for the
           gateway instance.

        4. Click **Save**.

        Wait for the gateway status to show **Up** before continuing. You can monitor
        progress at **CoPilot > Monitor > Notifications > Tasks**.
      </Step>

      <Step title="Configure the UDR on the agent subnet">
        Create a UDR that blackhole all outbound traffic from the agent subnet. Aviatrix
        will program the route later through the Aviatrix spoke gateway, then associate
        it with the delegated subnet.

        1. In the Azure Portal, navigate to **Route Tables** and click **+ Create**.

        2. Provide a name (for example, `foundry-agent-udr`) and select the same region
           and resource group as your Microsoft Foundry VNet.

        3. After creation, open the route table and select **Routes > + Add**:

           | Field                        | Value               |
           | ---------------------------- | ------------------- |
           | **Route name**               | `default-blackhole` |
           | **Destination type**         | IP Addresses        |
           | **Destination IP addresses** | `0.0.0.0/0`         |
           | **Next hop type**            | None                |

        4. Select **Subnets > + Associate** and associate the route table with the
           `foundry-agent-subnet`.

        Enable Single IP NAT on the spoke gateway, navigate to **Cloud Fabric >
        Gateways > Spoke Gateways** in CoPilot and select the gateway. Navigate to
        **Settings > Network Address Translation (NAT)** to change the setting.

        <Note>
          Do not apply this UDR to the private endpoint subnet. Private endpoint subnets
          route directly to their Azure PaaS resources and must not traverse the spoke
          gateway.
        </Note>
      </Step>

      <Step title="Create SmartGroups">
        SmartGroups identify the agent workload as a policy source and define Azure
        platform traffic that must bypass decryption.

        1. Navigate to **Security > Distributed Cloud Firewall > Groups** and select the
           **SmartGroups** tab.
        2. Create the following SmartGroups:

           **`foundry-agents`**: the agent workload source

           * Match type: **Subnet**
           * Account: your Aviatrix Azure account name
           * Subnet name: the name of the ACA-delegated agent subnet

           **`aca-platform-svctags`**: all ACA platform control-plane service tags in a
           single SmartGroup

           * Match type: **External (Azure)**
           * Add four entries, one per service tag: `AzureActiveDirectory`,
             `MicrosoftContainerRegistry`, `AzureFrontDoorFirstParty`,
             `AzureContainerRegistry`
           * Aviatrix evaluates each entry as a logical OR — traffic matching any tag in
             the set is covered by the single ACA service tag rule
      </Step>

      <Step title="Create WebGroups">
        WebGroups define the FQDN allow-lists for ACA platform requirements and
        permitted agent tool-call destinations.

        1. Navigate to **Security > Distributed Cloud Firewall > Groups** and select the
           **WebGroups** tab.
        2. Create the following WebGroups:

           **`aca-requirements-fqdns`**: ACA platform FQDNs required for agent startup
           and operation.

           | Scenario                             | FQDNs                                                                                                       |
           | ------------------------------------ | ----------------------------------------------------------------------------------------------------------- |
           | MCR (all agent types)                | `mcr.microsoft.com`, `*.data.mcr.microsoft.com`                                                             |
           | AKS infrastructure (all agent types) | `packages.aks.azure.com`, `acs-mirror.azureedge.net`                                                        |
           | Managed identity                     | `*.identity.azure.net`, `login.microsoftonline.com`, `*.login.microsoftonline.com`, `*.login.microsoft.com` |
           | Hosted agent container registry      | `login.microsoft.com`                                                                                       |
           | Application Insights telemetry       | `*.in.applicationinsights.azure.com`                                                                        |

        <Note>
          In the list above for Hosted agent container registry, we did not included the
          optional \*.blob.core.windows.net FQDN as it represents an large data
          exfiltration path. Feel free to include it back as needed.
        </Note>

        **`foundry-tool-calls`**: approved tool call and MCP server destinations

        * Add the FQDN of each external API, tool endpoint, or MCP server the agent is
          permitted to reach
        * This is the primary allow-list for the agent's declared tool-call surface
      </Step>

      <Step title="Create DCF policies">
        DCF policies enforce the ACA runtime requirements, permit declared tool
        destinations with optional inspection, and block all other egress. Policies are
        evaluated top-down; the first matching rule applies.

        Navigate to **Security > Distributed Cloud Firewall > Policy** and create the
        following rules in priority order:

        | Priority | Name                 | Source           | Destination               | Action | Decrypt               |
        | -------- | -------------------- | ---------------- | ------------------------- | ------ | --------------------- |
        | 1        | `block-threat-intel` | `foundry-agents` | Global Threat Intel group | DENY   | —                     |
        | 2        | `allow-aca-fqdns`    | `foundry-agents` | `aca-requirements-fqdns`  | PERMIT | `DECRYPT_NOT_ALLOWED` |
        | 3        | `allow-aca-svctags`  | `foundry-agents` | `aca-platform-svctags`    | PERMIT | `DECRYPT_NOT_ALLOWED` |
        | 4        | `allow-tool-calls`   | `foundry-agents` | `foundry-tool-calls`      | PERMIT | `DECRYPT_ALLOWED`     |
        | 5        | `deny-internet`      | `foundry-agents` | Any                       | DENY   | —                     |

        <Warning>
          The ACA platform rules (priorities 2–3) must sit above the tool-call and
          default-deny rules. ACA platform traffic is evaluated before any agent-level
          policy. If these rules are missing or ordered below the default deny, the
          agent container will fail to start.
        </Warning>
      </Step>

      <Step title="Verify containment">
        1. Deploy a Microsoft Foundry agent and confirm it starts and responds
           correctly. ACA platform traffic (container registry pulls, managed identity
           token acquisition) should appear as PERMIT events in CoPilot DCF logs.

        2. Invoke a tool call from the agent and confirm the destination appears in
           CoPilot DCF logs with action PERMIT and rule `allow-tool-calls`.

        3. Attempt a connection from the agent to a destination not in
           `foundry-tool-calls` and confirm it is denied. The denied connection appears
           in CoPilot per-connection logs with the rule name that matched.

        4. Navigate to **Security > Distributed Cloud Firewall** in CoPilot and confirm
           all five rules are active with the correct source and destination groups.
      </Step>
    </Steps>
  </Tab>

  <Tab title="Terraform">
    <Steps>
      <Step title="Prepare the agent subnet">
        The delegated subnet must exist before the Microsoft Foundry project is
        deployed. Create it with the required delegation:

        ```hcl theme={null}
        resource "azurerm_subnet" "foundry_agents" {
          name                 = "foundry-agent-subnet"
          resource_group_name  = var.resource_group_name
          virtual_network_name = var.vnet_name
          address_prefixes     = [var.agent_subnet_cidr]

          delegation {
            name = "foundry-agent-delegation"
            service_delegation {
              name    = "Microsoft.App/environments"
              actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"]
            }
          }
        }
        ```
      </Step>

      <Step title="Deploy an Aviatrix spoke gateway in the cluster VPC">
        An Aviatrix spoke gateway must be deployed in the same VPC as the target
        Kubernetes cluster. If a spoke gateway is already deployed in the cluster VPC,
        proceed to the next step.

        ```hcl theme={null}
        resource "aviatrix_spoke_gateway" "mcp_containment" {
          cloud_type   = var.cloud_type
          account_name = var.account_name
          gw_name      = "mcp-containment-spoke"
          vpc_id       = var.cluster_vpc_id
          vpc_reg      = var.region
          gw_size      = var.gateway_size
          subnet       = var.subnet_cidr
        }
        ```
      </Step>

      <Step title="Configure the UDR on the agent subnet">
        Create a route table with a default internet route (0.0.0.0/0) pointing to None
        and associate it with the delegated agent subnet. Do not associate this route
        table with the private endpoint subnet.

        ```hcl theme={null}
        resource "azurerm_route_table" "foundry_agents" {
          name                = "foundry-agent-udr"
          resource_group_name = var.resource_group_name
          location            = var.location
        }

        resource "azurerm_route" "default_via_aviatrix" {
          name                   = "default-via-aviatrix"
          resource_group_name    = var.resource_group_name
          route_table_name       = azurerm_route_table.foundry_agents.name
          address_prefix         = "0.0.0.0/0"
          next_hop_type          = "None"
        }

        resource "azurerm_subnet_route_table_association" "foundry_agents" {
          subnet_id      = azurerm_subnet.foundry_agents.id
          route_table_id = azurerm_route_table.foundry_agents.id
        }
        ```
      </Step>

      <Step title="Create SmartGroups">
        ```hcl theme={null}
        # Agent workload source — subnet-type matching is more precise than CIDR
        # and scopes the SmartGroup to exactly one subnet by account and name.
        resource "aviatrix_smart_group" "foundry_agents" {
          name = "foundry-agents"
          selector {
            match_expressions {
              type         = "subnet"
              account_name = var.aviatrix_account_name
              name         = var.agent_subnet_name
            }
          }
        }

        # All ACA platform control-plane service tags in a single SmartGroup.
        # Aviatrix evaluates each match_expressions entry as a logical OR —
        # traffic matching any tag in the set is permitted by rule 3.
        resource "aviatrix_smart_group" "aca_platform_svctags" {
          name = "aca-platform-svctags"
          selector {
            dynamic "match_expressions" {
              for_each = [
                "AzureActiveDirectory",
                "MicrosoftContainerRegistry",
                "AzureFrontDoorFirstParty",
                "AzureContainerRegistry",
              ]
              content {
                external = "azureips"
                ext_args = {
                  service_name = match_expressions.value
                }
              }
            }
          }
        }
        ```
      </Step>

      <Step title="Create WebGroups">
        ```hcl theme={null}
        locals {
          aca_requirement_fqdns = [
            # MCR - all agent types
            "mcr.microsoft.com",
            "*.data.mcr.microsoft.com",
            # AKS infrastructure - all agent types
            "packages.aks.azure.com",
            "acs-mirror.azureedge.net",
            # Managed identity
            "*.identity.azure.net",
            "login.microsoftonline.com",
            "*.login.microsoftonline.com",
            "*.login.microsoft.com",
            # Hosted agent container registry
            "login.microsoft.com",
            # Application Insights telemetry
            "*.in.applicationinsights.azure.com",
            # This is optional and create a large data exfiltration path if enabled
            # "*.blob.core.windows.net"
          ]
        }

        resource "aviatrix_web_group" "aca_requirements_fqdns" {
          name = "aca-requirements-fqdns"
          selector {
            dynamic "match_expressions" {
              for_each = local.aca_requirement_fqdns
              content {
                snifilter = match_expressions.value
              }
            }
          }
        }

        resource "aviatrix_web_group" "foundry_tool_calls" {
          name = "foundry-tool-calls"
          selector {
            dynamic "match_expressions" {
              for_each = var.tool_call_fqdns
              content {
                snifilter = match_expressions.value
              }
            }
          }
        }
        ```
      </Step>

      <Step title="Create DCF policies">
        ```hcl theme={null}
        locals {
          # Built-in Aviatrix SmartGroup UUIDs — identical across all controllers.
          sg_public_internet = "def000ad-0000-0000-0000-000000000001"
          sg_threat_intel    = "def05854-4100-0000-0000-000000000000"
        }

        # DCF attachment point — attaches the ruleset before any UI-managed rules.
        data "aviatrix_dcf_attachment_point" "tf_before_ui" {
          name = "TERRAFORM_BEFORE_UI_MANAGED"
        }

        resource "aviatrix_dcf_ruleset" "foundry" {
          name      = "foundry-agent-egress"
          attach_to = data.aviatrix_dcf_attachment_point.tf_before_ui.id

          # Rule 1 — deny known-malicious destinations before any permit.
          rules {
            name             = "block-threat-intel"
            priority         = 1
            action           = "DENY"
            protocol         = "ANY"
            logging          = true
            src_smart_groups = [aviatrix_smart_group.foundry_agents.uuid]
            dst_smart_groups = [local.sg_threat_intel]
          }

          # Rule 2 — ACA runtime FQDNs: permit without decryption.
          # TLS decryption breaks ACA container startup; these FQDNs must bypass inspection.
          rules {
            name                 = "allow-aca-fqdns"
            priority             = 2
            action               = "PERMIT"
            protocol             = "TCP"
            decrypt_policy       = "DECRYPT_NOT_ALLOWED"
            flow_app_requirement = "TLS_REQUIRED"
            logging              = true
            src_smart_groups     = [aviatrix_smart_group.foundry_agents.uuid]
            dst_smart_groups     = [local.sg_public_internet]
            web_groups           = [aviatrix_web_group.aca_requirements_fqdns.uuid]
            port_ranges { lo = 443 }
          }

          # Rule 3 — ACA platform service tags: permit without decryption.
          # Single SmartGroup covers all four ACA control-plane service tags (logical OR).
          rules {
            name                 = "allow-aca-svctags"
            priority             = 3
            action               = "PERMIT"
            protocol             = "TCP"
            decrypt_policy       = "DECRYPT_NOT_ALLOWED"
            flow_app_requirement = "TLS_REQUIRED"
            logging              = true
            src_smart_groups     = [aviatrix_smart_group.foundry_agents.uuid]
            dst_smart_groups     = [aviatrix_smart_group.aca_platform_svctags.uuid]
            port_ranges { lo = 443 }
          }

          # Rule 4 — approved tool-call FQDNs: permit with TLS decryption for inspection.
          rules {
            name             = "allow-tool-calls"
            priority         = 4
            action           = "PERMIT"
            protocol         = "TCP"
            decrypt_policy   = "DECRYPT_ALLOWED"
            logging          = true
            src_smart_groups = [aviatrix_smart_group.foundry_agents.uuid]
            dst_smart_groups = [local.sg_public_internet]
            web_groups       = [aviatrix_web_group.foundry_tool_calls.uuid]
            port_ranges { lo = 443 }
          }

          # Rule 5 — default deny: block all remaining egress.
          rules {
            name             = "deny-internet"
            priority         = 5
            action           = "DENY"
            protocol         = "ANY"
            logging          = true
            src_smart_groups = [aviatrix_smart_group.foundry_agents.uuid]
            dst_smart_groups = [local.sg_public_internet]
          }
        }
        ```
      </Step>

      <Step title="Verify containment">
        1. Apply the Terraform configuration and confirm no errors in the output.

        2. Deploy a Microsoft Foundry agent and confirm it starts correctly. ACA
           platform traffic should appear as PERMIT events in CoPilot DCF logs.

        3. Invoke a tool call from the agent and confirm the destination appears in
           CoPilot DCF logs with action PERMIT and rule `allow-tool-calls`.

        4. Attempt a connection from the agent to a destination not in
           `foundry-tool-calls` and confirm it is denied. The denied connection appears
           in CoPilot per-connection logs.
      </Step>
    </Steps>
  </Tab>
</Tabs>
