Bring your own registry for hosted agents

This article shows how to deploy a Microsoft Foundry hosted agent that uses a container image stored in an authenticated container registry outside Azure, such as JFrog Artifactory or a self-hosted Docker Distribution registry. The solution uses OpenID Connect (OIDC) token exchange so that the agent can authenticate with the registry by using short-lived credentials instead of stored secrets, such as registry usernames, passwords, or access tokens.

In this article, private registry refers to any container registry that requires authentication. It doesn't refer to an Azure Container Registry (ACR) secured with an Azure private endpoint. To use an existing ACR or an ACR with public network access disabled, see Deploy a hosted agent with a private Azure Container Registry.

This approach works with non-Azure registries that support OIDC token exchange. A Microsoft Entra application and the Foundry project managed identity participate in the authentication flow, allowing the registry to issue short-lived credentials for image pulls.

Prerequisites

  • An Azure subscription.

  • An existing Foundry project. You need the Foundry Project Manager role to create a project connection. See Hosted agent permissions reference.

    Important

    The Foundry RBAC roles were recently renamed. Foundry User, Foundry Owner, Foundry Account Owner, and Foundry Project Manager were previously named Azure AI User, Azure AI Owner, Azure AI Account Owner, and Azure AI Project Manager. You might still see the previous names in some places while the rename rolls out. The role IDs and core permissions are unchanged by the rename.

  • Azure CLI, installed and authenticated.

  • A Bash-compatible shell.

  • Azure Developer CLI, version 1.32.0 or later, installed and authenticated.

  • The Azure Developer CLI Foundry extensions, including azure.ai.agents 1.0.0-beta.13 or later. See Install the Azure Developer CLI Foundry extensions.

  • For the Python SDK workflow, Python 3.10 or later and the Azure AI Projects and Azure Identity packages.

    pip install azure-ai-projects azure-identity
    
  • A fully qualified prebuilt image in an authenticated container registry outside Azure or in a self-hosted Docker Distribution registry.

  • Permission to configure identity trust and read-only repository access in the registry or its authentication service.

  • The registry target, token audience, and token-exchange endpoint from your registry administrator.

A registry that supports OpenID Connect (OIDC) isn't automatically compatible with this workflow. The registry's authentication service must be able to exchange a Microsoft Foundry project managed identity token for short-lived credentials that the registry accepts for image pulls. JFrog Artifactory is one validated example of a compatible implementation. Docker Distribution requires an authentication adapter because its native bearer token authentication flow doesn't provide an OAuth 2.0 token exchange endpoint.

Create an Entra application

Create an Entra application to provide the audience value used during token exchange:

ENTRA_APPLICATION_ID=$(az ad app create \
  --display-name "Foundry-Registry-Agent" \
  --query appId \
  --output tsv)

echo "$ENTRA_APPLICATION_ID"

Record the application (client) ID. The application doesn't need a client secret for this flow. Give the application ID to your registry administrator to configure as the expected audience in the registry's OIDC provider or token-exchange service.

Configure OIDC for your registry

Configure the registry authentication service to trust the Foundry project managed identity and grant it pull access only to the repository that contains the agent image:

  1. Set the Foundry project resource ID.

    PROJECT_ID="/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.CognitiveServices/accounts/<account-name>/projects/<project-name>"
    
  2. Get the project managed identity object ID.

    PROJECT_PRINCIPAL_ID=$(az resource show \
      --ids "$PROJECT_ID" \
      --api-version 2025-06-01 \
      --query identity.principalId \
      --output tsv)
    
    echo "$PROJECT_PRINCIPAL_ID"
    
  3. Give PROJECT_PRINCIPAL_ID to your registry administrator. Bind that identity to read-only access for the repository that contains the agent image.

Choose the setup that matches your registry.

In JFrog Artifactory, create an OIDC integration and identity mapping that bind the project managed identity to a group with read-only repository access. Use /access/api/v1/oidc/token as the token endpoint. Record the JFrog OIDC provider name because the connection sends it to the token endpoint as body.provider_name.

Configure the Entra application ID from Step 1 as the JFrog OIDC audience. The audience in the Foundry connection must match the audience in the JFrog OIDC integration.

Record these values from your JFrog administrator:

Value Description
Registry target HTTPS origin for the registry, such as https://registry.contoso.com.
Audience Microsoft Entra application ID configured as the JFrog OIDC audience.
Token endpoint /access/api/v1/oidc/token.
OIDC provider name Provider name sent as body.provider_name.

Create the registry connection

Create a CustomKeys connection in the same Foundry project that hosts the agent. The connection metadata identifies it as an image registry connection that uses OAuth token exchange.

Set the project endpoint. Include the project name in the /api/projects/<project-name> path:

PROJECT_ENDPOINT="https://<account-name>.services.ai.azure.com/api/projects/<project-name>"

Create the connection. Choose the command for your registry authentication service.

For JFrog Artifactory:

azd ai connection create private-registry \
  --project-endpoint "$PROJECT_ENDPOINT" \
  --kind custom-keys \
  --target "https://<registry-host>" \
  --auth-type custom-keys \
  --custom-key "audience=<token-audience>" \
  --custom-key "tokenEndpoint=<token-exchange-path>" \
  --custom-key "body.provider_name=<oidc-provider-name>" \
  --metadata "type=registry_connection" \
  --metadata "mode=oauth_token_exchange"

For a Docker Distribution authentication adapter:

azd ai connection create private-registry \
  --project-endpoint "$PROJECT_ENDPOINT" \
  --kind custom-keys \
  --target "https://<registry-host>" \
  --auth-type custom-keys \
  --custom-key "audience=<token-audience>" \
  --custom-key "tokenEndpoint=<adapter-token-exchange-path>" \
  --metadata "type=registry_connection" \
  --metadata "mode=oauth_token_exchange"

Add a body.<field-name> custom key for each extra form field that your adapter requires.

Verify the connection without displaying its credentials:

azd ai connection show private-registry \
  --project-endpoint "$PROJECT_ENDPOINT"

The output identifies a CustomKeys connection with the registry target and the registry_connection metadata.

Configure private network access

Choose the option that matches your Foundry project network configuration.

When the registry target and token endpoint are available through public HTTPS endpoints, you don't need to configure extra registry networking.

Create and deploy the agent

Create and deploy the hosted agent from the prebuilt private registry image.

Initialize a hosted agent project with the fully qualified image and the connection name. The connection must already exist in the selected project.

azd ai agent init --no-prompt \
  --agent-name private-registry-agent \
  --image <registry-host>/<repository>/agent:<tag> \
  --project-id "$PROJECT_ID" \
  --registry-connection private-registry

Review the generated azure.yaml. Keep the azure.ai.project service and the agent's uses relationship. The agent service must include the prebuilt image, docker.imagePassthrough: true, and the registry connection name:

services:
  existing-project:
    host: azure.ai.project
    endpoint: ${FOUNDRY_PROJECT_ENDPOINT}

  private-registry-agent:
    host: azure.ai.agent
    uses:
      - existing-project
    kind: hosted
    name: private-registry-agent
    image: "<registry-host>/<repository>/agent:<tag>"
    docker:
      imagePassthrough: true
    registryConnectionId: private-registry
    protocols:
      - protocol: responses
        version: "2.0.0"

An existing connection doesn't belong in the uses list. The registryConnectionId value can be the connection's Foundry name or resource ID.

If the agent requires runtime configuration, store nonsecret values in the active azd environment and reference them from the agent service's env map. Don't store secrets directly in azure.yaml. See Configure environment variables for a hosted agent.

Deploy the agent:

azd deploy --no-prompt

Verify and invoke the agent

Wait for the hosted agent to become active, and then send a test request.

Check the agent status:

azd ai agent show

Wait for the status to change to Active, and then invoke the agent:

azd ai agent invoke "Respond with a short deployment confirmation."

A successful deployment confirms that Foundry exchanged the identity token, retrieved the image, and started the container. A successful invocation also confirms that the container implements the configured Responses protocol.

Troubleshoot registry deployment

Symptom Likely cause Resolution
Token exchange returns invalid_subject The registry mapping doesn't identify the project managed identity. Compare the identity claim used by the registry with the project's managed identity object ID.
Token exchange returns invalid_audience or invalid_target The connection audience and registry identity-provider audience don't match. Use the same resource identifier in the registry provider and the connection's audience key.
Token exchange rejects the issuer The registry provider expects a different token issuer. Inspect the presented token in the registry authentication logs, and configure the provider to match its iss claim exactly.
Token exchange succeeds, but the image pull returns 403 The mapped identity lacks repository pull permission, or the issued token has the wrong scope. Grant read-only pull permission for the image repository and verify the token's repository scope.
A Docker Distribution registry returns 401 after token exchange The adapter-issued credential doesn't satisfy the registry's bearer-token requirements. Verify the registry service, repository scope, token issuer, signing key, and trust configuration.
The registry connection can't be found The connection exists in a different Foundry project, or the name doesn't match. Create the connection in the selected project and set registryConnectionId to its exact name or resource ID.
The request times out Foundry can't resolve or reach the registry or token endpoint. Verify private DNS, private endpoint approval, routes, firewalls, and outbound TCP 443 for both endpoints.