Skip to content

Commit e5bb003

Browse files
Merge pull request #487 from microsoft/dev
feat: Publish Local Build Container to Azure Container Registry - [dev to main]
2 parents b4f707f + 98d619f commit e5bb003

21 files changed

+653
-443
lines changed

.devcontainer/devcontainer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"forwardPorts": [50505],
55
"features": {
66
"ghcr.io/azure/azure-dev/azd:latest": {},
7-
"ghcr.io/devcontainers/features/azure-cli:1": {}
7+
"ghcr.io/devcontainers/features/azure-cli:1": {},
8+
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
89
},
910
"customizations": {
1011
"vscode": {

.devcontainer/setup_env.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ git pull
66
# provide execute permission to quotacheck script
77
sudo chmod +x ./infra/scripts/checkquota_km.sh
88
sudo chmod +x ./infra/scripts/quota_check_params.sh
9-
sudo chmod +x ./infra/scripts/run_process_data_scripts.sh
9+
sudo chmod +x ./infra/scripts/run_process_data_scripts.sh
10+
sudo chmod +x ./infra/scripts/docker-build.sh
11+
sudo chmod +x ./infra/scripts/docker-build.ps1

azure.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,19 @@ metadata:
99
1010

1111
hooks:
12+
preprovision:
13+
windows:
14+
run: |
15+
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"; $logFile = "azd_preprovision_$timestamp.log"; ./infra/scripts/docker-build.ps1 $env:AZURE_SUBSCRIPTION_ID $env:AZURE_ENV_NAME $env:AZURE_LOCATION $env:AZURE_RESOURCE_GROUP $env:USE_LOCAL_BUILD *>&1 | Tee-Object -FilePath $logFile
16+
shell: pwsh
17+
continueOnError: false
18+
interactive: true
19+
posix:
20+
run: |
21+
timestamp=$(date +"%Y%m%d-%H%M%S"); logFile="azd_preprovision_$timestamp.log"; sed -i 's/\r$//' ./infra/scripts/docker-build.sh; ./infra/scripts/docker-build.sh "$AZURE_SUBSCRIPTION_ID" "$AZURE_ENV_NAME" "$AZURE_LOCATION" "$AZURE_RESOURCE_GROUP" "$USE_LOCAL_BUILD" 2>&1 | tee "$logFile"
22+
shell: sh
23+
continueOnError: false
24+
interactive: true
1225
postprovision:
1326
windows:
1427
run: |

infra/deploy_ai_foundry.bicep

Lines changed: 24 additions & 188 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,11 @@ param managedIdentityObjectId string
1414
param existingLogAnalyticsWorkspaceId string = ''
1515

1616
var abbrs = loadJsonContent('./abbreviations.json')
17-
var storageName = '${solutionName}hubstorage'
18-
var storageSkuName = 'Standard_LRS'
1917
var aiServicesName = '${abbrs.ai.aiServices}${solutionName}'
2018
var aiServicesName_cu = '${abbrs.ai.aiServices}${solutionName}-cu'
2119
var location_cu = cuLocation
2220
var workspaceName = '${abbrs.managementGovernance.logAnalyticsWorkspace}${solutionName}'
2321
var applicationInsightsName = '${abbrs.managementGovernance.applicationInsights}${solutionName}'
24-
var containerRegistryName = '${abbrs.containers.containerRegistry}${solutionName}'
2522
var keyvaultName = '${abbrs.security.keyVault}${solutionName}'
2623
var location = solutionLocation //'eastus2'
2724
var aiProjectName = '${abbrs.ai.aiHubProject}${solutionName}'
@@ -49,8 +46,6 @@ var aiModelDeployments = [
4946
}
5047
]
5148

52-
var containerRegistryNameCleaned = replace(containerRegistryName, '-', '')
53-
5449
var useExisting = !empty(existingLogAnalyticsWorkspaceId)
5550
var existingLawSubscription = useExisting ? split(existingLogAnalyticsWorkspaceId, '/')[2] : ''
5651
var existingLawResourceGroup = useExisting ? split(existingLogAnalyticsWorkspaceId, '/')[4] : ''
@@ -89,39 +84,6 @@ resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = {
8984
}
9085
}
9186

92-
resource containerRegistry 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
93-
name: containerRegistryNameCleaned
94-
location: location
95-
sku: {
96-
name: 'Premium'
97-
}
98-
properties: {
99-
adminUserEnabled: false
100-
dataEndpointEnabled: false
101-
networkRuleBypassOptions: 'AzureServices'
102-
networkRuleSet: {
103-
defaultAction: 'Deny'
104-
}
105-
policies: {
106-
quarantinePolicy: {
107-
status: 'disabled'
108-
}
109-
retentionPolicy: {
110-
status: 'enabled'
111-
days: 7
112-
}
113-
trustPolicy: {
114-
status: 'disabled'
115-
type: 'Notary'
116-
}
117-
}
118-
publicNetworkAccess: 'Disabled'
119-
zoneRedundancy: 'Disabled'
120-
}
121-
}
122-
123-
var storageNameCleaned = replace(storageName, '-', '')
124-
12587
resource aiServices 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = {
12688
name: aiServicesName
12789
location: location
@@ -186,147 +148,27 @@ resource aiServicesDeployments 'Microsoft.CognitiveServices/accounts/deployments
186148
}]
187149

188150
resource aiSearch 'Microsoft.Search/searchServices@2024-06-01-preview' = {
189-
name: aiSearchName
190-
location: solutionLocation
191-
sku: {
192-
name: 'basic'
193-
}
194-
properties: {
195-
replicaCount: 1
196-
partitionCount: 1
197-
hostingMode: 'default'
198-
publicNetworkAccess: 'enabled'
199-
networkRuleSet: {
200-
ipRules: []
201-
}
202-
encryptionWithCmk: {
203-
enforcement: 'Unspecified'
204-
}
205-
disableLocalAuth: false
206-
authOptions: {
207-
apiKeyOnly: {}
208-
}
209-
semanticSearch: 'free'
210-
}
211-
}
212-
213-
resource storage 'Microsoft.Storage/storageAccounts@2022-09-01' = {
214-
name: storageNameCleaned
215-
location: location
151+
name: aiSearchName
152+
location: solutionLocation
216153
sku: {
217-
name: storageSkuName
154+
name: 'basic'
218155
}
219-
kind: 'StorageV2'
220156
properties: {
221-
accessTier: 'Hot'
222-
allowBlobPublicAccess: false
223-
allowCrossTenantReplication: false
224-
allowSharedKeyAccess: false
225-
encryption: {
226-
keySource: 'Microsoft.Storage'
227-
requireInfrastructureEncryption: false
228-
services: {
229-
blob: {
230-
enabled: true
231-
keyType: 'Account'
232-
}
233-
file: {
234-
enabled: true
235-
keyType: 'Account'
236-
}
237-
queue: {
238-
enabled: true
239-
keyType: 'Service'
240-
}
241-
table: {
242-
enabled: true
243-
keyType: 'Service'
244-
}
245-
}
157+
replicaCount: 1
158+
partitionCount: 1
159+
hostingMode: 'default'
160+
publicNetworkAccess: 'enabled'
161+
networkRuleSet: {
162+
ipRules: []
246163
}
247-
isHnsEnabled: false
248-
isNfsV3Enabled: false
249-
keyPolicy: {
250-
keyExpirationPeriodInDays: 7
164+
encryptionWithCmk: {
165+
enforcement: 'Unspecified'
251166
}
252-
largeFileSharesState: 'Disabled'
253-
minimumTlsVersion: 'TLS1_2'
254-
networkAcls: {
255-
bypass: 'AzureServices'
256-
defaultAction: 'Allow'
167+
disableLocalAuth: false
168+
authOptions: {
169+
apiKeyOnly: {}
257170
}
258-
supportsHttpsTrafficOnly: true
259-
}
260-
}
261-
262-
@description('This is the built-in Storage Blob Data Contributor.')
263-
resource blobDataContributor 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' existing = {
264-
scope: resourceGroup()
265-
name: 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'
266-
}
267-
268-
resource storageroleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
269-
name: guid(resourceGroup().id, managedIdentityObjectId, blobDataContributor.id)
270-
properties: {
271-
principalId: managedIdentityObjectId
272-
roleDefinitionId:blobDataContributor.id
273-
principalType: 'ServicePrincipal'
274-
}
275-
}
276-
277-
resource storageroleAiServiceAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
278-
name: guid(resourceGroup().id, aiServices.id, blobDataContributor.id)
279-
properties: {
280-
principalId: aiServices.identity.principalId
281-
roleDefinitionId: blobDataContributor.id
282-
principalType: 'ServicePrincipal'
283-
}
284-
}
285-
286-
resource cognitiveServicesUserRoleDefinition 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
287-
scope: aiServices_CU
288-
name: 'a97b65f3-24c7-4388-baec-2e87135dc908'
289-
}
290-
291-
resource cognitiveServicesUserAccessProj 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
292-
name: guid(resourceGroup().id, managedIdentityObjectId, cognitiveServicesUserRoleDefinition.id)
293-
properties: {
294-
principalId: managedIdentityObjectId
295-
roleDefinitionId: cognitiveServicesUserRoleDefinition.id
296-
principalType: 'ServicePrincipal'
297-
}
298-
}
299-
300-
resource cognitiveServicesUserAiServiceAccessProj 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
301-
name: guid(resourceGroup().id, aiServices.id, cognitiveServicesUserRoleDefinition.id)
302-
properties: {
303-
principalId: aiServices.identity.principalId
304-
roleDefinitionId: cognitiveServicesUserRoleDefinition.id
305-
principalType: 'ServicePrincipal'
306-
}
307-
}
308-
309-
310-
resource aiDeveloperRoleDefinition 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
311-
scope: aiServices_CU
312-
name: '64702f94-c441-49e6-a78b-ef80e0188fee'
313-
}
314-
315-
resource aiDeveloperAccessProj 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
316-
name: guid(resourceGroup().id, managedIdentityObjectId, aiDeveloperRoleDefinition.id)
317-
properties: {
318-
principalId: managedIdentityObjectId
319-
roleDefinitionId: aiDeveloperRoleDefinition.id
320-
principalType: 'ServicePrincipal'
321-
}
322-
}
323-
324-
resource aiDeveloperAiServiceAccessProj 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
325-
name: guid(resourceGroup().id, aiServices.id, aiDeveloperRoleDefinition.id)
326-
properties: {
327-
principalId: aiServices.identity.principalId
328-
roleDefinitionId: aiDeveloperRoleDefinition.id
329-
principalType: 'ServicePrincipal'
171+
semanticSearch: 'free'
330172
}
331173
}
332174

@@ -358,20 +200,16 @@ resource project_connection_azureai_search 'Microsoft.CognitiveServices/accounts
358200
}
359201
}
360202

361-
resource project_connection_azure_storage 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = {
362-
name: 'myStorageProjectConnectionName'
363-
parent: aiProject
203+
resource aiUser 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
204+
name: '53ca6127-db72-4b80-b1b0-d745d6d5456d'
205+
}
206+
207+
resource aiUserAccessFoundry 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
208+
name: guid(resourceGroup().id, managedIdentityObjectId, aiUser.id)
364209
properties: {
365-
category: 'AzureBlob'
366-
target: storage.properties.primaryEndpoints.blob
367-
authType: 'AAD'
368-
metadata: {
369-
ApiType: 'Azure'
370-
ResourceId: storage.id
371-
location: storage.location
372-
containerName: 'ai-container'
373-
accountName: storage.name
374-
}
210+
principalId: managedIdentityObjectId
211+
roleDefinitionId: aiUser.id
212+
principalType: 'ServicePrincipal'
375213
}
376214
}
377215

@@ -560,8 +398,6 @@ output logAnalyticsWorkspaceResourceName string = useExisting ? existingLogAnaly
560398
output logAnalyticsWorkspaceResourceGroup string = useExisting ? existingLawResourceGroup : resourceGroup().name
561399
output logAnalyticsWorkspaceSubscription string = useExisting ? existingLawSubscription : subscription().subscriptionId
562400

563-
output storageAccountName string = storageNameCleaned
564-
565401
output azureOpenAIKeyName string = azureOpenAIApiKeyEntry.name
566402

567403
output projectEndpoint string = aiProject.properties.endpoints['AI Foundry API']

infra/deploy_app_service.bicep

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ param appSettings object = {}
1212
param appServicePlanId string
1313
param appImageName string
1414
param userassignedIdentityId string = ''
15+
param useLocalBuild string
1516

1617
resource appService 'Microsoft.Web/sites@2020-06-01' = {
1718
name: solutionName
@@ -27,6 +28,7 @@ resource appService 'Microsoft.Web/sites@2020-06-01' = {
2728
properties: {
2829
serverFarmId: appServicePlanId
2930
siteConfig: {
31+
acrUseManagedIdentityCreds: useLocalBuild == 'true'
3032
alwaysOn: true
3133
ftpsState: 'Disabled'
3234
linuxFxVersion: appImageName

infra/deploy_backend_docker.bicep

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
param imageTag string
2+
param acrName string
23
param applicationInsightsId string
34

45
@description('Solution Location')
@@ -11,8 +12,9 @@ param userassignedIdentityId string
1112
param aiProjectName string
1213
param keyVaultName string
1314
param aiServicesName string
15+
param useLocalBuild string
1416

15-
var imageName = 'DOCKER|kmcontainerreg.azurecr.io/km-api:${imageTag}'
17+
var imageName = 'DOCKER|${acrName}.azurecr.io/km-api:${imageTag}'
1618
//var name = '${solutionName}-api'
1719
param name string
1820
var reactAppLayoutConfig ='''{
@@ -87,6 +89,7 @@ module appService 'deploy_app_service.bicep' = {
8789
appServicePlanId: appServicePlanId
8890
appImageName: imageName
8991
userassignedIdentityId:userassignedIdentityId
92+
useLocalBuild: useLocalBuild
9093
appSettings: union(
9194
appSettings,
9295
{
@@ -177,6 +180,23 @@ resource aiUserAccessFoundry 'Microsoft.Authorization/roleAssignments@2022-04-01
177180
}
178181
}
179182

183+
resource containerRegistry 'Microsoft.ContainerRegistry/registries@2021-09-01' existing = if (useLocalBuild == 'true') {
184+
name: acrName
185+
}
186+
187+
resource AcrPull 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = if (useLocalBuild == 'true') {
188+
name: '7f951dda-4ed3-4680-a7ca-43fe172d538d'
189+
}
190+
191+
resource acrPullRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (useLocalBuild == 'true') {
192+
name: guid(appService.name, AcrPull.id)
193+
scope: containerRegistry
194+
properties: {
195+
roleDefinitionId: AcrPull.id
196+
principalId: appService.outputs.identityPrincipalId
197+
}
198+
}
199+
180200
output appUrl string = appService.outputs.appUrl
181201
output reactAppLayoutConfig string = reactAppLayoutConfig
182202
output appInsightInstrumentationKey string = reference(applicationInsightsId, '2015-05-01').InstrumentationKey

0 commit comments

Comments
 (0)