Skip to content

Commit 9c51956

Browse files
updates in custom deployment
1 parent c07d99c commit 9c51956

3 files changed

Lines changed: 132 additions & 55 deletions

File tree

infra/main_custom.bicep

Lines changed: 126 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ param backendImageName string = 'content-gen-api'
141141
@description('Optional. Image tag for container deployment. Leave empty to skip ACI deployment.')
142142
param imageTag string
143143

144+
@description('Optional. Azure Container Registry name (unused - ACR name is auto-generated). Declared for parameter file compatibility.')
145+
#disable-next-line no-unused-params
146+
param acrName string = ''
147+
144148
@description('Optional. Created by user name.')
145149
param createdBy string = contains(deployer(), 'userPrincipalName')? split(deployer().userPrincipalName, '@')[0]: deployer().objectId
146150

@@ -160,6 +164,7 @@ var solutionSuffix = toLower(trim(replace(
160164
''
161165
)))
162166

167+
// ACR name is always auto-generated in custom deployment
163168
var acrResourceName = 'cr${solutionSuffix}'
164169

165170
var cosmosDbZoneRedundantHaRegionPairs = {
@@ -381,9 +386,16 @@ module containerRegistry 'br/public:avm/res/container-registry/registry:0.9.0' =
381386
enableTelemetry: enableTelemetry
382387
acrSku: 'Standard'
383388
acrAdminUserEnabled: false
384-
anonymousPullEnabled: true // Allows ACI to pull images without credentials
389+
anonymousPullEnabled: false
385390
publicNetworkAccess: 'Enabled'
386391
networkRuleBypassOptions: 'AzureServices'
392+
roleAssignments: [
393+
{
394+
principalId: userAssignedIdentity.outputs.principalId
395+
roleDefinitionIdOrName: '7f951dda-4ed3-4680-a7ca-43fe172d538d' // AcrPull
396+
principalType: 'ServicePrincipal'
397+
}
398+
]
387399
}
388400
}
389401

@@ -889,61 +901,120 @@ module webSite 'modules/web-sites.bicep' = {
889901
}
890902

891903
// ========== Container Instance (Backend API) ========== //
892-
// CUSTOM DEPLOYMENT: ACI is skipped when imageTag='none' (first run), deployed after images are built
904+
// CUSTOM DEPLOYMENT: Inline ACI definition with managed identity auth for ACR
893905
var containerInstanceName = 'aci-${solutionSuffix}'
894906
var backendImageUrl = '${containerRegistry.outputs.loginServer}/${backendImageName}:${imageTag}'
907+
var aciPort = 8000
908+
var isPrivateNetworking = enablePrivateNetworking
909+
// Construct identity resource ID from known values (required for deployment-time calculation)
910+
var userAssignedIdentityResourceIdForACI = '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/${userAssignedIdentityResourceName}'
895911
// Deploy ACI only when imageTag is set to a real tag (not 'none')
896912
var shouldDeployACI = !empty(imageTag) && imageTag != 'none'
897-
module containerInstance 'modules/container-instance.bicep' = if (shouldDeployACI) {
898-
name: take('module.container-instance.${containerInstanceName}', 64)
899-
params: {
900-
name: containerInstanceName
901-
location: solutionLocation
902-
tags: tags
903-
containerImage: backendImageUrl
904-
cpu: 2
905-
memoryInGB: 4
906-
port: 8000
907-
// Only pass subnetResourceId when private networking is enabled
908-
subnetResourceId: enablePrivateNetworking ? virtualNetwork!.outputs.aciSubnetResourceId : ''
909-
userAssignedIdentityResourceId: userAssignedIdentity.outputs.resourceId
910-
enableTelemetry: enableTelemetry
911-
environmentVariables: [
912-
// Azure OpenAI Settings
913-
{ name: 'AZURE_OPENAI_ENDPOINT', value: 'https://${aiFoundryAiServicesResourceName}.openai.azure.com/' }
914-
{ name: 'AZURE_OPENAI_GPT_MODEL', value: gptModelName }
915-
{ name: 'AZURE_OPENAI_IMAGE_MODEL', value: imageModelConfig[imageModelChoice].name }
916-
{ name: 'AZURE_OPENAI_GPT_IMAGE_ENDPOINT', value: imageModelChoice != 'none' ? 'https://${aiFoundryAiServicesResourceName}.openai.azure.com/' : '' }
917-
{ name: 'AZURE_OPENAI_API_VERSION', value: azureOpenaiAPIVersion }
918-
// Azure Cosmos DB Settings
919-
{ name: 'AZURE_COSMOS_ENDPOINT', value: 'https://cosmos-${solutionSuffix}.documents.azure.com:443/' }
920-
{ name: 'AZURE_COSMOS_DATABASE_NAME', value: cosmosDBDatabaseName }
921-
{ name: 'AZURE_COSMOS_PRODUCTS_CONTAINER', value: cosmosDBProductsContainer }
922-
{ name: 'AZURE_COSMOS_CONVERSATIONS_CONTAINER', value: cosmosDBConversationsContainer }
923-
// Azure Blob Storage Settings
924-
{ name: 'AZURE_BLOB_ACCOUNT_NAME', value: storageAccountName }
925-
{ name: 'AZURE_BLOB_PRODUCT_IMAGES_CONTAINER', value: productImagesContainer }
926-
{ name: 'AZURE_BLOB_GENERATED_IMAGES_CONTAINER', value: generatedImagesContainer }
927-
// Azure AI Search Settings
928-
{ name: 'AZURE_AI_SEARCH_ENDPOINT', value: 'https://${aiSearchName}.search.windows.net' }
929-
{ name: 'AZURE_AI_SEARCH_PRODUCTS_INDEX', value: azureSearchIndex }
930-
{ name: 'AZURE_AI_SEARCH_IMAGE_INDEX', value: 'product-images' }
931-
// App Settings
932-
{ name: 'AZURE_CLIENT_ID', value: userAssignedIdentity.outputs.clientId }
933-
{ name: 'PORT', value: '8000' }
934-
{ name: 'WORKERS', value: '4' }
935-
{ name: 'RUNNING_IN_PRODUCTION', value: 'true' }
936-
// Azure AI Foundry Settings
937-
{ name: 'USE_FOUNDRY', value: useFoundryMode ? 'true' : 'false' }
938-
{ name: 'AZURE_AI_PROJECT_ENDPOINT', value: aiFoundryAiProjectEndpoint }
939-
{ name: 'AZURE_AI_MODEL_DEPLOYMENT_NAME', value: gptModelName }
940-
{ name: 'AZURE_AI_IMAGE_MODEL_DEPLOYMENT', value: imageModelConfig[imageModelChoice].name }
941-
// Logging Settings
942-
{ name: 'AZURE_BASIC_LOGGING_LEVEL', value: 'INFO' }
943-
{ name: 'AZURE_PACKAGE_LOGGING_LEVEL', value: 'WARNING' }
944-
{ name: 'AZURE_LOGGING_PACKAGES', value: '' }
945-
// Application Insights
946-
{ name: 'APPLICATIONINSIGHTS_CONNECTION_STRING', value: enableMonitoring ? applicationInsights!.outputs.connectionString : '' }
913+
914+
#disable-next-line no-deployments-resources
915+
resource aciTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry && shouldDeployACI) {
916+
name: '46d3xbcp.res.containerinstance.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, solutionLocation), 0, 4)}'
917+
properties: {
918+
mode: 'Incremental'
919+
template: {
920+
'$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
921+
contentVersion: '1.0.0.0'
922+
resources: []
923+
}
924+
}
925+
}
926+
927+
resource containerInstance 'Microsoft.ContainerInstance/containerGroups@2025-09-01' = if (shouldDeployACI) {
928+
name: containerInstanceName
929+
location: solutionLocation
930+
tags: tags
931+
identity: {
932+
type: 'UserAssigned'
933+
userAssignedIdentities: {
934+
'${userAssignedIdentityResourceIdForACI}': {}
935+
}
936+
}
937+
properties: {
938+
containers: [
939+
{
940+
name: containerInstanceName
941+
properties: {
942+
image: backendImageUrl
943+
resources: {
944+
requests: {
945+
cpu: 2
946+
memoryInGB: 4
947+
}
948+
}
949+
ports: [
950+
{
951+
port: aciPort
952+
protocol: 'TCP'
953+
}
954+
]
955+
environmentVariables: [
956+
// Azure OpenAI Settings
957+
{ name: 'AZURE_OPENAI_ENDPOINT', value: 'https://${aiFoundryAiServicesResourceName}.openai.azure.com/' }
958+
{ name: 'AZURE_OPENAI_GPT_MODEL', value: gptModelName }
959+
{ name: 'AZURE_OPENAI_IMAGE_MODEL', value: imageModelConfig[imageModelChoice].name }
960+
{ name: 'AZURE_OPENAI_GPT_IMAGE_ENDPOINT', value: imageModelChoice != 'none' ? 'https://${aiFoundryAiServicesResourceName}.openai.azure.com/' : '' }
961+
{ name: 'AZURE_OPENAI_API_VERSION', value: azureOpenaiAPIVersion }
962+
// Azure Cosmos DB Settings
963+
{ name: 'AZURE_COSMOS_ENDPOINT', value: 'https://cosmos-${solutionSuffix}.documents.azure.com:443/' }
964+
{ name: 'AZURE_COSMOS_DATABASE_NAME', value: cosmosDBDatabaseName }
965+
{ name: 'AZURE_COSMOS_PRODUCTS_CONTAINER', value: cosmosDBProductsContainer }
966+
{ name: 'AZURE_COSMOS_CONVERSATIONS_CONTAINER', value: cosmosDBConversationsContainer }
967+
// Azure Blob Storage Settings
968+
{ name: 'AZURE_BLOB_ACCOUNT_NAME', value: storageAccountName }
969+
{ name: 'AZURE_BLOB_PRODUCT_IMAGES_CONTAINER', value: productImagesContainer }
970+
{ name: 'AZURE_BLOB_GENERATED_IMAGES_CONTAINER', value: generatedImagesContainer }
971+
// Azure AI Search Settings
972+
{ name: 'AZURE_AI_SEARCH_ENDPOINT', value: 'https://${aiSearchName}.search.windows.net' }
973+
{ name: 'AZURE_AI_SEARCH_PRODUCTS_INDEX', value: azureSearchIndex }
974+
{ name: 'AZURE_AI_SEARCH_IMAGE_INDEX', value: 'product-images' }
975+
// App Settings
976+
{ name: 'AZURE_CLIENT_ID', value: userAssignedIdentity.outputs.clientId }
977+
{ name: 'PORT', value: '8000' }
978+
{ name: 'WORKERS', value: '4' }
979+
{ name: 'RUNNING_IN_PRODUCTION', value: 'true' }
980+
// Azure AI Foundry Settings
981+
{ name: 'USE_FOUNDRY', value: useFoundryMode ? 'true' : 'false' }
982+
{ name: 'AZURE_AI_PROJECT_ENDPOINT', value: aiFoundryAiProjectEndpoint }
983+
{ name: 'AZURE_AI_MODEL_DEPLOYMENT_NAME', value: gptModelName }
984+
{ name: 'AZURE_AI_IMAGE_MODEL_DEPLOYMENT', value: imageModelConfig[imageModelChoice].name }
985+
// Logging Settings
986+
{ name: 'AZURE_BASIC_LOGGING_LEVEL', value: 'INFO' }
987+
{ name: 'AZURE_PACKAGE_LOGGING_LEVEL', value: 'WARNING' }
988+
{ name: 'AZURE_LOGGING_PACKAGES', value: '' }
989+
// Application Insights
990+
{ name: 'APPLICATIONINSIGHTS_CONNECTION_STRING', value: enableMonitoring ? applicationInsights!.outputs.connectionString : '' }
991+
]
992+
}
993+
}
994+
]
995+
osType: 'Linux'
996+
restartPolicy: 'Always'
997+
subnetIds: isPrivateNetworking ? [
998+
{
999+
id: virtualNetwork!.outputs.aciSubnetResourceId
1000+
}
1001+
] : null
1002+
ipAddress: {
1003+
type: isPrivateNetworking ? 'Private' : 'Public'
1004+
ports: [
1005+
{
1006+
port: aciPort
1007+
protocol: 'TCP'
1008+
}
1009+
]
1010+
dnsNameLabel: isPrivateNetworking ? null : containerInstanceName
1011+
}
1012+
// Managed identity auth for ACR (instead of anonymous pull)
1013+
imageRegistryCredentials: [
1014+
{
1015+
server: containerRegistry.outputs.loginServer
1016+
identity: userAssignedIdentityResourceIdForACI
1017+
}
9471018
]
9481019
}
9491020
}
@@ -1037,13 +1108,13 @@ output AZURE_APPLICATION_INSIGHTS_CONNECTION_STRING string = (enableMonitoring &
10371108
output AZURE_ENV_OPENAI_LOCATION string = azureAiServiceLocation
10381109

10391110
@description('Contains Container Instance Name')
1040-
output CONTAINER_INSTANCE_NAME string = shouldDeployACI ? containerInstance!.outputs.name : ''
1111+
output CONTAINER_INSTANCE_NAME string = shouldDeployACI ? containerInstance!.name : ''
10411112

10421113
@description('Contains Container Instance IP Address')
1043-
output CONTAINER_INSTANCE_IP string = shouldDeployACI ? containerInstance!.outputs.ipAddress : ''
1114+
output CONTAINER_INSTANCE_IP string = shouldDeployACI ? containerInstance!.properties.ipAddress.ip : ''
10441115

10451116
@description('Contains Container Instance FQDN (only for non-private networking)')
1046-
output CONTAINER_INSTANCE_FQDN string = (shouldDeployACI && !enablePrivateNetworking) ? containerInstance!.outputs.fqdn : ''
1117+
output CONTAINER_INSTANCE_FQDN string = (shouldDeployACI && !isPrivateNetworking) ? containerInstance!.properties.ipAddress.fqdn : ''
10471118

10481119
@description('Contains ACR Name')
10491120
output ACR_NAME string = acrResourceName

infra/scripts/package_frontend.ps1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22
# This script is called by AZD during prepackage hook
33
# Working directory is ./src/app/frontend-server (project directory)
44

5+
$ErrorActionPreference = 'Stop'
6+
57
Write-Host "Building React frontend..." -ForegroundColor Cyan
68

79
# Build React frontend (one level up)
810
Push-Location ../frontend
911
npm ci --loglevel=error
12+
if ($LASTEXITCODE -ne 0) { throw "npm ci failed with exit code $LASTEXITCODE" }
1013
npm run build -- --outDir ../frontend-server/static
14+
if ($LASTEXITCODE -ne 0) { throw "npm run build failed with exit code $LASTEXITCODE" }
1115
Pop-Location
1216

1317
Write-Host "Packaging frontend server..." -ForegroundColor Cyan

infra/scripts/package_frontend.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#!/bin/bash
2+
set -euo pipefail
3+
24
# Package frontend for App Service deployment
35
# This script is called by AZD during prepackage hook
46
# Working directory is ./src/app/frontend-server (project directory)

0 commit comments

Comments
 (0)