Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
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 Managerrole 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.agents1.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-identityA 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:
Set the Foundry project resource ID.
PROJECT_ID="/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.CognitiveServices/accounts/<account-name>/projects/<project-name>"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"Give
PROJECT_PRINCIPAL_IDto 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. |