|
| 1 | +/* |
| 2 | +Creates a ModelGateway connection on a Foundry project. |
| 3 | +
|
| 4 | +What this does: |
| 5 | + Adds a connection (of category 'ModelGateway') to a project on your LOCAL |
| 6 | + Foundry account. The connection points at the OpenAI-compatible endpoint |
| 7 | + of a REMOTE Foundry account, so model calls made against the local project |
| 8 | + are routed to model deployments hosted on the remote account. |
| 9 | +
|
| 10 | + Authentication uses the remote account's API key (looked up at deploy time |
| 11 | + via listKeys() — no key needs to be passed in). |
| 12 | +
|
| 13 | +Terminology used below: |
| 14 | + - local = the account/project where this connection resource is created |
| 15 | + (also the deployment resource group, unless overridden). |
| 16 | + - remote = the account whose model deployments will be called through |
| 17 | + the connection. |
| 18 | +
|
| 19 | +Deploy: |
| 20 | + az deployment group create \ |
| 21 | + --subscription <subscription-id> \ |
| 22 | + --resource-group <rg-of-local-account> \ |
| 23 | + --template-file foundry-modelgateway-connection-apikey.bicep \ |
| 24 | + --parameters \ |
| 25 | + connectionName=<connection-name> \ |
| 26 | + localAccountName=<local-account> \ |
| 27 | + localProjectName=<local-project> \ |
| 28 | + remoteAccountName=<remote-account> \ |
| 29 | + remoteAccountResourceGroup=<rg-of-remote-account> # only needed if the remote account lives in a different RG than the deployment |
| 30 | +*/ |
| 31 | + |
| 32 | +@description('Name of the new ModelGateway connection to create on the local project. This is the name that will appear in the Foundry portal.') |
| 33 | +param connectionName string |
| 34 | + |
| 35 | +@description('Name of the local Foundry (AIServices) account that hosts the project where this connection will be created. Must already exist in the deployment resource group.') |
| 36 | +param localAccountName string |
| 37 | + |
| 38 | +@description('Name of the project (under the local account) that this connection will be attached to. Must already exist.') |
| 39 | +param localProjectName string |
| 40 | + |
| 41 | +@description('Name of the remote Foundry (AIServices) account whose model deployments will be invoked through this connection. The connection target URL and API key are both derived from this account.') |
| 42 | +param remoteAccountName string |
| 43 | + |
| 44 | +@description('Resource group of the remote Foundry account. Defaults to the deployment resource group (i.e. assumes the remote account is in the same RG as the local account). Set this explicitly only if the remote account is in a different RG — otherwise the deployment fails with ResourceNotFound when it tries to listKeys() on the remote account.') |
| 45 | +param remoteAccountResourceGroup string = resourceGroup().name |
| 46 | + |
| 47 | +@description('If true, the connection is visible to every project on the local account (and shows up in the Foundry portal\'s "Admin-connected models" picker). If false, only the local project listed above can use it. Default: true.') |
| 48 | +param isSharedToAll bool = true |
| 49 | + |
| 50 | +// List of Models: Edit this list of models based on what you need to expose. These models should be available on the remote account |
| 51 | +@description('List of model deployments available on the remote account that should be exposed through this connection. Each entry must match a real deployment on the remote account (same name, model name, and version). Embedding the list here lets clients resolve "<connection-name>/<deployment-name>" without an extra service lookup. Override the default to match the deployments on YOUR remote account.') |
| 52 | +param staticModels array = [ |
| 53 | + { name: 'gpt-4.1-mini' , properties: { model: { name: 'gpt-4.1-mini' , version: '2025-04-14', format: 'OpenAI' } } } |
| 54 | + { name: 'gpt-4.1' , properties: { model: { name: 'gpt-4.1' , version: '2025-04-14', format: 'OpenAI' } } } |
| 55 | + { name: 'gpt-5' , properties: { model: { name: 'gpt-5' , version: '2025-08-07', format: 'OpenAI' } } } |
| 56 | + { name: 'gpt-5.1' , properties: { model: { name: 'gpt-5.1' , version: '2025-11-13', format: 'OpenAI' } } } |
| 57 | + { name: 'gpt-5.2' , properties: { model: { name: 'gpt-5.2' , version: '2025-12-11', format: 'OpenAI' } } } |
| 58 | + { name: 'gpt-5.2-chat' , properties: { model: { name: 'gpt-5.2-chat' , version: '2025-12-11', format: 'OpenAI' } } } |
| 59 | + { name: 'DeepSeek-V3-0324', properties: { model: { name: 'DeepSeek-V3-0324', version: '1' , format: 'OpenAI' } } } |
| 60 | +] |
| 61 | + |
| 62 | +// Target URL: the OpenAI-compatible (/openai/v1) endpoint on the remote |
| 63 | +// Foundry account. This is the same URL the Foundry portal uses when it |
| 64 | +// creates a ModelGateway connection through its UI wizard. |
| 65 | +var targetUrl = 'https://${remoteAccountName}.services.ai.azure.com/openai/v1' |
| 66 | + |
| 67 | +resource localAccount 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { |
| 68 | + name: localAccountName |
| 69 | +} |
| 70 | +resource localProject 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' existing = { |
| 71 | + name: localProjectName |
| 72 | + parent: localAccount |
| 73 | +} |
| 74 | +resource remoteAccount 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { |
| 75 | + name: remoteAccountName |
| 76 | + scope: resourceGroup(remoteAccountResourceGroup) |
| 77 | +} |
| 78 | + |
| 79 | +// Connection metadata. These five fields are required by the ModelGateway |
| 80 | +// connection contract — the portal wizard sets the exact same values, so do |
| 81 | +// NOT omit any of them or substitute your own without a strong reason: |
| 82 | +// - models : stringified JSON of the deployment list above; lets |
| 83 | +// the portal render the model picker and lets the |
| 84 | +// runtime resolve "<connection>/<deployment>" locally. |
| 85 | +// - deploymentInPath : 'false' = deployment name is sent in the request |
| 86 | +// body (OpenAI-style), not in the URL path. |
| 87 | +// - authHeaderName : header used to pass the API key on every call. |
| 88 | +// - authHeaderFormat : token format placed in that header. '{api_key}' |
| 89 | +// means the raw key, with no "Bearer " prefix. |
| 90 | +// - customHeaders : extra static headers to add per request. Empty by |
| 91 | +// default; must still be present as '{}' . |
| 92 | +var metadata = { |
| 93 | + models: string(staticModels) |
| 94 | + deploymentInPath: 'false' |
| 95 | + authHeaderFormat: '{api_key}' |
| 96 | + authHeaderName: 'x-api-key' |
| 97 | + customHeaders: '{}' |
| 98 | +} |
| 99 | + |
| 100 | +resource connection 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = { |
| 101 | + name: connectionName |
| 102 | + parent: localProject |
| 103 | + properties: { |
| 104 | + category: 'ModelGateway' |
| 105 | + target: targetUrl |
| 106 | + authType: 'ApiKey' |
| 107 | + isSharedToAll: isSharedToAll |
| 108 | + credentials: { |
| 109 | + key: remoteAccount.listKeys().key1 |
| 110 | + } |
| 111 | + metadata: metadata |
| 112 | + } |
| 113 | +} |
| 114 | + |
| 115 | +output connectionId string = connection.id |
| 116 | +output connectionName string = connection.name |
| 117 | +output targetUrl string = targetUrl |
| 118 | +output deploymentCount int = length(staticModels) |
| 119 | +output isSharedToAll bool = isSharedToAll |
0 commit comments