|
| 1 | +// ========== main.bicep ========== // |
| 2 | +targetScope = 'resourceGroup' |
| 3 | + |
| 4 | +@minLength(3) |
| 5 | +@maxLength(20) |
| 6 | +@description('A unique prefix for all resources in this deployment. This should be 3-20 characters long:') |
| 7 | +param environmentName string |
| 8 | + |
| 9 | +var uniqueId = toLower(uniqueString(subscription().id, environmentName, resourceGroup().location)) |
| 10 | +var solutionPrefix = 'cps-${padLeft(take(uniqueId, 12), 12, '0')}' |
| 11 | + |
| 12 | +@description('Location used for Azure Cosmos DB, Azure Container App deployment') |
| 13 | +param secondaryLocation string = 'EastUs2' |
| 14 | + |
| 15 | +@minLength(1) |
| 16 | +@description('Location for the Azure AI Content Understanding service deployment:') |
| 17 | +@allowed(['WestUS', 'SwedenCentral', 'AustraliaEast']) |
| 18 | +@metadata({ |
| 19 | + azd: { |
| 20 | + type: 'location' |
| 21 | + } |
| 22 | +}) |
| 23 | +param contentUnderstandingLocation string = 'WestUS' |
| 24 | + |
| 25 | +@metadata({azd: { |
| 26 | + type: 'location' |
| 27 | + usageName: [ |
| 28 | + 'OpenAI.GlobalStandard.gpt-4o,100' |
| 29 | + ] |
| 30 | + } |
| 31 | +}) |
| 32 | +param aiDeploymentsLocation string |
| 33 | + |
| 34 | +@minLength(1) |
| 35 | +@description('GPT model deployment type:') |
| 36 | +@allowed([ |
| 37 | + 'Standard' |
| 38 | + 'GlobalStandard' |
| 39 | +]) |
| 40 | +param deploymentType string = 'GlobalStandard' |
| 41 | + |
| 42 | +@description('Name of the GPT model to deploy:') |
| 43 | +param gptModelName string = 'gpt-4o' |
| 44 | + |
| 45 | +@description('Version of the GPT model to deploy:') |
| 46 | +param gptModelVersion string = '2024-08-06' |
| 47 | + |
| 48 | +@minValue(10) |
| 49 | +@description('Capacity of the GPT deployment:') |
| 50 | +// You can increase this, but capacity is limited per model/region, so you will get errors if you go over |
| 51 | +// https://learn.microsoft.com/en-us/azure/ai-services/openai/quotas-limits |
| 52 | +param gptDeploymentCapacity int = 100 |
| 53 | + |
| 54 | +@description('Minimum number of replicas to be added for Container App') |
| 55 | +param minReplicaContainerApp int = 1 |
| 56 | + |
| 57 | +@description('Maximum number of replicas to be added for Container App') |
| 58 | +param maxReplicaContainerApp int = 1 |
| 59 | + |
| 60 | +@description('Minimum number of replicas to be added for Container Api') |
| 61 | +param minReplicaContainerApi int = 1 |
| 62 | + |
| 63 | +@description('Maximum number of replicas to be added for Container Api') |
| 64 | +param maxReplicaContainerApi int = 1 |
| 65 | + |
| 66 | +@description('Minimum number of replicas to be added for Container Web App') |
| 67 | +param minReplicaContainerWeb int = 1 |
| 68 | + |
| 69 | +@description('Maximum number of replicas to be added for Container Web App') |
| 70 | +param maxReplicaContainerWeb int = 1 |
| 71 | + |
| 72 | +@description('Optional: Existing Log Analytics Workspace Resource ID') |
| 73 | +param existingLogAnalyticsWorkspaceId string = '' |
| 74 | + |
| 75 | +param imageTag string = 'latest' |
| 76 | + |
| 77 | +var containerImageEndPoint = 'cpscontainerreg.azurecr.io' |
| 78 | +var resourceGroupLocation = resourceGroup().location |
| 79 | + |
| 80 | +// Load the abbrevations file required to name the azure resources. |
| 81 | +var abbrs = loadJsonContent('./abbreviations.json') |
| 82 | + |
| 83 | +// ========== Managed Identity ========== // |
| 84 | +module managedIdentityModule 'deploy_managed_identity.bicep' = { |
| 85 | + name: 'deploy_managed_identity' |
| 86 | + params: { |
| 87 | + solutionName: solutionPrefix |
| 88 | + miName: '${abbrs.security.managedIdentity}${solutionPrefix}' |
| 89 | + solutionLocation: resourceGroupLocation |
| 90 | + } |
| 91 | + scope: resourceGroup(resourceGroup().name) |
| 92 | +} |
| 93 | + |
| 94 | +// ========== Key Vault Module ========== // |
| 95 | +module kvault 'deploy_keyvault.bicep' = { |
| 96 | + name: 'deploy_keyvault' |
| 97 | + params: { |
| 98 | + solutionLocation: resourceGroupLocation |
| 99 | + keyvaultName: '${abbrs.security.keyVault}${solutionPrefix}' |
| 100 | + managedIdentityObjectId: managedIdentityModule.outputs.managedIdentityOutput.objectId |
| 101 | + } |
| 102 | + scope: resourceGroup(resourceGroup().name) |
| 103 | +} |
| 104 | + |
| 105 | +// ========== Application insights ========== // |
| 106 | +module applicationInsights 'deploy_app_insights.bicep' = { |
| 107 | + name: 'deploy_app_insights' |
| 108 | + params: { |
| 109 | + existingLogAnalyticsWorkspaceId: existingLogAnalyticsWorkspaceId |
| 110 | + applicationInsightsName: '${abbrs.managementGovernance.applicationInsights}${solutionPrefix}' |
| 111 | + logAnalyticsWorkspaceName: '${abbrs.managementGovernance.logAnalyticsWorkspace}${solutionPrefix}' |
| 112 | + } |
| 113 | +} |
| 114 | + |
| 115 | +// // ========== Container Registry ========== // |
| 116 | +// module containerRegistry 'deploy_container_registry.bicep' = { |
| 117 | +// name: 'deploy_container_registry' |
| 118 | +// params: { |
| 119 | +// environmentName: environmentName |
| 120 | +// } |
| 121 | +// } |
| 122 | + |
| 123 | +// ========== Storage Account ========== // |
| 124 | +module storage 'deploy_storage_account.bicep' = { |
| 125 | + name: 'deploy_storage_account' |
| 126 | + params: { |
| 127 | + solutionLocation: resourceGroupLocation |
| 128 | + managedIdentityObjectId: managedIdentityModule.outputs.managedIdentityOutput.objectId |
| 129 | + saName: '${abbrs.storage.storageAccount}${solutionPrefix}' |
| 130 | + } |
| 131 | +} |
| 132 | + |
| 133 | +// ========== AI Foundry and related resources ========== // |
| 134 | +module aifoundry 'deploy_ai_foundry.bicep' = { |
| 135 | + name: 'deploy_ai_foundry' |
| 136 | + params: { |
| 137 | + solutionName: solutionPrefix |
| 138 | + solutionLocation: aiDeploymentsLocation |
| 139 | + cuLocation: contentUnderstandingLocation |
| 140 | + deploymentType: deploymentType |
| 141 | + gptModelName: gptModelName |
| 142 | + gptModelVersion: gptModelVersion |
| 143 | + gptDeploymentCapacity: gptDeploymentCapacity |
| 144 | + } |
| 145 | + scope: resourceGroup(resourceGroup().name) |
| 146 | +} |
| 147 | + |
| 148 | +module containerAppEnv './container_app/deploy_container_app_env.bicep' = { |
| 149 | + name: 'deploy_container_app_env' |
| 150 | + params: { |
| 151 | + solutionName: solutionPrefix |
| 152 | + containerEnvName: '${abbrs.containers.containerAppsEnvironment}${solutionPrefix}' |
| 153 | + location: secondaryLocation |
| 154 | + logAnalyticsWorkspaceName: applicationInsights.outputs.logAnalyticsWorkspaceName |
| 155 | + logAnalyticsWorkspaceResourceGroup: applicationInsights.outputs.logAnalyticsWorkspaceResourceGroup |
| 156 | + logAnalyticsWorkspaceSubscription: applicationInsights.outputs.logAnalyticsWorkspaceSubscription |
| 157 | + } |
| 158 | +} |
| 159 | + |
| 160 | +module containerApps './container_app/deploy_container_app_api_web.bicep' = { |
| 161 | + name: 'deploy_container_app_api_web' |
| 162 | + params: { |
| 163 | + solutionName: solutionPrefix |
| 164 | + location: secondaryLocation |
| 165 | + appConfigEndPoint: '' |
| 166 | + containerAppApiEndpoint: '' |
| 167 | + containerAppWebEndpoint: '' |
| 168 | + azureContainerRegistry: containerImageEndPoint |
| 169 | + containerAppEnvId: containerAppEnv.outputs.containerEnvId |
| 170 | + containerRegistryReaderId: containerAppEnv.outputs.containerRegistryReaderId |
| 171 | + minReplicaContainerApp: minReplicaContainerApp |
| 172 | + maxReplicaContainerApp: maxReplicaContainerApp |
| 173 | + minReplicaContainerApi: minReplicaContainerApi |
| 174 | + maxReplicaContainerApi: maxReplicaContainerApi |
| 175 | + minReplicaContainerWeb: minReplicaContainerWeb |
| 176 | + maxReplicaContainerWeb: maxReplicaContainerWeb |
| 177 | + imageTag: 'latest' |
| 178 | + } |
| 179 | +} |
| 180 | + |
| 181 | +// ========== Cosmos Database for Mongo DB ========== // |
| 182 | +module cosmosdb './deploy_cosmos_db.bicep' = { |
| 183 | + name: 'deploy_cosmos_db' |
| 184 | + params: { |
| 185 | + cosmosAccountName: '${abbrs.databases.cosmosDBDatabase}${solutionPrefix}' |
| 186 | + solutionLocation: secondaryLocation |
| 187 | + kind: 'MongoDB' |
| 188 | + } |
| 189 | +} |
| 190 | + |
| 191 | +// ========== App Configuration ========== // |
| 192 | +module appconfig 'deploy_app_config_service.bicep' = { |
| 193 | + name: 'deploy_app_config_service' |
| 194 | + scope: resourceGroup(resourceGroup().name) |
| 195 | + params: { |
| 196 | + appConfigName: '${abbrs.developerTools.appConfigurationStore}${solutionPrefix}' |
| 197 | + storageBlobUrl: storage.outputs.storageBlobUrl |
| 198 | + storageQueueUrl: storage.outputs.storageQueueUrl |
| 199 | + openAIEndpoint: aifoundry.outputs.aiServicesTarget |
| 200 | + contentUnderstandingEndpoint: aifoundry.outputs.aiServicesCUEndpoint |
| 201 | + gptModelName: gptModelName |
| 202 | + keyVaultId: kvault.outputs.keyvaultId |
| 203 | + aiProjectConnectionString: aifoundry.outputs.aiProjectConnectionString |
| 204 | + cosmosDbName: cosmosdb.outputs.cosmosAccountName |
| 205 | + } |
| 206 | +} |
| 207 | + |
| 208 | +// ========== Role Assignments ========== // |
| 209 | +module roleAssignments 'deploy_role_assignments.bicep' = { |
| 210 | + name: 'deploy_role_assignments' |
| 211 | + params: { |
| 212 | + appConfigResourceId: appconfig.outputs.appConfigId |
| 213 | + conainerAppPrincipalIds: [ |
| 214 | + containerApps.outputs.containerAppPrincipalId |
| 215 | + containerApps.outputs.containerAppApiPrincipalId |
| 216 | + containerApps.outputs.containerAppWebPrincipalId |
| 217 | + ] |
| 218 | + storageResourceId: storage.outputs.storageId |
| 219 | + storagePrincipalId: storage.outputs.storagePrincipalId |
| 220 | + containerApiPrincipalId: containerApps.outputs.containerAppApiPrincipalId |
| 221 | + containerAppPrincipalId: containerApps.outputs.containerAppPrincipalId |
| 222 | + aiServiceCUId: aifoundry.outputs.aiServicesCuId |
| 223 | + aiServiceId: aifoundry.outputs.aiServicesId |
| 224 | + } |
| 225 | +} |
| 226 | + |
| 227 | +module updateContainerApp './container_app/deploy_container_app_api_web.bicep' = { |
| 228 | + name: 'deploy_update_container_app_update' |
| 229 | + params: { |
| 230 | + solutionName: solutionPrefix |
| 231 | + location: secondaryLocation |
| 232 | + azureContainerRegistry: containerImageEndPoint |
| 233 | + appConfigEndPoint: appconfig.outputs.appConfigEndpoint |
| 234 | + containerAppEnvId: containerAppEnv.outputs.containerEnvId |
| 235 | + containerRegistryReaderId: containerAppEnv.outputs.containerRegistryReaderId |
| 236 | + containerAppWebEndpoint: containerApps.outputs.containweAppWebEndPoint |
| 237 | + containerAppApiEndpoint: containerApps.outputs.containweAppApiEndPoint |
| 238 | + minReplicaContainerApp: minReplicaContainerApp |
| 239 | + maxReplicaContainerApp: maxReplicaContainerApp |
| 240 | + minReplicaContainerApi: minReplicaContainerApi |
| 241 | + maxReplicaContainerApi: maxReplicaContainerApi |
| 242 | + minReplicaContainerWeb: minReplicaContainerWeb |
| 243 | + maxReplicaContainerWeb: maxReplicaContainerWeb |
| 244 | + imageTag: imageTag |
| 245 | + } |
| 246 | + dependsOn: [roleAssignments] |
| 247 | +} |
| 248 | + |
| 249 | +output CONTAINER_WEB_APP_NAME string = containerApps.outputs.containerAppWebName |
| 250 | +output CONTAINER_API_APP_NAME string = containerApps.outputs.containerAppApiName |
| 251 | +output CONTAINER_WEB_APP_FQDN string = containerApps.outputs.containweAppWebEndPoint |
| 252 | +output CONTAINER_APP_NAME string = containerApps.outputs.containerAppName |
| 253 | +output CONTAINER_API_APP_FQDN string = containerApps.outputs.containweAppApiEndPoint |
| 254 | +output CONTAINER_APP_USER_IDENTITY_ID string = containerAppEnv.outputs.containerRegistryReaderId |
| 255 | +output CONTAINER_APP_USER_PRINCIPAL_ID string = containerAppEnv.outputs.containerRegistryReaderPrincipalId |
| 256 | +output AZURE_ENV_IMAGETAG string = imageTag |
0 commit comments