> ## 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 Enterprise Transit

> Apply Aviatrix AgentGuard containment to Microsoft Foundry agent workloads in BYOVNet mode using an existing Aviatrix Enterprise Transit: spoke gateway insertion, UDR enforcement, ACA runtime exclusions, selective TLS inspection, and East-West containment across the transit fabric.

When complete, all outbound traffic from the Microsoft Foundry delegated agent
subnet will route through the 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 TLS decryption on
tool-call traffic and explicit bypass for Azure control-plane paths. East-West
SmartGroup policy prevents a compromised agent from reaching adjacent workload
spokes in the transit fabric.

This guide covers the Enterprise Transit topology: the Microsoft Foundry VNet is
attached to an existing Aviatrix Transit Gateway, giving you both internet
egress containment and East-West isolation in the same deployment. If you do not
yet have Aviatrix Enterprise Transit in place, see <a href={"/docs/enterprise/" + "10.1" +
"/guides/security/egress/ai-agent-containment-azure-foundry-standalone"}>Microsoft
Foundry: Standalone Deployment</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
* An Aviatrix Enterprise Transit Gateway deployed in the same Azure region as
  the Microsoft Foundry VNet
* 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 (hosted agent Micro VMs and
the single-tenant data proxy) 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 Microsoft Foundry VNet is peered to your Aviatrix Enterprise Transit. This
gives you two enforcement surfaces: internet egress is controlled at the spoke
gateway boundary, and East-West traffic between the Microsoft Foundry spoke and
adjacent workload spokes is controlled by SmartGroup policy. A compromised agent
cannot pivot to adjacent internal workloads any more than it can reach arbitrary
internet destinations.

Internal resource traffic (the data proxy connecting 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.
Aviatrix only enforces policy on external egress and cross-spoke traffic.

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
`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="Attach the spoke gateway to your Enterprise Transit">
        1. In CoPilot, navigate to **Cloud Fabric > Gateways > Spoke Gateways** and
           select the gateway you just deployed.
        2. Select **Actions > Attach to Transit** and choose your Enterprise Transit
           Gateway.
        3. Confirm the attachment completes and the spoke shows as **Up** in the transit
           topology.
      </Step>

      <Step title="Configure the UDR on the agent subnet">
        Create a UDR that routes all outbound traffic from the agent subnet 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, define Azure
        platform traffic that must bypass decryption, and set the East-West boundaries
        between the Microsoft Foundry spoke and adjacent workload spokes.

        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

           **`adjacent-spokes`**: other workload VNets in the transit fabric the agent
           must not reach

           * Match type: **CIDR**
           * Add a CIDR entry for each adjacent spoke VNet CIDR that requires East-West
             deny protection
      </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 inspection, and block all other egress and East-West traffic.
        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` | Public Internet           | DENY   | —                     |
        | 6        | `deny-east-west`     | `foundry-agents` | `adjacent-spokes`         | 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 FlowIQ.

        2. Invoke a tool call from the agent and confirm the destination appears in
           CoPilot FlowIQ 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. Attempt a connection from the agent subnet to an adjacent spoke VNet CIDR and
           confirm it is denied by the `deny-east-west` rule.

        5. Navigate to **Security > Distributed Cloud Firewall** in CoPilot and confirm
           all six 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="Attach the spoke gateway to your Enterprise Transit">
        ```hcl theme={null}
        resource "aviatrix_spoke_transit_attachment" "foundry" {
          spoke_gw_name   = aviatrix_spoke_gateway.mcp_containment.gw_name
          transit_gw_name = var.transit_gateway_name
        }
        ```
      </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-blackhole"
          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
                }
              }
            }
          }
        }

        resource "aviatrix_smart_group" "adjacent_spokes" {
          name = "adjacent-spokes"
          selector {
            dynamic "match_expressions" {
              for_each = var.adjacent_spoke_cidrs
              content {
                cidr = 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 internet 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]
          }

          # Rule 6 — deny East-West: prevent pivoting to adjacent spoke VNets.
          rules {
            name             = "deny-east-west"
            priority         = 6
            action           = "DENY"
            protocol         = "ANY"
            logging          = true
            src_smart_groups = [aviatrix_smart_group.foundry_agents.uuid]
            dst_smart_groups = [aviatrix_smart_group.adjacent_spokes.uuid]
          }
        }
        ```
      </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.

        5. Attempt a connection from the agent subnet to an adjacent spoke VNet CIDR and
           confirm it is denied by the `deny-east-west` rule.

        6. In CoPilot FlowIQ, confirm all six rules appear in the active ruleset and
           that no ACA startup traffic is being denied.
      </Step>
    </Steps>
  </Tab>
</Tabs>
