Install
$ agentstack add skill-aiappsgbb-awesome-gbb-foundry-cross-resource ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Cross-Resource Model Invocation via Foundry AI Gateway
> Status — verified live on 2026-04-23 with Foundry account > xtest-foundry-mr5kfi / project xtest-proj-mr5kfi (Sweden Central) calling > deployment gpt-4o-mini v2024-07-18 hosted on a different Azure OpenAI > account (acme-aoai-shared) through APIM acme-ai-apim. Both ApiKey > and ProjectManagedIdentity auth paths returned PONG on all three > invocation patterns. See "Verified working configuration" at the end. > > Default in worked examples below: gpt-5.4-mini (current > Foundry-routable chat-mini family, May 2026). The original verification > was on gpt-4o-mini; the gateway routes by deployment-name-in-path > regardless of model family, so the recipe is mechanically identical for > any deployment your APIM backend actually carries. The exact API > version string in the connection metadata below is illustrative — > use whatever your backend deployment is registered with (check > az cognitiveservices account deployment show).
1. What this skill solves
You have a Foundry project ("consumer") that needs to invoke models deployed on a different Azure OpenAI / AI Services account ("backend"), fronted by an Azure API Management instance acting as the AI Gateway. You want to address those models via the Foundry-native string connectionName/deploymentName so the application code is identical to a local-deployment call.
┌────────────────────────┐ ┌──────────────────────────┐ ┌─────────────────────────────┐
│ Consumer Foundry │ │ APIM AI Gateway │ │ Backend AI/OpenAI account │
│ project │ Foundry MI │ acme-ai-apim │ APIM MI → │ acme-aoai-shared │
│ xtest-proj-mr5kfi ├───or────────► │ /xtest-aoai ├──Bearer───────►│ gpt-5.4-mini deployment │
│ (Sweden Central) │ ApiKey │ /xtest-aoai-pmi │ (msi token) │ (East US 2) │
│ │ │ │ │ │
│ ApiManagement │ │ validate token / │ │ Cognitive Services User RBAC│
│ connection │ │ enforce subscription │ │ granted to APIM MI │
│ category=ApiManagement│ │ set-backend-service │ │ │
│ target=APIM API URL │ │ authentication-managed- │ │ │
│ authType=ApiKey | PMI │ │ identity → backend │ │ │
└────────────────────────┘ └──────────────────────────┘ └─────────────────────────────┘
The consumer project never holds the backend's API key or RBAC. APIM is the trust boundary.
2. The 100% reliable checklist
Before any agent call, ALL of these must be true. Cross them off in order; the official validator script test_apim_connection.py plus the field-tested probes in §8 below verify each step end-to-end.
| # | Item | How to verify | |---|------|---------------| | 1 | Backend AI Services / Azure OpenAI account exists with the model deployed | az cognitiveservices account deployment list -g -n | | 2 | APIM exists (any SKU; Developer is fine for testing, Standard v2/Premium for prod) and has system-assigned managed identity enabled | az apim show -g -n --query identity | | 3 | APIM MI has Cognitive Services User (or Cognitive Services OpenAI User) on the backend account | az role assignment list --assignee --scope | | 4 | APIM has an API whose path you'll target (e.g., /xtest-aoai) with inbound policy that calls set-backend-service + authentication-managed-identity + Bearer header injection (see §6) | Invoke-RestMethod -Uri https://.azure-api.net//deployments//chat/completions?api-version=2024-10-21 -Headers @{api-key='';...} returns 200 | | 5 | (ApiKey only) An APIM subscription scoped to that API (or to a product the API is in) exists; you have the primary key | az apim subscription show ... | | 6 | (PMI only) APIM API has subscriptionRequired: false AND inbound policy includes ` with containing the **consumer project MI's client/app ID** | API GET shows subscriptionRequired:false; policy GET shows the GUID | | 7 | Consumer Foundry project exists, has Azure AI User RBAC for the caller, and (PMI) has system-assigned managed identity enabled | az cognitiveservices account project show ... --query identity | | 8 | An ApiManagement connection exists on the consumer project (NOT AzureOpenAI, NOT AIServices) | See §5 for the verified PUT body | | 9 | The connection's metadata.models is a **JSON-stringified** array (NOT a real array) and metadata.deploymentInPath matches the backend style | See §5 — this is the most common silent error | | 10 | Caller code uses model="/" and calls responses.create(...) (NOT chat.completions.create`) | See §7 |
If step 4 doesn't return 200 outside Foundry, no Foundry call will work. Always smoke-test the gateway directly first.
3. Decision tree
┌──────────────────────────────────────┐
│ What backend does APIM forward to? │
└─────────────┬────────────────────────┘
│
┌─────────────────────────┴────────────────────────┐
│ │
AOAI / AI Services on /openai OpenAI v1 (/v1/...)
(most common — Azure OpenAI) or Anthropic etc.
│ │
▼ ▼
metadata.deploymentInPath = "true" metadata.deploymentInPath = "false"
metadata.inferenceAPIVersion = "2024-10-21" metadata.inferenceAPIVersion = "" (or omit)
models[].properties.model.format = "OpenAI" models[].properties.model.format = "OpenAI"|"Anthropic"|"NonOpenAI"
modelDiscovery.listModelsEndpoint = "/models"
modelDiscovery.deploymentProvider = "AzureOpenAI"|"OpenAI"|"Anthropic"|"NonOpenAI"
┌──────────────────────────────────────┐
│ How do callers prove identity to APIM?│
└─────────────┬────────────────────────┘
│
┌─────────────────────────┼────────────────────────┐
│ │ │
APIM subscription key Project Managed Identity Both (dual-auth)
│ (no static secrets) │
▼ ▼ ▼
authType: "ApiKey" authType: "ProjectManagedIdentity" Two connections OR
credentials.key: credentials: {} APIM
APIM api: subscription req audience: policy that branches
APIM policy: pass-through "https://cognitiveservices.azure.com"
APIM policy: validate-azure-ad-token
with
containing project MI clientId
APIM api: subscriptionRequired=false
4. APIM-side configuration
4.1 Service URL on the API
The Foundry connection target is https://.azure-api.net/. Inside the API's inbound policy you set-backend-service to the backend account. Foundry then appends /deployments/{dep}/chat/completions?api-version=... (or /v1/responses etc.) to the gateway URL.
Operations on the API (i.e., the routes Foundry will call) must therefore match the AOAI / OpenAI surface. The simplest setup is catch-all with one operation per HTTP verb on the wildcard path /{*path}. For Azure OpenAI backends, all of the following must reach the inbound policy and 200:
POST /deployments/{deployment-id}/chat/completions?api-version=2024-10-21
POST /deployments/{deployment-id}/embeddings?api-version=2024-10-21
POST /v1/responses?api-version=preview (Responses API)
GET /models?api-version=2024-10-21 (model catalogue, used for dynamic discovery)
4.2 ApiKey-only inbound policy (verified working — xtest-aoai)
@("Bearer " + (string)context.Variables["msi-access-token"])
Subscription enforcement is at the API/product level (set subscriptionRequired: true and configure subscriptionKeyParameterNames.header to api-key so the caller sends api-key: , not Ocp-Apim-Subscription-Key). Foundry sends the api-key header.
4.3 PMI-only inbound policy (verified working — xtest-aoai-pmi)
"
header-name="Authorization"
failed-validation-httpcode="401"
failed-validation-error-message="Unauthorized: token did not match expected audience or application">
https://cognitiveservices.azure.com
https://cognitiveservices.azure.com/
@("Bearer " + (string)context.Variables["msi-access-token"])
PMI API also needs subscriptionRequired: false on the API resource (otherwise APIM rejects with 401 before the policy runs).
> **Why ` and not with xmsmirid?** > Both mechanisms work in principle, but the xmsmirid value Foundry's > project MI puts in its token is **not** the project ARM ID — it varies by > resource provider and current APIs. The MI's appid claim is universally > present and stable, so is the documented and > most reliable check. (Verified 2026-04-23: a policy requiring > xms_mirid = returned 401; switching to ` returned 200.)
4.4 Dual-auth inbound policy (ApiKey OR PMI)
"
header-name="Authorization"
failed-validation-httpcode="401">
https://cognitiveservices.azure.com
https://cognitiveservices.azure.com/
.openai.azure.com/openai" />
@("Bearer " + (string)context.Variables["msi-access-token"])
API must have subscriptionRequired: false (so PMI calls aren't rejected up front); the `` branch enforces token validation only when no subscription was used.
5. Connection schema (verified live)
ARM type: Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview. Reachable also via the Foundry data-plane endpoint https://.services.ai.azure.com/api/projects//connections/?api-version=v1.
> ⚠ The metadata.models and metadata.modelDiscovery fields are > JSON-encoded strings, not real JSON objects. This is because Azure > connection metadata is a flat string→string dictionary. Pass the JSON as a > string value or your PUT will be silently ignored / rejected.
5.1 ApiKey connection — verified working PUT body
{
"properties": {
"category": "ApiManagement",
"target": "https://acme-ai-apim.azure-api.net/xtest-aoai",
"authType": "ApiKey",
"credentials": { "key": "" },
"isSharedToAll": true,
"metadata": {
"deploymentInPath": "true",
"inferenceAPIVersion": "2024-10-21",
"models": "[{\"name\":\"gpt-5.4-mini\",\"properties\":{\"model\":{\"name\":\"gpt-5.4-mini\",\"format\":\"OpenAI\",\"version\":\"2026-04-30\",\"publisher\":\"Microsoft\"}}}]"
}
}
}
PUT URL:
https://management.azure.com/subscriptions//resourceGroups//providers/
Microsoft.CognitiveServices/accounts//projects//
connections/?api-version=2025-04-01-preview
Or via the data plane:
PUT https://.services.ai.azure.com/api/projects//connections/?api-version=v1
5.2 PMI connection — verified working PUT body
{
"properties": {
"category": "ApiManagement",
"target": "https://acme-ai-apim.azure-api.net/xtest-aoai-pmi",
"authType": "ProjectManagedIdentity",
"credentials": {},
"audience": "https://cognitiveservices.azure.com",
"isSharedToAll": true,
"metadata": {
"deploymentInPath": "true",
"inferenceAPIVersion": "2024-10-21",
"models": "[{\"name\":\"gpt-5.4-mini\",\"properties\":{\"model\":{\"name\":\"gpt-5.4-mini\",\"format\":\"OpenAI\",\"version\":\"2026-04-30\",\"publisher\":\"Microsoft\"}}}]"
}
}
}
audience lives at properties.audience, NOT in metadata. The data-plane GET strips it from the response, but it is required on PUT and is what Foundry uses when requesting the MI token.
5.3 Metadata reference (full)
| Field | Type | Required | Default | Purpose | |-------|------|----------|---------|---------| | deploymentInPath | string "true"/"false" | Yes (in practice — without it: Upstream gateway returned NotFound) | none | "true" → AOAI shape /deployments/{dep}/chat/completions. "false" → OpenAI v1 shape with model in body. | | inferenceAPIVersion | string | Recommended for AOAI | none | Appended as ?api-version=.... Use 2024-10-21 (or newer GA) for AOAI; leave empty for /v1/responses style backends. | | deploymentAPIVersion | string | Optional | inferenceAPIVersion | Used for modelDiscovery list calls. | | models | string (JSON array, escaped) | Required if no modelDiscovery | none | Static catalogue. Must be JSON-stringified. | | modelDiscovery | string (JSON object, escaped) | Required if no models | none | Dynamic discovery. Must be JSON-stringified. Defaults: listModelsEndpoint=/deployments, getModelEndpoint=/deployments/{deploymentName}, deploymentProvider=AzureOpenAI. Override to /models and OpenAI for OpenAI-v1 style backends. | | customHeaders | string (JSON object) | Optional | none | Adds extra HTTP headers on every Foundry-to-APIM call. | | authConfig | string (JSON object) | Optional | none | For non-Authorization-header auth (e.g., gateway expects x-api-key: Bearer ). |
models[].properties.model.format accepts OpenAI, Anthropic, NonOpenAI. For Azure OpenAI deployments use OpenAI.
5.4 What does NOT work (verified failures)
| Connection variant | Result | |--------------------|--------| | Connection with no metadata at all | 400 Model gateway error: Upstream gateway returned NotFound | | Connection with metadata.modelDiscovery (stringified) but no models | 400 Model gateway error: Upstream gateway returned NotFound (against AOAI backends — AOAI's /deployments data-plane endpoint isn't a list) | | models passed as a real JSON array (not stringified) | Foundry stores it but inference returns Upstream gateway returned NotFound; this is a silent footgun | | authType: "AAD" (legacy alias) on Responses API | Inconsistent — sometimes accepted, sometimes rejected. Use ProjectManagedIdentity for Responses API / hosted agents. | | authType: "ProjectManagedIdentity" on Assistants v1 API | Rejected — "AuthType ProjectManagedIdentity for connection is not supported". The standard-agent Assistants API (/assistants, /threads//runs) only accepts authType: "AAD". | | authType: "AAD" on **private VNet standard ag
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: aiappsgbb
- Source: aiappsgbb/awesome-gbb
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.