|
| 1 | +@description('Suffix for resource names to ensure uniqueness') |
| 2 | +@minLength(3) |
| 3 | +param suffix string = uniqueString(resourceGroup().id) |
| 4 | + |
| 5 | +@description('Container Apps environment name') |
| 6 | +@minLength(3) |
| 7 | +param environmentName string = 'aca-env-${suffix}' |
| 8 | + |
| 9 | +@description('Session pool name') |
| 10 | +@minLength(3) |
| 11 | +param sessionPoolName string = 'sp-${suffix}' |
| 12 | + |
| 13 | +@description('The amount of CPU to provide to each container instance, in vCPU counts') |
| 14 | +@minValue(1) |
| 15 | +@maxValue(16) |
| 16 | +param cpu int = 1 |
| 17 | + |
| 18 | +@description('The amount of RAM to provide to each container instance, in GiB') |
| 19 | +@minValue(1) |
| 20 | +@maxValue(16) |
| 21 | +param memory int = 2 |
| 22 | + |
| 23 | +@description('Location of all ACA resources.') |
| 24 | +@allowed([ |
| 25 | + 'eastus' |
| 26 | + 'swedencentral' |
| 27 | + 'northeurope' |
| 28 | +]) |
| 29 | +param location string = 'swedencentral' |
| 30 | + |
| 31 | +@description('Use managed identity for deployment script principal') |
| 32 | +param useManagedIdentity bool = true |
| 33 | + |
| 34 | +@description('An image that implements the code interpreter HTTP API') |
| 35 | +param image string = 'mcr.microsoft.com/k8se/services/codeinterpreter:0.9.18-python3.12' |
| 36 | + |
| 37 | +@description('Model deployment name') |
| 38 | +param modelDeploymentName string = 'my-gpt-4o-mini' |
| 39 | + |
| 40 | +@description('Model to deploy') |
| 41 | +param modelName string = 'gpt-4o-mini' |
| 42 | + |
| 43 | +resource environment 'Microsoft.App/managedEnvironments@2025-10-02-preview' = { |
| 44 | + name: environmentName |
| 45 | + location: location |
| 46 | + properties: { |
| 47 | + workloadProfiles: [ |
| 48 | + { |
| 49 | + name: 'Consumption' |
| 50 | + workloadProfileType: 'Consumption' |
| 51 | + } |
| 52 | + ] |
| 53 | + } |
| 54 | +} |
| 55 | + |
| 56 | +resource sessionPool 'Microsoft.App/sessionPools@2025-10-02-preview' = { |
| 57 | + name: sessionPoolName |
| 58 | + location: location |
| 59 | + properties: { |
| 60 | + environmentId: environment.id |
| 61 | + poolManagementType: 'Dynamic' |
| 62 | + containerType: 'CustomContainer' |
| 63 | + scaleConfiguration: { |
| 64 | + maxConcurrentSessions: 10 |
| 65 | + readySessionInstances: 5 |
| 66 | + } |
| 67 | + dynamicPoolConfiguration: { |
| 68 | + lifecycleConfiguration: { |
| 69 | + cooldownPeriodInSeconds: 600 |
| 70 | + lifecycleType: 'Timed' |
| 71 | + } |
| 72 | + } |
| 73 | + customContainerTemplate: { |
| 74 | + containers: [ |
| 75 | + { |
| 76 | + name: 'jupyterpython' |
| 77 | + image: image |
| 78 | + env: [ |
| 79 | + { |
| 80 | + name: 'SYS_RUNTIME_SANDBOX' |
| 81 | + value: 'AzureContainerApps-DynamicSessions' |
| 82 | + } |
| 83 | + { |
| 84 | + name: 'AZURE_CODE_EXEC_ENV' |
| 85 | + value: 'AzureContainerApps-DynamicSessions-Py3.12' |
| 86 | + } |
| 87 | + { |
| 88 | + name: 'AZURECONTAINERAPPS_SESSIONS_SANDBOX_VERSION' |
| 89 | + value: '7758' |
| 90 | + } |
| 91 | + { |
| 92 | + name: 'JUPYTER_TOKEN' |
| 93 | + value: 'AzureContainerApps-DynamicSessions' |
| 94 | + } |
| 95 | + ] |
| 96 | + resources: { |
| 97 | + cpu: cpu |
| 98 | + memory: '${memory}Gi' |
| 99 | + } |
| 100 | + probes: [ |
| 101 | + { |
| 102 | + type: 'Liveness' |
| 103 | + httpGet: { |
| 104 | + path: '/health' |
| 105 | + port: 6000 |
| 106 | + } |
| 107 | + failureThreshold: 4 |
| 108 | + } |
| 109 | + { |
| 110 | + type: 'Startup' |
| 111 | + httpGet: { |
| 112 | + path: '/health' |
| 113 | + port: 6000 |
| 114 | + } |
| 115 | + failureThreshold: 30 |
| 116 | + periodSeconds: 2 |
| 117 | + } |
| 118 | + ] |
| 119 | + } |
| 120 | + ] |
| 121 | + ingress: { |
| 122 | + targetPort: 6000 |
| 123 | + } |
| 124 | + } |
| 125 | + mcpServerSettings: { |
| 126 | + isMcpServerEnabled: true |
| 127 | + } |
| 128 | + sessionNetworkConfiguration: { |
| 129 | + status: 'egressEnabled' |
| 130 | + } |
| 131 | + } |
| 132 | +} |
| 133 | + |
| 134 | +resource scriptPrincipal 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = if (useManagedIdentity){ |
| 135 | + name: 'deployScriptIdentity-${suffix}' |
| 136 | + location: location |
| 137 | +} |
| 138 | + |
| 139 | +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (useManagedIdentity) { |
| 140 | + name: guid(scriptPrincipal!.id, 'apps-sessionpool-contributor') |
| 141 | + scope: resourceGroup() |
| 142 | + properties: { |
| 143 | + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7669afb-68b2-44b4-9c5f-6d2a47fddda0') // Container Apps SessionPools Contributor |
| 144 | + principalId: scriptPrincipal!.properties.principalId |
| 145 | + principalType: 'ServicePrincipal' |
| 146 | + } |
| 147 | +} |
| 148 | + |
| 149 | +resource deployScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { |
| 150 | + name: 'getmcpkey-${suffix}' |
| 151 | + location: location |
| 152 | + kind: 'AzureCLI' |
| 153 | + identity: useManagedIdentity ? { |
| 154 | + type: 'UserAssigned' |
| 155 | + userAssignedIdentities: { |
| 156 | + '${scriptPrincipal!.id}': {} |
| 157 | + } |
| 158 | + } : null |
| 159 | + properties: { |
| 160 | + azCliVersion: '2.77.0' |
| 161 | + scriptContent: ''' |
| 162 | + az rest --method post --url "$SESSION_POOL_ID/fetchMCPServerCredentials?api-version=2025-02-02-preview" | jq -c '{"key": .apiKey}' > $AZ_SCRIPTS_OUTPUT_PATH |
| 163 | + ''' |
| 164 | + timeout: 'PT30M' |
| 165 | + retentionInterval: 'P1D' |
| 166 | + cleanupPreference: 'OnSuccess' |
| 167 | + environmentVariables: [ |
| 168 | + { |
| 169 | + name: 'SESSION_POOL_ID' |
| 170 | + value: sessionPool.id |
| 171 | + } |
| 172 | + ] |
| 173 | + } |
| 174 | +} |
| 175 | + |
| 176 | +resource aiAccount 'Microsoft.CognitiveServices/accounts@2025-10-01-preview' = { |
| 177 | + name: 'aia-${suffix}' |
| 178 | + location: location |
| 179 | + kind: 'AIServices' |
| 180 | + sku: { |
| 181 | + name: 'S0' |
| 182 | + } |
| 183 | + properties: { |
| 184 | + customSubDomainName: 'myaiaccount-${suffix}' |
| 185 | + allowProjectManagement: true |
| 186 | + } |
| 187 | + |
| 188 | + resource project 'projects' = { |
| 189 | + name: 'aip-${suffix}s' |
| 190 | + properties: { |
| 191 | + description: 'This is my AI project.' |
| 192 | + } |
| 193 | + |
| 194 | + resource mcpConn 'connections' = { |
| 195 | + name: 'aic-${suffix}' |
| 196 | + properties: { |
| 197 | + authType: 'CustomKeys' |
| 198 | + category: 'RemoteTool' |
| 199 | + credentials: { |
| 200 | + keys: { |
| 201 | + 'x-ms-apikey': deployScript.properties.outputs.key |
| 202 | + } |
| 203 | + } |
| 204 | + target: sessionPool.properties.mcpServerSettings.mcpServerEndpoint |
| 205 | + } |
| 206 | + } |
| 207 | + } |
| 208 | + |
| 209 | + resource model 'deployments' = { |
| 210 | + name: modelDeploymentName |
| 211 | + sku: { |
| 212 | + name: 'GlobalStandard' |
| 213 | + capacity: 1 |
| 214 | + } |
| 215 | + properties: { |
| 216 | + model: { |
| 217 | + format: 'OpenAI' |
| 218 | + name: modelName |
| 219 | + } |
| 220 | + } |
| 221 | + } |
| 222 | +} |
| 223 | + |
| 224 | +@description('Outputs the ID of the project connection for the Code Interpreter MCP Tool') |
| 225 | +output AZURE_AI_CONNECTION_ID string = aiAccount::project::mcpConn.id |
| 226 | + |
| 227 | +@description('Model deployment name') |
| 228 | +output AZURE_AI_MODEL_DEPLOYMENT_NAME string = aiAccount::model.name |
| 229 | + |
| 230 | +@description('AI Project Endpoint') |
| 231 | +output AZURE_AI_PROJECT_ENDPOINT string = aiAccount::project.properties.endpoints['AI Foundry API'] |
0 commit comments