Skip to content

feat(agents): support private non-ACR registry connections - #9586

Draft
Wei Meng (m5i-work) wants to merge 1 commit into
Azure:mainfrom
m5i-work:m5i/9582-private-registry
Draft

feat(agents): support private non-ACR registry connections#9586
Wei Meng (m5i-work) wants to merge 1 commit into
Azure:mainfrom
m5i-work:m5i/9582-private-registry

Conversation

@m5i-work

Copy link
Copy Markdown
Member

Summary

  • add optional registryConnectionId authoring for hosted container agents
  • map it to definition.container_configuration.registry_connection_id
  • add non-interactive azd ai agent init --registry-connection <name-or-id> support
  • verify external connections by Foundry connection name or ID when an existing project is available
  • validate sibling azure.ai.connection dependencies through uses
  • keep public-image, ACR, and code-deploy behavior unchanged when the property is unset
  • preserve the property through unified azure.yaml and legacy agent-definition round trips

The implementation is registry-neutral: it does not inspect registry vendors, hostnames, token-exchange semantics, or vendor-specific credential fields.

Fixes #9582

Tested scenarios

A real E2E was run in westus2 using a private JFrog Docker repository, a brownfield Foundry project, and JFrog OIDC token exchange bound to the Foundry project managed identity. Anonymous manifest access returned 401 before deployment, confirming that the image was private.

1. Existing connection through non-interactive init

Command:

azd ai agent init --no-prompt \
  --agent-name private-registry-init-agent \
  --image <private-jfrog-host>/<repository>/echo-agent:<tag> \
  --project-id <foundry-project-resource-id> \
  --registry-connection private-registry-init

azd deploy --no-prompt

azd ai agent invoke private-registry-init-agent \
  --protocol invocations \
  --new-session \
  --input-file request.json

Key generated azure.yaml output:

services:
  private-registry-init-agent:
    host: azure.ai.agent
    image: <private-jfrog-host>/<repository>/echo-agent:<tag>
    uses:
      - existing-foundry-project
    registryConnectionId: private-registry-init

The existing external connection was not added to uses.

Key deployed-agent output:

{
  "version": "1",
  "status": "active",
  "definition": {
    "container_configuration": {
      "image": "<private-jfrog-host>/<repository>/echo-agent:<tag>",
      "registry_connection_id": "private-registry-init"
    },
    "protocol_versions": [
      { "protocol": "invocations", "version": "1.0.0" }
    ]
  }
}

Key invocation output:

Agent:    private-registry-init-agent (remote, invocations protocol)
Session:  <session-id> (assigned by server)
Trace ID: <trace-id>

"reply": "Hello! You said: 'Hello from the azd init private-registry demo'."
Server responded in 2.484s

The Foundry session logstream showed container startup, GET /readiness returning 200, and both invocation requests returning 200.

2. Declarative brownfield project and sibling connection

Configuration:

infra:
  provider: microsoft.foundry

services:
  existing-foundry-project:
    host: azure.ai.project
    endpoint: https://<account>.services.ai.azure.com/api/projects/<project>

  private-registry-declarative:
    host: azure.ai.connection
    uses:
      - existing-foundry-project
    category: CustomKeys
    target: ${REGISTRY_URL}
    authType: CustomKeys
    credentials:
      keys:
        audience: ${REGISTRY_AUDIENCE}
        tokenEndpoint: ${REGISTRY_TOKEN_ENDPOINT}
        body.provider_name: ${REGISTRY_PROVIDER}
    metadata:
      type: registry_connection
      mode: oauth_token_exchange

  private-registry-yaml-agent:
    host: azure.ai.agent
    uses:
      - existing-foundry-project
      - private-registry-declarative
    kind: hosted
    name: private-registry-yaml-agent
    image: <private-jfrog-host>/<repository>/echo-agent:<tag>
    registryConnectionId: private-registry-declarative
    protocols:
      - protocol: invocations
        version: 1.0.0

Commands:

azd env set AZURE_AI_PROJECT_ID <foundry-project-resource-id>
azd env set REGISTRY_URL https://<private-jfrog-host>
azd env set REGISTRY_AUDIENCE <entra-audience-app-id>
azd env set REGISTRY_TOKEN_ENDPOINT /access/api/v1/oidc/token
azd env set REGISTRY_PROVIDER <oidc-provider-name>

azd provision --no-prompt
azd deploy --no-prompt

azd ai agent invoke private-registry-yaml-agent \
  --protocol invocations \
  --new-session \
  --input-file request.json

Key provisioning output:

SUCCESS: Your application was provisioned in Azure in 36 seconds.

The provisioned project connection reported:

{
  "name": "private-registry-declarative",
  "properties": {
    "category": "CustomKeys",
    "authType": "CustomKeys",
    "metadata": {
      "type": "registry_connection",
      "mode": "oauth_token_exchange"
    }
  }
}

Key deployment and invocation output:

private-registry-declarative  Done
private-registry-yaml-agent   Done
SUCCESS: Your application was deployed to Azure in 48 seconds.

Agent:    private-registry-yaml-agent (remote, invocations protocol)
Session:  <session-id> (assigned by server)
Trace ID: <trace-id>

"reply": "Hello! You said: 'Hello from the declarative brownfield private-registry demo'."
Server responded in 2.562s

The deployed definition contained container_configuration.registry_connection_id: private-registry-declarative, and the Foundry session logstream showed readiness and invocation HTTP 200 responses.

E2E finding / draft follow-up

The declarative deployment initially failed before the extension deploy phase because azd core attempted an anonymous local pull of the private source image:

Pulling container source image
Error response from daemon: ... Authentication is required

Adding the following to the declarative agent service prevented the host-side source-image pull and allowed Foundry to pull the image through the project connection:

docker:
  remoteBuild: true

No remote build occurred in the successful run; the extension deployed the original image as a pre-built image. Before this PR is marked ready, we should either prevent the core pre-package pull automatically for registry-backed agent images or make this setting part of the documented declarative contract.

Trace capture

The E2E retained sanitized local artifacts for CLI demo preparation:

  • PTY transcripts for init, provision, deploy, and invoke
  • generated and declarative azure.yaml files
  • deployed agent-version JSON
  • raw invocation response headers and bodies
  • CLI trace IDs and APIM/request/invocation/session correlation IDs
  • hosted-agent session SSE logstreams

No Application Insights connection was configured for this temporary project, so runtime traces came from Foundry hosted-session logstreams rather than App Insights. The artifact bundle passed a scan against the JFrog username and administrator token.

All temporary Foundry, Entra, JFrog, session, and image resources were deleted after the run; the test resource group was empty afterward.

Automated validation

cd cli/azd/extensions/azure.ai.agents
go build ./...
go test ./... -short
go fix ./...

# Repository/schema checks
git diff --check
jq empty schemas/azure.ai.agent.json

Focused coverage includes:

  • authoring and legacy round trips
  • schema constraints
  • exact REST placement and omission
  • code-deploy omission
  • public non-ACR and ACR behavior without a registry connection
  • non-interactive flag validation and generated configuration
  • external connection name/ID lookup
  • sibling connection uses validation
  • arbitrary credentials.keys.body.* pass-through

Adds registry-neutral hosted-agent authoring, non-interactive init support, Foundry connection validation, and registry_connection_id REST mapping.\n\nFixes Azure#9582
@m5i-work Wei Meng (m5i-work) added feature Feature request area/extensions Extensions (general) ext-agents azure.ai.agents extension labels Aug 14, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
6 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/extensions Extensions (general) ext-agents azure.ai.agents extension feature Feature request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[azure.ai.agents] Support private non-ACR registry connections for hosted agents

1 participant