Add guardrails to a hosted agent

This article shows you how to attach guardrails to a hosted agent in Microsoft Foundry. Define guardrails in a Responsible AI (RAI) policy that you reference from the agent definition. The platform applies these guardrails at runtime. This article covers two kinds of guardrails:

  • Content safety controls screen the prompts your agent receives and the responses it returns, so harmful content is filtered according to your organization's safety configuration.
  • Network egress controls (preview) govern the outbound connections your agent makes, so it reaches only the destinations you allow.

You reference the guardrail by its RAI policy resource ID on the agent definition. You can attach it when you deploy by using the Azure Developer CLI (azd), the Python SDK, or the REST API. The same attach steps apply to both kinds of guardrails. To understand content safety policy precedence, streaming behavior, latency, and block handling, see Hosted agent content safety overview. To learn what guardrails are, the risks they detect, and how to create one, see Guardrails and controls overview.

If your agent uses the invocations protocol, attaching a policy isn't enough on its own. You also declare where the text to screen lives in your request and response bodies. See Add a guardrail to an agent that uses the invocations protocol.

Prerequisites

  • A Microsoft Foundry project.

  • A hosted agent, or a container image ready to deploy as one. See Deploy a hosted agent.

  • A guardrail (RAI policy) on the Foundry resource, and its full Azure Resource Manager (ARM) resource ID. To create one in the Foundry portal, see Configure guardrails and controls. For a network egress guardrail, you can also create the policy with azd provision. The ARM resource ID has this form:

    /subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.CognitiveServices/accounts/<account>/raiPolicies/<policy-name>
    
  • For the Azure Developer CLI method: the azd ai agent extension, version 1.0.0-beta.12 or later.

  • For the Python SDK method: the Azure AI Projects client library for Python, version 2.2.0 or later:

    pip install "azure-ai-projects>=2.2.0"
    

    To configure moderation for the invocations protocol, use version 2.7.0 or later:

    pip install "azure-ai-projects>=2.7.0"
    

How guardrails apply to hosted agents

A hosted agent definition has an optional rai_config setting with a rai_policy_name field. Set rai_policy_name to the full ARM resource ID of your guardrail's RAI policy. The platform applies that policy to the agent's prompts and responses.

When you omit rai_config, the agent runs without a content safety guardrail. When you include rai_config but omit rai_policy_name, the platform applies the default policy, Microsoft.DefaultV2. Provide a custom policy when you need stricter or organization-specific filtering.

Always use the full ARM resource ID for rai_policy_name, not the bare policy name.

Warning

Don't rely on deploy-time validation to catch a bad policy ID. On many subscriptions an agent that references a policy that doesn't exist is created successfully and reports active, but no content filtering is applied - the guardrail fails open and harmful prompts reach the agent. Confirm the policy exists on the account, then test the guardrail before you rely on the agent's content safety.

rai_config is the shape the Foundry API accepts, so the Python SDK and REST examples in this article set it directly. The Azure Developer CLI doesn't expose rai_config in azure.yaml; it uses a policies list instead and maps it to rai_config when it deploys.

Protocol differences

How much configuration a guardrail needs depends on the protocol your agent exposes:

Protocol Configuration
responses Set rai_policy_name. The platform knows the request and response shapes, so it locates the text to screen on its own.
invocations Set rai_policy_name and invocations_moderation. Request and response bodies are defined by your agent, so you declare where the text lives.
invocations_ws Content safety moderation isn't available.
activity Set rai_policy_name. The platform knows the request, response and outbound request shapes, and the text gets moderated automatically

Important

On the invocations protocol, a policy attached without invocations_moderation is inert. The platform has no way to find the text in your custom body shapes, so it doesn't screen anything and requests pass through unfiltered. The agent still deploys and returns HTTP 200, which makes the gap easy to miss.

Add a guardrail

Choose the method you use to deploy the agent. These examples attach a guardrail to an agent on the responses protocol. On the invocations protocol, you also need moderation settings, which Add a guardrail to an agent that uses the invocations protocol covers.

When you use azd, declare the guardrail in the policies list on the azure.ai.agent service in azure.yaml. Add an entry with type: rai_policy and set raiPolicyName to the full ARM resource ID of the RAI policy. When you deploy, azd maps that entry to rai_config.rai_policy_name on the agent definition it sends to Foundry.

  1. In your azure.yaml, add a policies list to the agent service:

    services:
      my-agent:
        host: azure.ai.agent
        project: src/my-agent
        kind: hosted
        name: my-hosted-agent
        description: A hosted agent with a content safety guardrail
        policies:
          - type: rai_policy
            # Full ARM resource ID of the RAI policy on the Foundry resource.
            raiPolicyName: /subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.CognitiveServices/accounts/<account>/raiPolicies/<policy-name>
        protocols:
          - protocol: responses
            version: "2.0.0"
    
  2. Deploy the agent:

    azd deploy
    

The platform attaches the guardrail when it creates the agent version.

Note

In azure.yaml the field is camelCased as raiPolicyName. The deprecated standalone agent.yaml uses the snake_case rai_policy_name. Both map to rai_config.rai_policy_name on the agent version. Don't declare the guardrail in agent.manifest.yaml - azd reads that file only during azd ai agent init and ignores it at deploy time.

Verify the guardrail is applied

Get the agent version and confirm that rai_config.rai_policy_name holds your policy ID.

curl -s -X GET "$BASE_URL/agents/my-agent/versions/1?api-version=$API_VERSION" \
  -H "Authorization: Bearer $TOKEN" | jq '.definition.rai_config'

The response includes the policy you set:

{
  "rai_policy_name": "/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.CognitiveServices/accounts/<account>/raiPolicies/<policy-name>"
}

Test content safety filtering

To confirm that the guardrail filters content, send a prompt that violates your safety policy to the agent's Responses endpoint. The platform screens the prompt at the input stage and rejects it before the agent runs.

curl -i -X POST "$BASE_URL/agents/my-agent/endpoint/protocols/openai/responses?api-version=$API_VERSION" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"input":"<a prompt that your policy is configured to block>","store":true}'

A blocked prompt returns HTTP 400 with a content_filter error:

{
  "error": {
    "code": "content_filter",
    "message": "The request was blocked due to content safety policy violation at input stage.",
    "type": "content_safety_error"
  }
}

A prompt that passes the policy returns HTTP 200 with the agent's response. If a harmful prompt isn't blocked, check in this order:

  1. The policy named by rai_policy_name actually exists on the account. A nonexistent policy fails open with no error. List the policies on the account and confirm the final segment of rai_policy_name matches one of them:

    az rest --method get \
      --url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.CognitiveServices/accounts/<account>/raiPolicies?api-version=2024-10-01" \
      --query "value[].name" -o tsv
    
  2. The policy is configured to filter the relevant content category and severity.

The guardrail applies to streaming requests too. By using "stream": true, a violating prompt is rejected with the same HTTP 400 before any event is emitted.

Add a guardrail to an agent that uses the invocations protocol

On the responses protocol, the platform knows the request and response shapes, so rai_policy_name is all you need. The invocations protocol accepts request and response bodies that your agent defines, so the platform can't tell which fields hold user or agent text. Add an invocations_moderation object to rai_config that declares where the text lives.

Until you do, the policy is attached but screens nothing.

Moderation settings

Setting Required Description
response_mode Yes The response shapes your agent can produce: non_streaming, streaming, or both.
input_content_type No How to parse the request body: json (default) or text.
output_content_type No How to parse the response body: json (default) or text.
input_paths When input_content_type is json Path expressions that select the user text in the request body.
output_paths When response_mode is non_streaming or both, and output_content_type is json Path expressions that select the agent text in a buffered response body.
stream_selectors When response_mode is streaming or both, and output_content_type is json Pairs of event_type and text_field that locate text in streamed events.

Set input_content_type or output_content_type to text when that body is plain text. The platform then screens the body itself and you don't provide paths for that direction.

You can attach only one RAI policy to an agent, so invocations_moderation applies to that single policy.

Path expressions

input_paths and output_paths accept $ for the document root, dot notation for members, array indexes, and [*] wildcards. For example, $.messages[*].content selects the content field of every element in the messages array. When a path selects several values, the platform joins them and screens them together.

Stream selectors

For a streamed response, the platform reads the type field of each event and compares it to event_type. On a match, it reads the field named by text_field and screens that text.

Both event_type and text_field are exact, case-sensitive matches against top-level properties of the event's JSON payload. You can't select a nested field.

text_field is a field name, not a path expression. Use content, not $.content.

An event whose type matches no selector, or whose text_field names no property, contributes no text. That event's content goes unscreened, and if no selector ever yields text, the response isn't screened at all. When you omit text_field, the platform uses delta.

Response modes

response_mode declares the shapes your agent can return. It applies only to output: input screening runs regardless of the value you set. For output, the platform inspects the response Content-Type and runs one check, using the streaming check for text/event-stream and the buffered check otherwise.

Declare every shape your agent can return. A successful response that carries content in a shape you didn't declare is rejected with HTTP 502 rather than skipping moderation. Use both only when your agent genuinely answers both ways.

Output screening applies to successful responses that carry content. The platform doesn't screen error responses from your container or empty acknowledgments.

Limits

Content safety screening has bounds that affect large payloads:

Limit Behavior
Request body larger than 2 MB Forwarded to your agent without input screening.
Buffered response body larger than 1 MB Rejected with HTTP 502. The response never reaches the client.
Text longer than 10,000 characters in a single check Truncated before analysis.

The platform also forwards a request unscreened when it can't parse the body as JSON or when input_paths selects nothing. Confirm your paths match your real request bodies rather than assuming a deployed policy is screening them.

Add the moderation settings

Choose the method you use to deploy the agent.

Add an invocationsModeration block to the rai_policy entry in azure.yaml. These settings use camel case, and azd maps them to the snake case names that the API accepts.

  1. In your azure.yaml, add invocationsModeration to the rai_policy entry. This example screens the message field of the request. The agent streams events shaped like {"type": "token", "content": "..."} and a final {"type": "done", "full_text": "..."}.

    services:
      my-agent:
        host: azure.ai.agent
        project: src/my-agent
        kind: hosted
        name: my-hosted-agent
        policies:
          - type: rai_policy
            raiPolicyName: /subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.CognitiveServices/accounts/<account>/raiPolicies/<policy-name>
            invocationsModeration:
              responseMode: streaming
              inputPaths:
                - $.message
              streamSelectors:
                - eventType: token
                  textField: content
                - eventType: done
                  textField: full_text
        protocols:
          - protocol: invocations
            version: "2.0.0"
    
  2. Deploy the agent:

    azd deploy
    

    The platform applies the moderation settings when it creates the agent version.

azd checks the block before it deploys, so a structural mistake fails locally instead of at runtime. For example, declaring moderation on an agent that doesn't expose the invocations protocol returns:

policies[0] invocationsModeration is only supported for agents that expose the 'invocations' protocol; add it to 'protocols' or remove the moderation block

These checks cover structure, not meaning. azd can't tell whether your paths and field names match the bodies your agent actually sends, so verify that yourself with the test in Test the moderation settings.

Moderation on Activity Protocol

If you add guardrails on an agent that uses activity protocol, the moderation happens on three paths:

Activity path Content screened How to enable on policy
Input Incoming content. Enable Input Moderation.
Output Content returned by the agent. Enable Output Moderation.
Outbound Activity events sent to the Microsoft 365 service using POST or PUT. Enable Output Moderation.

What a blocked invocation looks like

The response to a blocked request depends on which stage the platform blocks and whether your agent streams.

A blocked request returns HTTP 400 before your agent runs. The message ends with the request ID, which you can use when you file a support request:

{
  "error": {
    "code": "content_filter",
    "message": "The request was blocked due to content safety policy violation at input stage. [Request ID: <request-id>]",
    "type": "content_safety_error"
  }
}

A blocked buffered response also returns HTTP 400, with a message that names the output stage.

A blocked streamed response is different. The platform sends response headers before it screens the agent's output, so the status stays HTTP 200. The platform discards the events it was holding, sends a single error event, and ends the stream:

event: error
data: {"type":"error","code":"content_filter","message":"The response was blocked due to content safety policy violation."}

Handle this event in your client. Treat it as terminal. Earlier events might already have reached the client, so the user could see partial output before the block. A 200 status alone doesn't mean the response passed the policy.

Test the moderation settings

To confirm your settings screen the right fields, send a request that your policy is configured to block and check that the platform blocks it.

If you deployed with azd, put the request body in a file, such as blocked-request.json:

{
  "message": "<a prompt that your policy is configured to block>"
}

Then invoke the agent with that file:

azd ai agent invoke -f blocked-request.json

azd reads the protocol from azure.yaml. Send the body as a file rather than as a message argument: azd sends a message argument as text/plain, and an inputContentType of json can't parse it, so the platform forwards the request unscreened.

You can also call the endpoint directly:

curl -i -X POST "$BASE_URL/agents/my-agent/endpoint/protocols/invocations?api-version=$API_VERSION" \
  -H "Authorization: ******" \
  -H "Content-Type: application/json" \
  -d '{"message":"<a prompt that your policy is configured to block>"}'

If the request isn't blocked, check that:

  • input_paths matches the field that holds the user text. A path that selects nothing means nothing is screened.
  • Each text_field is a field name, such as content, rather than a path such as $.content.
  • Each event_type matches the type value your agent sends in its streamed events.
  • Your event_type and text_field values match your agent's casing exactly, and name top-level properties rather than nested ones.
  • The policy filters the relevant content category and severity.

If requests fail with HTTP 502 instead, response_mode probably doesn't match what your agent returns. Set it to both if your agent answers both ways.

Network egress controls (preview)

Important

Network egress controls are in preview. They apply to hosted agents only and don't affect prompt-based agents or model deployments. Configure them by using the 2026-05-15-preview API version of the RAI policy. Preview features are provided without a service-level agreement and are not intended to be used in production or in a live operating environment. This feature consists of tooling only. Customers are responsible for understanding the data handling practices of any endpoints receiving data.

Content safety controls screen prompts and responses. Network egress controls govern the outbound connections your hosted agent makes. You define ordered rules that allow, deny, transform, or rewrite outbound requests by destination host, and the platform enforces them inside the agent's sandbox before traffic leaves the runtime. Egress rules are stored in the same RAI policy you attach in the previous sections, so the azd, Python SDK, and REST API attach steps apply them automatically.

How egress rules are evaluated

  • The system evaluates rules in order, from top to bottom. The first matching rule wins.
  • If no rule matches, the policy's default action applies. Set the default action to Deny for an allow list (recommended) or Allow for a deny list.
  • When you apply an egress policy, the agent runtime automatically allow lists foundational domains it needs to function. A Deny default action doesn't block this required platform connectivity, so you don't need to add rules for it.
  • Each rule matches on the request host. Wildcards such as *.contoso.com are supported.
  • Rule actions are Allow, Deny, Transform (allow the request and modify its headers), and Rewrite (redirect the request to another destination).
  • Evaluation is fail-closed: if the policy can't be evaluated, the request is denied.

Rule limits

You can add a maximum of 480 egress rules per policy. This limit applies to all egress rule actions: Allow, Deny, Transform, and Rewrite. To request an increase to this limit, create an Azure support request.

Choose an enforcement mode

Mode Behavior When to use
Audit Outbound traffic flows normally. Requests that would be denied are logged but not blocked. Observe what your agent calls before you enforce.
Enforce Matching rules are applied, and denied requests are blocked. Production enforcement after you validate rules in Audit mode.

Deploy in Audit mode first, review the egress decisions, refine your rules, and then switch to Enforce.

Note

Audit mode changes only how Deny actions behave: a request that would be denied is logged instead of blocked. Transform and Rewrite actions are applied in both Audit and Enforce modes, so header transforms and redirects still take effect while you audit.

Common egress control use cases

Use egress controls to limit a hosted agent to the external services required for its task. The following patterns are common starting points:

Use case Policy approach
Discover an agent's outbound dependencies Start with Audit mode and a Deny default action. Run representative tasks, review the egress decisions, and add the required hosts before you enforce the policy.
Restrict a coding agent to approved package and source repositories Allow the package registries, download hosts, and source-control APIs that the agent needs. Use a Deny default action for all other destinations.
Limit an integration agent to approved SaaS APIs Allow only the API hosts for the services that the agent integrates with, such as an issue tracker or source-control provider.
Add request metadata for an enterprise API Use a Transform rule with a static header value to add a workload identifier or correlation tag. Don't put credentials or other secrets in a static header value.
Route requests through an enterprise gateway Use a Rewrite rule to redirect a matched host to an approved gateway. Test rewrites in a nonproduction environment before you use them with agent workloads.

Package managers and SDKs can follow redirects or use separate download hosts. Don't assume that the registry host is the only destination required. Use Audit mode with representative workloads to identify the complete host set.

Add egress rules by using the Azure Developer CLI

Add the RAI policy ARM resource to your azd project's Bicep infrastructure. The azd provision command deploys the resource through ARM.

  1. Add the following Bicep to the resource-group-scoped infrastructure for the resource group that contains your Foundry resource:

    @description('Name of the existing Foundry resource.')
    param accountName string
    
    resource account 'Microsoft.CognitiveServices/accounts@2026-05-15-preview' existing = {
      name: accountName
    }
    
    resource egressPolicy 'Microsoft.CognitiveServices/accounts/raiPolicies@2026-05-15-preview' = {
      parent: account
      name: 'allow-contoso'
      properties: {
        mode: 'Blocking'
        basePolicyName: 'Microsoft.DefaultV2'
        egressPolicy: {
          mode: 'Enforced'
          defaultAction: 'Deny'
          rules: [
            {
              name: 'allow-contoso'
              ruleType: 'Fqdn'
              match: {
                host: '*.contoso.com'
              }
              action: {
                actionType: 'Allow'
              }
            }
          ]
        }
      }
    }
    
    output RAI_POLICY_ID string = egressPolicy.id
    

    Reference: Microsoft.CognitiveServices accounts/raiPolicies.

  2. Provision the policy:

    azd provision
    

The command creates or updates the Microsoft.CognitiveServices/accounts/raiPolicies child resource. Use the RAI_POLICY_ID output as the full policy resource ID when you attach the guardrail to a hosted agent.

Add egress rules by using the REST API

An RAI policy stores egress rules in the egressPolicy property. Create or update the policy by using the Azure Resource Manager RAI Policies - Create Or Update operation with the 2026-05-15-preview API version:

TOKEN=$(az account get-access-token --resource "https://management.azure.com" --query accessToken -o tsv)

curl -X PUT \
  "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.CognitiveServices/accounts/<account>/raiPolicies/<policy-name>?api-version=2026-05-15-preview" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "properties": {
      "mode": "Blocking",
      "basePolicyName": "Microsoft.DefaultV2",
      "egressPolicy": {
        "mode": "Enforced",
        "defaultAction": "Deny",
        "rules": [
          {
            "name": "allow-contoso",
            "ruleType": "Fqdn",
            "match": { "host": "*.contoso.com" },
            "action": { "actionType": "Allow" }
          }
        ]
      }
    }
  }'
  • Set egressPolicy.mode to Enforced to block traffic, or Audit to log would-deny events without blocking.
  • Set egressPolicy.defaultAction to Deny for an allow list or Allow for a deny list.
  • Set each rule's action.actionType to Allow, Deny, Transform, or Rewrite.

To review the configured rules, send a GET request to the same URL and inspect properties.egressPolicy.

For a complete request body that combines a default action with several rule types, see the PutRaiPolicyWithEgress.json example in the Azure REST API specs.

Example: restrict a dependency research agent

Consider a coding agent that researches Python dependencies and their source repositories. It needs to read package metadata from PyPI, download package files, and retrieve repository metadata from the GitHub API. It shouldn't connect to unrelated internet destinations.

Start with the following egress policy in Audit mode. Replace the egressPolicy object in the REST request from the previous section with this object:

{
  "mode": "Audit",
  "defaultAction": "Deny",
  "rules": [
    {
      "name": "allow-pypi-metadata",
      "ruleType": "Fqdn",
      "match": { "host": "pypi.org" },
      "action": { "actionType": "Allow" }
    },
    {
      "name": "allow-python-package-downloads",
      "ruleType": "Fqdn",
      "match": { "host": "files.pythonhosted.org" },
      "action": { "actionType": "Allow" }
    },
    {
      "name": "allow-github-api",
      "ruleType": "Fqdn",
      "match": { "host": "api.github.com" },
      "action": { "actionType": "Allow" }
    }
  ]
}

Validate and enforce the policy:

  1. Create or update the RAI policy with mode set to Audit.
  2. Attach the policy to the hosted agent and deploy a new agent version.
  3. Run representative tasks, such as retrieving Python package metadata, inspecting a package's source repository, and downloading a package.
  4. Have the agent attempt a request to an unrelated host, such as example.com. Audit mode allows the request but records that the policy would deny it.
  5. Review the egress decisions. Add any legitimate redirect or download hosts that appear in the agent's normal workflow.
  6. Change mode from Audit to Enforced, update the RAI policy, and deploy a new agent version.
  7. Start a new session or resume the session, and run the same tasks again. Running sandboxes don't reload policy changes. Requests to the approved package and source-control hosts succeed. A request to an unapproved host returns HTTP 403.

The exact host set depends on the package manager, SDK, and services that your agent uses. Keep the allow list specific to the workload instead of copying the example unchanged.

For a runnable hosted-agent sample that exercises Allow, Deny, Transform, Rewrite, Audit, wildcard, and rule-ordering scenarios, see the egress control sample.

Transform request headers

When a rule's action.actionType is Transform (or Rewrite), you can modify the headers of the outbound request by using an action.headers array. Each entry describes one header operation:

{
  "name": "allow-and-tag-contoso",
  "ruleType": "Fqdn",
  "match": { "host": "*.contoso.com" },
  "action": {
    "actionType": "Transform",
    "headers": [
      { "operation": "Set",    "name": "X-Trace-Source", "value": "hosted-agent" },
      { "operation": "Insert", "name": "X-Request-Id",   "value": "default-id" },
      { "operation": "Remove", "name": "User-Agent" }
    ]
  }
}

Each header object supports the following fields:

Field Required Description
operation Yes The header operation: Set, Insert, or Remove. Operation names are case-insensitive. If you omit it, Set is used.
name Yes The name of the header to modify.
value For Set and Insert when valueRef isn't used The static header value. Not used for Remove.
valueRef For Set and Insert when value isn't used A managed identity value reference. Not used for Remove.

You must include headers when actionType is Transform. You can omit headers when actionType is Rewrite. Header transforms apply only to requests that match the rule.

Note

Header transforms support static values and managed identity value references. For a managed identity value reference, set valueRef.managedIdentityRef.resource to the target resource URI, such as https://storage.azure.com/. Set format to the bearer scheme with the {token} placeholder. Grant the deployed agent's instance_identity.principal_id the required role on the target resource. Secret value references aren't supported during preview.

Header operations

The three operations differ only in how they treat a header that's already present on the outbound request:

Operation Header already present Header not present
Set Overwrites the existing value. Adds the header.
Insert Leaves the existing value unchanged. Adds the header.
Remove Removes the header. No effect.

Use Set to force a header to a specific value regardless of what the agent sent. Use Insert to supply a default only when the agent didn't already set the header. Use Remove to strip a header before the request leaves the runtime.

Add egress rules in the portal

You can also author egress rules in the Foundry portal as a Network control on a guardrail:

  1. In the Foundry portal, create or edit a guardrail, and then expand the Network control.

    Screenshot of the Network control in a guardrail showing the Egress rules row and the Outbound requests default action.

  2. Select Egress rules, and set the Outbound requests default action to Deny or Allow.

  3. Select Add rules, choose a Mode (Audit or Enforce), enter a Host match and an Action, and then select Add. Reorder rules as needed; the first match wins. For a Transform action, use a Static value or Managed identity value source for the header. The Secret reference value source appears in the dialog but isn't supported during preview. Don't use this option. For more information, see Preview limitations.

    Screenshot of the Create egress rules dialog with Audit and Enforce modes, a host match field, and an action list.

  4. Select Create, and then assign the guardrail to your hosted agent.

For details about creating and assigning guardrails in the portal, see Configure guardrails and controls.

Certificate handling

To inspect HTTPS traffic, the hosted-agent runtime injects the egress proxy's certificate authority (CA) into the sandbox trust bundle. The proxy CA is infrastructure-specific: it can differ across hosted-agent clusters and regions, and it rotates over time (currently about every 30 days). Treat the CA bundle as runtime configuration. Don't pin, copy, or persist it.

Configure TLS clients to read the runtime-provided CA bundle from the standard environment variables that are already present in the sandbox:

Environment variable Use
SSL_CERT_FILE Generic PEM CA bundle path for OpenSSL-style TLS clients.
REQUESTS_CA_BUNDLE Python requests and compatible HTTP clients.
GRPC_DEFAULT_SSL_ROOTS_FILE_PATH gRPC clients that support the default roots file override.
NODE_EXTRA_CA_CERTS Node.js additional CA file, read when the process starts.

For example, with Python requests:

import os
import requests

response = requests.get(
    "https://example.com",
    verify=os.environ["REQUESTS_CA_BUNDLE"],
)

Keep these constraints in mind:

  • Don't pin the CA subject, public key, thumbprint, or file contents. Any of these values can change on rotation or differ by cluster.

  • Don't persist the injected CA to a container image, persistent volume, snapshot, or source control. The CA is runtime infrastructure, not application configuration.

  • Rotation can happen while a sandbox is running. Long-running processes might need to reload their TLS configuration or restart.

  • If you must build a single custom bundle (for example, to combine enterprise roots with the runtime roots), build it at process startup from the current runtime bundle and treat it as temporary:

    cat "$SSL_CERT_FILE" /app/custom-roots.pem > /tmp/runtime-ca-bundle.pem
    export SSL_CERT_FILE=/tmp/runtime-ca-bundle.pem
    export REQUESTS_CA_BUNDLE=/tmp/runtime-ca-bundle.pem
    

View egress decisions

The agent sends network egress decisions to your project's Application Insights and trace monitoring tools. You can view these decisions in the Foundry portal playground and in Application Insights to confirm that network egress behavior aligns with your configured policy.

In the Foundry portal playground

To view egress decisions in the trace timeline:

  1. Sign in to Microsoft Foundry and open your project's playground.
  2. Run or select an agent invocation to open its trace.
  3. Select the Trajectories tab to see the full trace timeline.
  4. Expand the trace nodes until you see a span named Network egress decision. It appears next to the request that triggered it.
  5. Select the span to review the decision details.

Screenshot of the Trajectories tab in the Foundry portal playground, showing a Network egress decision span in the trace timeline.

The span details show the information you need to understand why a request was allowed or denied:

Field Description
Decision The outcome of the policy evaluation, for example Allow or Deny.
Reason A human-readable explanation, such as "Request matched allow rule" or "No egress policy rule allowed this destination."
Matched rule The name of the rule that determined the outcome, if one matched.
Rule source The origin of the matched rule, such as a connector or policy definition.
Enforcement Whether the policy was enforced or run in audit mode.
Destination The method and URL of the outbound request.
Default action The action applied when no rule matches, for example Deny.

Allowed requests appear with a success status, and denied requests appear with a failure status, so you can spot blocked calls without leaving the trace view.

In Application Insights

To review egress decisions in Application Insights logs:

  1. Locate your project's Application Insights resource:

    1. Go to the Foundry portal.
    2. Select the Operate tab.
    3. Select Admin.
    4. Search for and select your project.
    5. Open the Connected resources tab.
    6. Find the AppInsights connected resource and copy its Target URI.

    Each project should have a single Application Insights connection. If Application Insights isn't configured, select Add connection and add an Application Insights resource for the project.

  2. Open the Application Insights resource in the Azure portal and go to Logs. Run the following query to view recent network egress decisions:

traces
| where timestamp > ago(1h)
| where message == "Network egress decision"

Each event includes details such as the destination host, matched rule, decision, and enforcement mode.

What a blocked request looks like

When a rule denies an outbound call, the egress proxy returns an HTTP 403 response to the agent's network client. The agent handles the error with its own logic. For example, if the blocked call was a tool call, the agent typically reports that the tool call failed and tries a different approach. Policy internals aren't exposed to end users.

Preview limitations and what's coming next

Network egress controls are an additive feature. During preview:

  • Egress controls apply to hosted agents only.
  • Enforcement happens inside the Foundry-managed agent sandbox. Egress controls complement your own network controls, such as Azure Firewall, rather than replace them. They don't delegate enforcement to a customer-managed firewall, and they aren't centrally enforced through Azure Policy.
  • In the portal, rules match on host.

The following capabilities aren't available yet and are planned for future updates:

  • Secret header values — Injecting a header value from a secret reference isn't supported during preview.
  • Rule types such as Azure service tags and IP address ranges.
  • MCP tool policies, PII and data-loss-prevention inspection, and custom webhook hooks.