From 0029832821a3567f3d5692eeb974d4df8079cbcf Mon Sep 17 00:00:00 2001 From: VishalSh-Microsoft Date: Tue, 14 Jul 2026 12:42:39 +0530 Subject: [PATCH 1/2] feat: add Azure Container Registry module, enhance deployment scripts, and update model configuration --- docs/AzureGPTQuotaSettings.md | 2 +- docs/CustomizingAzdParameters.md | 2 +- docs/EXAMPLE-CustomizingAzdParameters.md | 4 +- docs/quota_check.md | 8 +- infra/main.bicep | 17 +- infra/main.json | 3409 ++-------------------- infra/modules/containerRegistry.bicep | 104 + scripts/build_and_push_images.ps1 | 248 +- scripts/build_and_push_images.sh | 203 +- scripts/checkquota.sh | 2 +- scripts/quota_check_params.sh | 2 +- src/backend/.env.sample | 10 +- 12 files changed, 690 insertions(+), 3321 deletions(-) create mode 100644 infra/modules/containerRegistry.bicep diff --git a/docs/AzureGPTQuotaSettings.md b/docs/AzureGPTQuotaSettings.md index d286ac20..cc0fb1b2 100644 --- a/docs/AzureGPTQuotaSettings.md +++ b/docs/AzureGPTQuotaSettings.md @@ -5,6 +5,6 @@ 3. **Go to** the `Management Center` from the bottom-left navigation menu. 4. Select `Quota` - Click on the `GlobalStandard` dropdown. - - Select the required **GPT model** (`GPT-4, GPT-4o`). + - Select the required **GPT model** (`GPT-4, GPT-5.1`). - Choose the **region** where the deployment is hosted. 5. Request More Quota or delete any unused model deployments as needed. \ No newline at end of file diff --git a/docs/CustomizingAzdParameters.md b/docs/CustomizingAzdParameters.md index 87b30d5a..9479c7e2 100644 --- a/docs/CustomizingAzdParameters.md +++ b/docs/CustomizingAzdParameters.md @@ -12,7 +12,7 @@ By default this template will use the environment name as the prefix to prevent | `AZURE_LOCATION` | string | `` | Location of the Azure resources. Controls where the infrastructure will be deployed. | | `AZURE_ENV_AI_SERVICE_LOCATION` | string | `` | Location of the Azure resources. Controls where the Azure AI Services will be deployed. | | `AZURE_ENV_MODEL_DEPLOYMENT_TYPE` | string | `GlobalStandard` | Change the Model Deployment Type (allowed values: Standard, GlobalStandard). | -| `AZURE_ENV_GPT_MODEL_NAME` | string | `gpt-5.1` | Set the Model Name (allowed values: gpt-5.1). | +| `AZURE_ENV_GPT_MODEL_NAME` | string | `gpt-5.1` | Set the Model Name (default: gpt-5.1). | | `AZURE_ENV_GPT_MODEL_VERSION` | string | `2025-11-13` | Set the Azure model version (allowed values: 2025-11-13) | | `AZURE_ENV_GPT_MODEL_CAPACITY` | integer | `150` | Set the Model Capacity (choose a number based on available GPT model capacity in your subscription). | | `AZURE_ENV_EXISTING_LOG_ANALYTICS_WORKSPACE_RID`| string | Guide to get your [Existing Workspace ID](re-use-log-analytics.md) | Set this if you want to reuse an existing Log Analytics Workspace instead of creating a new one. | diff --git a/docs/EXAMPLE-CustomizingAzdParameters.md b/docs/EXAMPLE-CustomizingAzdParameters.md index 2a12aa37..463aa030 100644 --- a/docs/EXAMPLE-CustomizingAzdParameters.md +++ b/docs/EXAMPLE-CustomizingAzdParameters.md @@ -17,10 +17,10 @@ Change the Model Deployment Type (allowed values: Standard, GlobalStandard) azd env set AZURE_ENV_MODEL_DEPLOYMENT_TYPE GlobalStandard ``` -Set the Model Name (allowed values: gpt-4o) +Set the Model Name (default: gpt-5.1) ```shell -azd env set AZURE_ENV_MODEL_NAME gpt-4o +azd env set AZURE_ENV_GPT_MODEL_NAME gpt-5.1 ``` Change the Model Capacity (choose a number based on available GPT model capacity in your subscription) diff --git a/docs/quota_check.md b/docs/quota_check.md index 37da7db5..26e910c2 100644 --- a/docs/quota_check.md +++ b/docs/quota_check.md @@ -1,7 +1,7 @@ ## Check Quota Availability Before Deployment Before deploying the accelerator, **ensure sufficient quota availability** for the required model. -> **For Global Standard | GPT-4o - the capacity to at least 200K tokens for optimal performance.** +> **For Global Standard | GPT-5.1 - ensure capacity is at least 200K tokens for optimal performance.** ### Login if you have not done so already ``` @@ -14,7 +14,7 @@ az login --use-device-code ### 📌 Default Models & Capacities: ``` -gpt-4o:150 +gpt-5.1:150 ``` ### 📌 Default Regions: ``` @@ -40,7 +40,7 @@ eastus, uksouth, eastus2, northcentralus, swedencentral, westus, westus2, southc ``` ✔️ Check specific model(s) in default regions: ``` - ./quota_check_params.sh --models gpt-4o:150 + ./quota_check_params.sh --models gpt-5.1:150 ``` ✔️ Check default models in specific region(s): ``` @@ -48,7 +48,7 @@ eastus, uksouth, eastus2, northcentralus, swedencentral, westus, westus2, southc ``` ✔️ Passing Both models and regions: ``` - ./quota_check_params.sh --models gpt-4o:150 --regions eastus,westus2 + ./quota_check_params.sh --models gpt-5.1:150 --regions eastus,westus2 ``` ✔️ All parameters combined: ``` diff --git a/infra/main.bicep b/infra/main.bicep index cbcea0b9..4242e6a0 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -334,6 +334,7 @@ var privateDnsZones = [ 'privatelink.oms.opinsights.azure.com' // Log Analytics OMS endpoints 'privatelink.ods.opinsights.azure.com' // Log Analytics ODS ingestion endpoints 'privatelink.agentsvc.azure-automation.net' // Agent service automation endpoints + 'privatelink.azurecr.io' // Azure Container Registry ] // DNS Zone Index Constants @@ -348,6 +349,7 @@ var dnsZoneIndex = { oms: 7 ods: 8 agentSvc: 9 + containerRegistry: 10 } // =================================================== @@ -902,15 +904,16 @@ module cosmosDb 'modules/cosmosDb.bicep' = { // and then updates the container apps to run the freshly built images. var placeholderContainerImage = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' -module containerRegistry 'br/public:avm/res/container-registry/registry:0.12.1' = { - name: take('avm.res.container-registry.registry.${solutionSuffix}', 64) +module containerRegistry 'modules/containerRegistry.bicep' = { + name: take('module.container-registry.${solutionSuffix}', 64) params: { - #disable-next-line BCP334 // solutionSuffix always yields a name of sufficient length at deployment time - name: take('cr${solutionSuffix}', 50) + acrName: take('cr${solutionSuffix}', 50) location: location - acrSku: enableRedundancy ? 'Premium' : 'Standard' - acrAdminUserEnabled: false - zoneRedundancy: enableRedundancy ? 'Enabled' : 'Disabled' + acrSku: (enableRedundancy || enablePrivateNetworking) ? 'Premium' : 'Standard' + publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' + enablePrivateNetworking: enablePrivateNetworking + backendSubnetResourceId: enablePrivateNetworking ? virtualNetwork!.outputs.pepsSubnetResourceId : '' + privateDnsZoneResourceId: enablePrivateNetworking ? avmPrivateDnsZones[dnsZoneIndex.containerRegistry]!.outputs.resourceId : '' roleAssignments: [ { principalId: appIdentity.outputs.principalId diff --git a/infra/main.json b/infra/main.json index e9af9da0..f3009de7 100644 --- a/infra/main.json +++ b/infra/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "4200460093633058559" + "version": "0.45.6.27763", + "templateHash": "16289228324296064708" }, "name": "Modernize Your Code Solution Accelerator", "description": "CSA CTO Gold Standard Solution Accelerator for Modernize Your Code. \r\n" @@ -239,7 +239,8 @@ "privatelink.monitor.azure.com", "privatelink.oms.opinsights.azure.com", "privatelink.ods.opinsights.azure.com", - "privatelink.agentsvc.azure-automation.net" + "privatelink.agentsvc.azure-automation.net", + "privatelink.azurecr.io" ], "dnsZoneIndex": { "cognitiveServices": 0, @@ -251,7 +252,8 @@ "monitor": 6, "oms": 7, "ods": 8, - "agentSvc": 9 + "agentSvc": 9, + "containerRegistry": 10 }, "bastionHostName": "[format('bas-{0}', variables('solutionSuffix'))]", "maintenanceConfigurationResourceName": "[format('mc-{0}', variables('solutionSuffix'))]", @@ -5087,8 +5089,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "12666388833640590711" + "version": "0.45.6.27763", + "templateHash": "278403134034191131" } }, "definitions": { @@ -13096,10 +13098,10 @@ "dependsOn": [ "applicationInsights", "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').agentSvc)]", + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').storageBlob)]", "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').monitor)]", "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').ods)]", "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').oms)]", - "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').storageBlob)]", "dataCollectionEndpoint", "logAnalyticsWorkspace", "virtualNetwork" @@ -26183,8 +26185,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "7796390788683636674" + "version": "0.45.6.27763", + "templateHash": "17695503720766712143" }, "name": "AI Services and Project Module", "description": "This module creates an AI Services resource and an AI Foundry project within it. It supports private networking, OpenAI deployments, and role assignments." @@ -27481,8 +27483,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "15798298565583456780" + "version": "0.45.6.27763", + "templateHash": "5349372727764638433" } }, "definitions": { @@ -29191,8 +29193,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "13008301984486295222" + "version": "0.45.6.27763", + "templateHash": "992342334639050267" } }, "definitions": { @@ -29406,8 +29408,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "15798298565583456780" + "version": "0.45.6.27763", + "templateHash": "5349372727764638433" } }, "definitions": { @@ -31116,8 +31118,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "13008301984486295222" + "version": "0.45.6.27763", + "templateHash": "992342334639050267" } }, "definitions": { @@ -32090,8 +32092,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "17237339939537137976" + "version": "0.45.6.27763", + "templateHash": "16329435452115246913" } }, "definitions": { @@ -40499,8 +40501,8 @@ }, "dependsOn": [ "appIdentity", - "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').storageFile)]", "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').storageBlob)]", + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').storageFile)]", "logAnalyticsWorkspace", "virtualNetwork" ] @@ -40544,8 +40546,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "8524856574515803596" + "version": "0.45.6.27763", + "templateHash": "16140682139384730655" } }, "definitions": { @@ -46805,24 +46807,26 @@ "containerRegistry": { "type": "Microsoft.Resources/deployments", "apiVersion": "2025-04-01", - "name": "[take(format('avm.res.container-registry.registry.{0}', variables('solutionSuffix')), 64)]", + "name": "[take(format('module.container-registry.{0}', variables('solutionSuffix')), 64)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": { + "acrName": { "value": "[take(format('cr{0}', variables('solutionSuffix')), 50)]" }, "location": { "value": "[parameters('location')]" }, - "acrSku": "[if(parameters('enableRedundancy'), createObject('value', 'Premium'), createObject('value', 'Standard'))]", - "acrAdminUserEnabled": { - "value": false + "acrSku": "[if(or(parameters('enableRedundancy'), parameters('enablePrivateNetworking')), createObject('value', 'Premium'), createObject('value', 'Standard'))]", + "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", + "enablePrivateNetworking": { + "value": "[parameters('enablePrivateNetworking')]" }, - "zoneRedundancy": "[if(parameters('enableRedundancy'), createObject('value', 'Enabled'), createObject('value', 'Disabled'))]", + "backendSubnetResourceId": "[if(parameters('enablePrivateNetworking'), createObject('value', reference('virtualNetwork').outputs.pepsSubnetResourceId.value), createObject('value', ''))]", + "privateDnsZoneResourceId": "[if(parameters('enablePrivateNetworking'), createObject('value', reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').containerRegistry)).outputs.resourceId.value), createObject('value', ''))]", "roleAssignments": { "value": [ { @@ -46841,3143 +46845,199 @@ }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", - "version": "0.42.1.51946", - "templateHash": "1509121545318808417" + "version": "0.45.6.27763", + "templateHash": "4265315615340631916" }, - "name": "Azure Container Registries (ACR)", - "description": "This module deploys an Azure Container Registry (ACR)." + "name": "Container Registry Module", + "description": "Deploys Azure Container Registry with support for both WAF (private networking) and non-WAF deployments" }, - "definitions": { - "privateEndpointOutputType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the private endpoint." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the private endpoint." - } - }, - "groupId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The group Id for the private endpoint Group." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "A list of private IP addresses of the private endpoint." - } - } - } - }, - "metadata": { - "description": "The custom DNS configurations of the private endpoint." - } - }, - "networkInterfaceResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." - } - } - }, - "metadata": { - "__bicep_export!": true - } + "parameters": { + "acrName": { + "type": "string" }, - "credentialSetType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the credential set." - } - }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityOnlySysAssignedType", - "nullable": true, - "metadata": { - "description": "Optional. The managed identity definition for this resource." - } - }, - "authCredentials": { - "type": "array", - "items": { - "$ref": "#/definitions/authCredentialsType" - }, - "metadata": { - "description": "Required. List of authentication credentials stored for an upstream. Usually consists of a primary and an optional secondary credential." - } - }, - "loginServer": { - "type": "string", - "metadata": { - "description": "Required. The credentials are stored for this upstream or login server." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a credential set." - } + "location": { + "type": "string" }, - "scopeMapsType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the scope map." - } - }, - "actions": { - "type": "array", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/scopeMaps@2025-03-01-preview#properties/properties/properties/actions" - }, - "description": "Required. The list of scoped permissions for registry artifacts." - } - }, - "description": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The user friendly description of the scope map." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a scope map." - } + "acrSku": { + "type": "string" + }, + "publicNetworkAccess": { + "type": "string" + }, + "enablePrivateNetworking": { + "type": "bool", + "defaultValue": false }, - "cacheRuleType": { + "backendSubnetResourceId": { + "type": "string", + "defaultValue": "" + }, + "privateDnsZoneResourceId": { + "type": "string", + "defaultValue": "" + }, + "tags": { "type": "object", + "defaultValue": {} + }, + "roleAssignments": { + "type": "array", + "defaultValue": [] + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true + } + }, + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2025-11-01", + "name": "[parameters('acrName')]", + "location": "[parameters('location')]", + "sku": { + "name": "[parameters('acrSku')]" + }, "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the cache rule. Will be derived from the source repository name if not defined." - } - }, - "sourceRepository": { - "type": "string", - "metadata": { - "description": "Required. Source repository pulled from upstream." - } - }, - "targetRepository": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Target repository specified in docker pull command. E.g.: docker pull myregistry.azurecr.io/{targetRepository}:{tag}." - } - }, - "credentialSetResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource ID of the credential store which is associated with the cache rule." - } - } + "adminUserEnabled": false, + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "networkRuleBypassAllowedForTasks": "[if(equals(parameters('acrSku'), 'Premium'), true(), null())]", + "networkRuleSet": "[if(and(equals(parameters('acrSku'), 'Premium'), equals(parameters('publicNetworkAccess'), 'Enabled')), createObject('defaultAction', 'Allow'), null())]" }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a cache rule." + "tags": "[parameters('tags')]", + "identity": { + "type": "SystemAssigned" } }, - "replicationType": { - "type": "object", + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName'))]", + "name": "[guid(resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName')), parameters('roleAssignments')[copyIndex()].principalId, parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the replication." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Location for all resources." - } - }, - "tags": { - "type": "object", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/replications@2025-03-01-preview#properties/tags" - }, - "description": "Optional. Tags of the resource." - }, - "nullable": true - }, - "regionEndpointEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Specifies whether the replication regional endpoint is enabled. Requests will not be routed to a replication whose regional endpoint is disabled, however its data will continue to be synced with other replications." - } - }, - "zoneRedundancy": { - "type": "string", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries@2025-03-01-preview#properties/properties/properties/zoneRedundancy" - }, - "description": "Optional. Whether or not zone redundancy is enabled for this container registry." - }, - "nullable": true - } + "principalId": "[parameters('roleAssignments')[copyIndex()].principalId]", + "principalType": "[parameters('roleAssignments')[copyIndex()].principalType]", + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a replication." - } + "dependsOn": [ + "[resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName'))]" + ] }, - "taskType": { - "type": "object", + { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "[take(format('46d3xbcp.ptn.acr.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4)), 64)]", "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the task." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Location for all resources." - } - }, - "tags": { - "type": "object", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/tasks@2025-03-01-preview#properties/tags" - }, - "description": "Optional. Tags of the resource." - }, - "nullable": true - }, - "platform": { - "type": "object", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/tasks@2025-03-01-preview#properties/properties/properties/platform" - }, - "description": "Optional. The platform properties for the task." - }, - "nullable": true - }, - "step": { - "type": "object", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/tasks@2025-03-01-preview#properties/properties/properties/step" - }, - "description": "Optional. The step properties for the task." - }, - "nullable": true - }, - "trigger": { - "type": "object", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/tasks@2025-03-01-preview#properties/properties/properties/trigger" - }, - "description": "Optional. The trigger properties for the task." - }, - "nullable": true - }, - "status": { - "type": "string", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/tasks@2025-03-01-preview#properties/properties/properties/status" - }, - "description": "Optional. The status of the task at the time the operation was called." - }, - "nullable": true - }, - "timeout": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The timeout in seconds for the task to run before it is automatically disabled." - } - }, - "agentConfiguration": { - "type": "object", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/tasks@2025-03-01-preview#properties/properties/properties/agentConfiguration" - }, - "description": "Optional. The agent configuration for the task." - }, - "nullable": true - }, - "agentPoolName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the agent pool to run the task on. If not specified, the task will run on Microsoft-hosted agents." - } - }, - "isSystemTask": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Whether this is a system task or not. System tasks have some additional restrictions and are used for internal purposes by Microsoft services, such as Azure DevOps pipelines integration." - } - }, - "logTemplate": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The log template for the task to use when creating logs in Log Analytics." + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } } } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a task." } }, - "tokenType": { - "type": "object", + { + "condition": "[and(parameters('enablePrivateNetworking'), not(empty(parameters('privateDnsZoneResourceId'))))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "[take(format('pep-{0}', parameters('acrName')), 64)]", "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the token." - } - }, - "scopeMapResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the scope map which defines the permissions for this token." - } + "expressionEvaluationOptions": { + "scope": "inner" }, - "status": { - "type": "string", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/tokens@2025-11-01#properties/properties/properties/status" - }, - "description": "Optional. The status of the token at the time the operation was called." + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('pep-{0}', parameters('acrName'))]" }, - "nullable": true - }, - "credentials": { - "type": "array", - "items": { - "$ref": "#/definitions/authCredentialsType" + "location": { + "value": "[parameters('location')]" }, - "nullable": true, - "metadata": { - "description": "Optional. The list of credentials associated with the token. Usually consists of a primary and an optional secondary credential." - } - } - } - }, - "webhookType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "minLength": 5, - "maxLength": 50, - "metadata": { - "description": "Optional. The name of the registry webhook." - } - }, - "serviceUri": { - "type": "string", - "metadata": { - "description": "Required. The service URI for the webhook to post notifications." - } - }, - "status": { - "type": "string", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/webhooks@2025-03-01-preview#properties/properties/properties/status" - }, - "description": "Optional. The status of the webhook at the time the operation was called." + "customNetworkInterfaceName": { + "value": "[format('nic-{0}', parameters('acrName'))]" }, - "nullable": true - }, - "action": { - "type": "array", - "items": { - "type": "string" + "privateLinkServiceConnections": { + "value": [ + { + "name": "[format('{0}-connection', parameters('acrName'))]", + "properties": { + "privateLinkServiceId": "[resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName'))]", + "groupIds": [ + "registry" + ] + } + } + ] }, - "nullable": true, - "metadata": { - "description": "Optional. The list of actions that trigger the webhook to post notifications." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Location for all resources." - } - }, - "tags": { - "type": "object", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/webhooks@2025-03-01-preview#properties/tags" - }, - "description": "Optional. Tags of the resource." + "privateDnsZoneGroup": { + "value": { + "privateDnsZoneGroupConfigs": [ + { + "name": "acr-dns-zone-group", + "privateDnsZoneResourceId": "[parameters('privateDnsZoneResourceId')]" + } + ] + } }, - "nullable": true - }, - "customHeaders": { - "type": "object", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/webhooks@2025-03-01-preview#properties/properties/properties/customHeaders" - }, - "description": "Optional. Custom headers that will be added to the webhook notifications." + "subnetResourceId": { + "value": "[parameters('backendSubnetResourceId')]" }, - "nullable": true - }, - "scope": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The scope of repositories where the event can be triggered. For example, 'foo:*' means events for all tags under repository 'foo'. 'foo:bar' means events for 'foo:bar' only. 'foo' is equivalent to 'foo:latest'. Empty means all events." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a webhook." - } - }, - "_1.privateEndpointCustomDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." + "tags": { + "value": "[parameters('tags')]" } }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" - } - } - }, - "_1.privateEndpointIpConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." - } + "_generator": { + "name": "bicep", + "version": "0.41.2.15936", + "templateHash": "18436885663402767850" }, - "memberName": { - "type": "string", + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + }, "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" - } - } - }, - "_1.privateEndpointPrivateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS Zone Group config." - } - }, - "privateDnsZoneResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of the private DNS zone." - } - } - } - }, - "metadata": { - "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" - } - } - }, - "authCredentialsType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the credential." - } - }, - "usernameSecretIdentifier": { - "type": "string", - "metadata": { - "description": "Required. KeyVault Secret URI for accessing the username." - } - }, - "passwordSecretIdentifier": { - "type": "string", - "metadata": { - "description": "Required. KeyVault Secret URI for accessing the password." - } - } - }, - "metadata": { - "description": "The type for auth credentials.", - "__bicep_imported_from!": { - "sourceTemplate": "credential-set/main.bicep" - } - } - }, - "customerManagedKeyWithAutoRotateType": { - "type": "object", - "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." - } - }, - "keyName": { - "type": "string", - "metadata": { - "description": "Required. The name of the customer managed key to use for encryption." - } - }, - "keyVersion": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using version as per 'autoRotationEnabled' setting." - } - }, - "autoRotationEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable auto-rotating to the latest key version. Default is `true`. If set to `false`, the latest key version at the time of the deployment is used." - } - }, - "userAssignedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type supports auto-rotation of the customer-managed key.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" - } - } - }, - "diagnosticSettingFullType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the diagnostic setting." - } - }, - "logCategoriesAndGroups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." - } - }, - "categoryGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." - } - }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." - } - }, - "metricCategories": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "metadata": { - "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." - } - }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." - } - }, - "logAnalyticsDestinationType": { - "type": "string", - "allowedValues": [ - "AzureDiagnostics", - "Dedicated" - ], - "nullable": true, - "metadata": { - "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." - } - }, - "workspaceResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." - } - }, - "storageAccountResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." - } - }, - "eventHubAuthorizationRuleResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." - } - }, - "eventHubName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." - } - }, - "marketplacePartnerResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" - } - } - }, - "lockType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of lock." - } - }, - "kind": { - "type": "string", - "allowedValues": [ - "CanNotDelete", - "None", - "ReadOnly" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of lock." - } - }, - "notes": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the notes of the lock." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a lock.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" - } - } - }, - "managedIdentityAllType": { - "type": "object", - "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." - } - }, - "userAssignedResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" - } - } - }, - "managedIdentityOnlySysAssignedType": { - "type": "object", - "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if only system-assigned identities are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" - } - } - }, - "privateEndpointSingleServiceType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private Endpoint." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The location to deploy the Private Endpoint to." - } - }, - "privateLinkServiceConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private link connection to create." - } - }, - "service": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "resourceGroupResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." - } - }, - "isManualConnection": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. If Manual Private Link Connection is required." - } - }, - "manualConnectionRequestMessage": { - "type": "string", - "nullable": true, - "maxLength": 140, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" - }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the Private Endpoint." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of lock." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.Network/privateEndpoints@2024-07-01#properties/tags" - }, - "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." - } - }, - "enableTelemetry": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" - } - } - }, - "roleAssignmentType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "principalId": { - "type": "string", - "metadata": { - "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." - } - }, - "principalType": { - "type": "string", - "allowedValues": [ - "Device", - "ForeignGroup", - "Group", - "ServicePrincipal", - "User" - ], - "nullable": true, - "metadata": { - "description": "Optional. The principal type of the assigned principal ID." - } - }, - "description": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The description of the role assignment." - } - }, - "condition": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." - } - }, - "conditionVersion": { - "type": "string", - "allowedValues": [ - "2.0" - ], - "nullable": true, - "metadata": { - "description": "Optional. Version of the condition." - } - }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a role assignment.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" - } - } - } - }, - "parameters": { - "name": { - "type": "string", - "minLength": 5, - "maxLength": 50, - "metadata": { - "description": "Required. Name of your Azure Container Registry." - } - }, - "acrAdminUserEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Enable admin user that have push / pull permission to the registry." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all resources." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "autoGeneratedDomainNameLabelScope": { - "type": "string", - "nullable": true, - "allowedValues": [ - "NoReuse", - "ResourceGroupReuse", - "SubscriptionReuse", - "TenantReuse", - "Unsecure" - ], - "metadata": { - "description": "Optional. The domain name label reuse scope." - } - }, - "roleAssignmentMode": { - "type": "string", - "nullable": true, - "allowedValues": [ - "AbacRepositoryPermissions", - "LegacyRegistryPermissions" - ], - "metadata": { - "description": "Optional. The registry permissions role assignment mode." - } - }, - "acrSku": { - "type": "string", - "defaultValue": "Premium", - "allowedValues": [ - "Basic", - "Premium", - "Standard" - ], - "metadata": { - "description": "Optional. Tier of your Azure container registry." - } - }, - "exportPolicyStatus": { - "type": "string", - "defaultValue": "disabled", - "allowedValues": [ - "disabled", - "enabled" - ], - "metadata": { - "description": "Optional. The value that indicates whether the export policy is enabled or not." - } - }, - "quarantinePolicyStatus": { - "type": "string", - "defaultValue": "disabled", - "allowedValues": [ - "disabled", - "enabled" - ], - "metadata": { - "description": "Optional. The value that indicates whether the quarantine policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'." - } - }, - "trustPolicyStatus": { - "type": "string", - "defaultValue": "disabled", - "allowedValues": [ - "disabled", - "enabled" - ], - "metadata": { - "description": "Optional. The value that indicates whether the trust policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'." - } - }, - "retentionPolicyStatus": { - "type": "string", - "defaultValue": "enabled", - "allowedValues": [ - "disabled", - "enabled" - ], - "metadata": { - "description": "Optional. The value that indicates whether the retention policy is enabled or not." - } - }, - "retentionPolicyDays": { - "type": "int", - "defaultValue": 15, - "metadata": { - "description": "Optional. The number of days to retain an untagged manifest after which it gets purged." - } - }, - "azureADAuthenticationAsArmPolicyStatus": { - "type": "string", - "defaultValue": "disabled", - "allowedValues": [ - "disabled", - "enabled" - ], - "metadata": { - "description": "Optional. The value that indicates whether the policy for using ARM audience token for a container registry is enabled or not. Default is disabled." - } - }, - "softDeletePolicyStatus": { - "type": "string", - "defaultValue": "disabled", - "allowedValues": [ - "disabled", - "enabled" - ], - "metadata": { - "description": "Optional. Soft Delete policy status. Default is disabled." - } - }, - "softDeletePolicyDays": { - "type": "int", - "defaultValue": 7, - "metadata": { - "description": "Optional. The number of days after which a soft-deleted item is permanently deleted." - } - }, - "dataEndpointEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Enable a single data endpoint per region for serving data. Not relevant in case of disabled public access. Note, requires the 'acrSku' to be 'Premium'." - } - }, - "publicNetworkAccess": { - "type": "string", - "nullable": true, - "allowedValues": [ - "Enabled", - "Disabled" - ], - "metadata": { - "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkRuleSetIpRules are not set. Note, requires the 'acrSku' to be 'Premium'." - } - }, - "networkRuleBypassOptions": { - "type": "string", - "defaultValue": "AzureServices", - "allowedValues": [ - "AzureServices", - "None" - ], - "metadata": { - "description": "Optional. Whether to allow trusted Azure services to access a network restricted registry." - } - }, - "networkRuleSetDefaultAction": { - "type": "string", - "defaultValue": "Deny", - "allowedValues": [ - "Allow", - "Deny" - ], - "metadata": { - "description": "Optional. The default action of allow or deny when no other rules match." - } - }, - "networkRuleSetIpRules": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The IP ACL rules. Note, requires the 'acrSku' to be 'Premium'. Set to an empty array to explicitly configure no allowed IPs." - } - }, - "privateEndpoints": { - "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointSingleServiceType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. Note, requires the 'acrSku' to be 'Premium'." - } - }, - "zoneRedundancy": { - "type": "string", - "defaultValue": "Enabled", - "allowedValues": [ - "Disabled", - "Enabled" - ], - "metadata": { - "description": "Optional. Whether or not zone redundancy is enabled for this container registry." - } - }, - "replications": { - "type": "array", - "items": { - "$ref": "#/definitions/replicationType" - }, - "nullable": true, - "metadata": { - "description": "Optional. All replications to create." - } - }, - "webhooks": { - "type": "array", - "items": { - "$ref": "#/definitions/webhookType" - }, - "nullable": true, - "metadata": { - "description": "Optional. All webhooks to create." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, - "metadata": { - "description": "Optional. The lock settings of the service." - } - }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityAllType", - "nullable": true, - "metadata": { - "description": "Optional. The managed identity definition for this resource." - } - }, - "tags": { - "type": "object", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries@2025-04-01#properties/tags" - }, - "description": "Optional. Tags of the resource." - }, - "nullable": true - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - }, - "diagnosticSettings": { - "type": "array", - "items": { - "$ref": "#/definitions/diagnosticSettingFullType" - }, - "nullable": true, - "metadata": { - "description": "Optional. The diagnostic settings of the service. If neither metrics nor logs are specified, all metrics & logs are configured by default. If either one is specified, the other is ignored." - } - }, - "anonymousPullEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Enables registry-wide pull from unauthenticated clients. It's in preview and available in the Standard and Premium service tiers." - } - }, - "customerManagedKey": { - "$ref": "#/definitions/customerManagedKeyWithAutoRotateType", - "nullable": true, - "metadata": { - "description": "Optional. The customer managed key definition." - } - }, - "cacheRules": { - "type": "array", - "items": { - "$ref": "#/definitions/cacheRuleType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of Cache Rules." - } - }, - "credentialSets": { - "type": "array", - "items": { - "$ref": "#/definitions/credentialSetType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of Credential Sets." - } - }, - "scopeMaps": { - "type": "array", - "items": { - "$ref": "#/definitions/scopeMapsType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Scope maps setting." - } - }, - "tokens": { - "type": "array", - "items": { - "$ref": "#/definitions/tokenType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Tokens to create for the container registry." - } - }, - "tasks": { - "type": "array", - "items": { - "$ref": "#/definitions/taskType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of ACR Tasks to create." - } - } - }, - "variables": { - "copy": [ - { - "name": "formattedRoleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", - "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" - } - ], - "enableReferencedModulesTelemetry": false, - "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", - "builtInRoleNames": { - "AcrDelete": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c2f4ef07-c644-48eb-af81-4b1b4947fb11')]", - "AcrImageSigner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6cef56e8-d556-48e5-a04f-b8e64114680f')]", - "AcrPull": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]", - "AcrPush": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8311e382-0749-4cb8-b61a-304f252e45ec')]", - "AcrQuarantineReader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cdda3590-29a3-44f6-95f2-9f980659eb04')]", - "AcrQuarantineWriter": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c8d4ff99-41c3-41a8-9f60-21dfdad59608')]", - "Container Registry Repository Catalog Lister": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfdb9389-c9a5-478a-bb2f-ba9ca092c3c7')]", - "Container Registry Repository Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2efddaa5-3f1f-4df3-97df-af3f13818f4c')]", - "Container Registry Repository Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b93aa761-3e63-49ed-ac28-beffa264f7ac')]", - "Container Registry Repository Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a1e307c-b015-4ebd-883e-5b7698a07328')]", - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - }, - "publicNetworkAccessMode": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(parameters('privateEndpoints'))), empty(parameters('networkRuleSetIpRules'))), 'Disabled', null()))]", - "shouldConfigureNetworkRuleSet": "[or(not(equals(parameters('networkRuleSetIpRules'), null())), and(equals(variables('publicNetworkAccessMode'), 'Enabled'), equals(parameters('networkRuleSetDefaultAction'), 'Deny')))]" - }, - "resources": { - "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2024-11-01", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" - }, - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('46d3xbcp.res.containerregistry-registry.{0}.{1}', replace('0.12.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", - "properties": { - "mode": "Incremental", - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "resources": [], - "outputs": { - "telemetry": { - "type": "String", - "value": "For more information, see https://aka.ms/avm/TelemetryInfo" - } - } - } - } - }, - "cMKKeyVault": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2024-11-01", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", - "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" - }, - "cMKUserAssignedIdentity": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", - "existing": true, - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2024-11-30", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", - "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" - }, - "registry": { - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2025-06-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "identity": "[variables('identity')]", - "tags": "[parameters('tags')]", - "sku": { - "name": "[parameters('acrSku')]" - }, - "properties": { - "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", - "adminUserEnabled": "[parameters('acrAdminUserEnabled')]", - "autoGeneratedDomainNameLabelScope": "[parameters('autoGeneratedDomainNameLabelScope')]", - "roleAssignmentMode": "[parameters('roleAssignmentMode')]", - "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('status', 'enabled', 'keyVaultProperties', createObject('identity', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyIdentifier', if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVersion'))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, tryGet(parameters('customerManagedKey'), 'keyVersion')), if(coalesce(tryGet(parameters('customerManagedKey'), 'autoRotationEnabled'), true()), reference('cMKKeyVault::cMKKey').keyUri, reference('cMKKeyVault::cMKKey').keyUriWithVersion)))), null())]", - "policies": { - "azureADAuthenticationAsArmPolicy": { - "status": "[parameters('azureADAuthenticationAsArmPolicyStatus')]" - }, - "exportPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('status', parameters('exportPolicyStatus')), null())]", - "quarantinePolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('status', parameters('quarantinePolicyStatus')), null())]", - "trustPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('type', 'Notary', 'status', parameters('trustPolicyStatus')), null())]", - "retentionPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('days', parameters('retentionPolicyDays'), 'status', parameters('retentionPolicyStatus')), null())]", - "softDeletePolicy": { - "retentionDays": "[parameters('softDeletePolicyDays')]", - "status": "[parameters('softDeletePolicyStatus')]" - } - }, - "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", - "publicNetworkAccess": "[variables('publicNetworkAccessMode')]", - "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", - "networkRuleSet": "[if(variables('shouldConfigureNetworkRuleSet'), createObject('defaultAction', parameters('networkRuleSetDefaultAction'), 'ipRules', coalesce(parameters('networkRuleSetIpRules'), createArray())), null())]", - "zoneRedundancy": "[if(equals(parameters('acrSku'), 'Premium'), parameters('zoneRedundancy'), null())]" - }, - "dependsOn": [ - "cMKKeyVault::cMKKey", - "cMKUserAssignedIdentity" - ] - }, - "registry_lock": { - "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", - "type": "Microsoft.Authorization/locks", - "apiVersion": "2020-05-01", - "scope": "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[coalesce(tryGet(parameters('lock'), 'notes'), if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.'))]" - }, - "dependsOn": [ - "registry" - ] - }, - "registry_diagnosticSettings": { - "copy": { - "name": "registry_diagnosticSettings", - "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" - }, - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", - "properties": { - "copy": [ - { - "name": "metrics", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), if(empty(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups')), createArray(createObject('category', 'AllMetrics')), createArray())))]", - "input": { - "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), if(empty(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups')), createArray(createObject('category', 'AllMetrics')), createArray()))[copyIndex('metrics')].category]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), if(empty(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups')), createArray(createObject('category', 'AllMetrics')), createArray()))[copyIndex('metrics')], 'enabled'), true())]", - "timeGrain": null - } - }, - { - "name": "logs", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), if(empty(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories')), createArray(createObject('categoryGroup', 'allLogs')), createArray())))]", - "input": { - "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), if(empty(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories')), createArray(createObject('categoryGroup', 'allLogs')), createArray()))[copyIndex('logs')], 'categoryGroup')]", - "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), if(empty(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories')), createArray(createObject('categoryGroup', 'allLogs')), createArray()))[copyIndex('logs')], 'category')]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), if(empty(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories')), createArray(createObject('categoryGroup', 'allLogs')), createArray()))[copyIndex('logs')], 'enabled'), true())]" - } - } - ], - "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", - "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", - "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", - "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" - }, - "dependsOn": [ - "registry" - ] - }, - "registry_roleAssignments": { - "copy": { - "name": "registry_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", - "properties": { - "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", - "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" - }, - "dependsOn": [ - "registry" - ] - }, - "registry_scopeMaps": { - "copy": { - "name": "registry_scopeMaps", - "count": "[length(coalesce(parameters('scopeMaps'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('{0}-Registry-Scope-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[tryGet(coalesce(parameters('scopeMaps'), createArray())[copyIndex()], 'name')]" - }, - "actions": { - "value": "[coalesce(parameters('scopeMaps'), createArray())[copyIndex()].actions]" - }, - "description": { - "value": "[tryGet(coalesce(parameters('scopeMaps'), createArray())[copyIndex()], 'description')]" - }, - "registryName": { - "value": "[parameters('name')]" - }, - "enableTelemetry": { - "value": "[variables('enableReferencedModulesTelemetry')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.42.1.51946", - "templateHash": "3787322352564227867" - }, - "name": "Container Registries scope maps", - "description": "This module deploys an Azure Container Registry (ACR) scope map." - }, - "parameters": { - "registryName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "defaultValue": "[format('{0}-scopemaps', parameters('registryName'))]", - "metadata": { - "description": "Optional. The name of the scope map." - } - }, - "actions": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The list of scoped permissions for registry artifacts." - } - }, - "description": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The user friendly description of the scope map." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - } - }, - "resources": { - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('46d3xbcp.res.containerregistry-registry-scopemap.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", - "properties": { - "mode": "Incremental", - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "resources": [], - "outputs": { - "telemetry": { - "type": "String", - "value": "For more information, see https://aka.ms/avm/TelemetryInfo" - } - } - } - } - }, - "registry": { - "existing": true, - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2025-11-01", - "name": "[parameters('registryName')]" - }, - "scopeMap": { - "type": "Microsoft.ContainerRegistry/registries/scopeMaps", - "apiVersion": "2025-11-01", - "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", - "properties": { - "actions": "[parameters('actions')]", - "description": "[parameters('description')]" - } - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the scope map." - }, - "value": "[parameters('name')]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the scope map was created in." - }, - "value": "[resourceGroup().name]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the scope map." - }, - "value": "[resourceId('Microsoft.ContainerRegistry/registries/scopeMaps', parameters('registryName'), parameters('name'))]" - } - } - } - }, - "dependsOn": [ - "registry" - ] - }, - "registry_replications": { - "copy": { - "name": "registry_replications", - "count": "[length(coalesce(parameters('replications'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('{0}-Registry-Replication-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[coalesce(parameters('replications'), createArray())[copyIndex()].name]" - }, - "registryName": { - "value": "[parameters('name')]" - }, - "location": { - "value": "[coalesce(parameters('replications'), createArray())[copyIndex()].location]" - }, - "regionEndpointEnabled": { - "value": "[tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'regionEndpointEnabled')]" - }, - "zoneRedundancy": { - "value": "[tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'zoneRedundancy')]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "enableTelemetry": { - "value": "[variables('enableReferencedModulesTelemetry')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.42.1.51946", - "templateHash": "6219097750044645017" - }, - "name": "Azure Container Registry (ACR) Replications", - "description": "This module deploys an Azure Container Registry (ACR) Replication." - }, - "parameters": { - "registryName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the replication." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all resources." - } - }, - "tags": { - "type": "object", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/replications@2025-11-01#properties/tags" - }, - "description": "Optional. Tags of the resource." - }, - "nullable": true - }, - "regionEndpointEnabled": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Specifies whether the replication regional endpoint is enabled. Requests will not be routed to a replication whose regional endpoint is disabled, however its data will continue to be synced with other replications." - } - }, - "zoneRedundancy": { - "type": "string", - "defaultValue": "Disabled", - "allowedValues": [ - "Disabled", - "Enabled" - ], - "metadata": { - "description": "Optional. Whether or not zone redundancy is enabled for this container registry." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - } - }, - "resources": { - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('46d3xbcp.res.containerregistry-registry-repl.{0}.{1}', replace('0.1.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", - "properties": { - "mode": "Incremental", - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "resources": [], - "outputs": { - "telemetry": { - "type": "String", - "value": "For more information, see https://aka.ms/avm/TelemetryInfo" - } - } - } - } - }, - "registry": { - "existing": true, - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2025-11-01", - "name": "[parameters('registryName')]" - }, - "replication": { - "type": "Microsoft.ContainerRegistry/registries/replications", - "apiVersion": "2025-11-01", - "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "regionEndpointEnabled": "[parameters('regionEndpointEnabled')]", - "zoneRedundancy": "[parameters('zoneRedundancy')]" - } - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the replication." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the replication." - }, - "value": "[resourceId('Microsoft.ContainerRegistry/registries/replications', parameters('registryName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the replication was created in." - }, - "value": "[resourceGroup().name]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('replication', '2025-11-01', 'full').location]" - } - } - } - }, - "dependsOn": [ - "registry" - ] - }, - "registry_credentialSets": { - "copy": { - "name": "registry_credentialSets", - "count": "[length(coalesce(parameters('credentialSets'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('{0}-Registry-CredentialSet-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].name]" - }, - "registryName": { - "value": "[parameters('name')]" - }, - "managedIdentities": { - "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].managedIdentities]" - }, - "authCredentials": { - "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].authCredentials]" - }, - "loginServer": { - "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].loginServer]" - }, - "enableTelemetry": { - "value": "[variables('enableReferencedModulesTelemetry')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.42.1.51946", - "templateHash": "13412699468141336519" - }, - "name": "Container Registries Credential Sets", - "description": "This module deploys an ACR Credential Set." - }, - "definitions": { - "authCredentialsType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the credential." - } - }, - "usernameSecretIdentifier": { - "type": "string", - "metadata": { - "description": "Required. KeyVault Secret URI for accessing the username." - } - }, - "passwordSecretIdentifier": { - "type": "string", - "metadata": { - "description": "Required. KeyVault Secret URI for accessing the password." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for auth credentials." - } - }, - "managedIdentityOnlySysAssignedType": { - "type": "object", - "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if only system-assigned identities are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" - } - } - } - }, - "parameters": { - "registryName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the credential set." - } - }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityOnlySysAssignedType", - "nullable": true, - "metadata": { - "description": "Optional. The managed identity definition for this resource." - } - }, - "authCredentials": { - "type": "array", - "items": { - "$ref": "#/definitions/authCredentialsType" - }, - "metadata": { - "description": "Required. List of authentication credentials stored for an upstream. Usually consists of a primary and an optional secondary credential." - } - }, - "loginServer": { - "type": "string", - "metadata": { - "description": "Required. The credentials are stored for this upstream or login server." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - } - }, - "resources": { - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('46d3xbcp.res.containerregistry-registry-credset.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", - "properties": { - "mode": "Incremental", - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "resources": [], - "outputs": { - "telemetry": { - "type": "String", - "value": "For more information, see https://aka.ms/avm/TelemetryInfo" - } - } - } - } - }, - "registry": { - "existing": true, - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2025-11-01", - "name": "[parameters('registryName')]" - }, - "credentialSet": { - "type": "Microsoft.ContainerRegistry/registries/credentialSets", - "apiVersion": "2025-11-01", - "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), 'SystemAssigned', null())), null())]", - "properties": { - "authCredentials": "[parameters('authCredentials')]", - "loginServer": "[parameters('loginServer')]" - } - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The Name of the Credential Set." - }, - "value": "[parameters('name')]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the Credential Set." - }, - "value": "[resourceGroup().name]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the Credential Set." - }, - "value": "[resourceId('Microsoft.ContainerRegistry/registries/credentialSets', parameters('registryName'), parameters('name'))]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The principal ID of the system assigned identity." - }, - "value": "[tryGet(tryGet(reference('credentialSet', '2025-11-01', 'full'), 'identity'), 'principalId')]" - } - } - } - }, - "dependsOn": [ - "registry" - ] - }, - "registry_cacheRules": { - "copy": { - "name": "registry_cacheRules", - "count": "[length(coalesce(parameters('cacheRules'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('{0}-Registry-Cache-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "registryName": { - "value": "[parameters('name')]" - }, - "sourceRepository": { - "value": "[coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository]" - }, - "name": { - "value": "[tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'name')]" - }, - "targetRepository": { - "value": "[coalesce(tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'targetRepository'), coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository)]" - }, - "credentialSetResourceId": { - "value": "[tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'credentialSetResourceId')]" - }, - "enableTelemetry": { - "value": "[variables('enableReferencedModulesTelemetry')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.42.1.51946", - "templateHash": "1319901650921923538" - }, - "name": "Container Registry Cache", - "description": "The cache for Azure Container Registry (Preview) feature allows users to cache container images in a private container registry. Cache for ACR, is a preview feature available in Basic, Standard, and Premium service tiers ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache))." - }, - "parameters": { - "registryName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "defaultValue": "[replace(replace(replace(parameters('sourceRepository'), '/', '-'), '.', '-'), '*', '')]", - "metadata": { - "description": "Optional. The name of the cache rule. Will be derived from the source repository name if not defined." - } - }, - "sourceRepository": { - "type": "string", - "metadata": { - "description": "Required. Source repository pulled from upstream." - } - }, - "targetRepository": { - "type": "string", - "defaultValue": "[parameters('sourceRepository')]", - "metadata": { - "description": "Optional. Target repository specified in docker pull command. E.g.: docker pull myregistry.azurecr.io/{targetRepository}:{tag}." - } - }, - "credentialSetResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource ID of the credential store which is associated with the cache rule. Required only when pulling from authenticated upstream registries (e.g., Docker Hub). Omit for anonymous public registries such as MCR (mcr.microsoft.com)." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - } - }, - "resources": { - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('46d3xbcp.res.containerregistry-registry-cacherule.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", - "properties": { - "mode": "Incremental", - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "resources": [], - "outputs": { - "telemetry": { - "type": "String", - "value": "For more information, see https://aka.ms/avm/TelemetryInfo" - } - } - } - } - }, - "registry": { - "existing": true, - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2025-11-01", - "name": "[parameters('registryName')]" - }, - "cacheRule": { - "type": "Microsoft.ContainerRegistry/registries/cacheRules", - "apiVersion": "2025-11-01", - "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", - "properties": { - "sourceRepository": "[parameters('sourceRepository')]", - "targetRepository": "[parameters('targetRepository')]", - "credentialSetResourceId": "[parameters('credentialSetResourceId')]" - } - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The Name of the Cache Rule." - }, - "value": "[parameters('name')]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the Cache Rule." - }, - "value": "[resourceGroup().name]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the Cache Rule." - }, - "value": "[resourceId('Microsoft.ContainerRegistry/registries/cacheRules', parameters('registryName'), parameters('name'))]" - } - } - } - }, - "dependsOn": [ - "registry", - "registry_credentialSets" - ] - }, - "registry_tokens": { - "copy": { - "name": "registry_tokens", - "count": "[length(coalesce(parameters('tokens'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('{0}-Registry-Token-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[coalesce(parameters('tokens'), createArray())[copyIndex()].name]" - }, - "registryName": { - "value": "[parameters('name')]" - }, - "scopeMapResourceId": { - "value": "[coalesce(parameters('tokens'), createArray())[copyIndex()].scopeMapResourceId]" - }, - "status": { - "value": "[tryGet(coalesce(parameters('tokens'), createArray())[copyIndex()], 'status')]" - }, - "credentials": { - "value": "[tryGet(coalesce(parameters('tokens'), createArray())[copyIndex()], 'credentials')]" - }, - "enableTelemetry": { - "value": "[variables('enableReferencedModulesTelemetry')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.42.1.51946", - "templateHash": "5970335582661416899" - }, - "name": "Container Registries Tokens", - "description": "Deploys an Azure Container Registry (ACR) Token." - }, - "parameters": { - "registryName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "minLength": 5, - "maxLength": 50, - "metadata": { - "description": "Required. The name of the token." - } - }, - "scopeMapResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the scope map to which the token will be associated with." - } - }, - "status": { - "type": "string", - "defaultValue": "enabled", - "allowedValues": [ - "disabled", - "enabled" - ], - "metadata": { - "description": "Optional. The status of the token. Default is enabled." - } - }, - "credentials": { - "type": "object", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/tokens@2025-11-01#properties/properties/properties/credentials" - }, - "description": "Optional. The credentials associated with the token for authentication." - }, - "nullable": true - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - } - }, - "resources": { - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('46d3xbcp.res.containerregistry-registry-token.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", - "properties": { - "mode": "Incremental", - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "resources": [], - "outputs": { - "telemetry": { - "type": "String", - "value": "For more information, see https://aka.ms/avm/TelemetryInfo" - } - } - } - } - }, - "registry": { - "existing": true, - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2025-11-01", - "name": "[parameters('registryName')]" - }, - "token": { - "type": "Microsoft.ContainerRegistry/registries/tokens", - "apiVersion": "2025-11-01", - "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", - "properties": { - "scopeMapId": "[parameters('scopeMapResourceId')]", - "status": "[parameters('status')]", - "credentials": "[if(not(empty(coalesce(parameters('credentials'), createArray()))), createObject('certificates', tryGet(parameters('credentials'), 'certificates'), 'passwords', tryGet(parameters('credentials'), 'passwords')), null())]" - } - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the token." - }, - "value": "[parameters('name')]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the token was created in." - }, - "value": "[resourceGroup().name]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the token." - }, - "value": "[resourceId('Microsoft.ContainerRegistry/registries/tokens', parameters('registryName'), parameters('name'))]" - } - } - } - }, - "dependsOn": [ - "registry", - "registry_scopeMaps" - ] - }, - "registry_tasks": { - "copy": { - "name": "registry_tasks", - "count": "[length(coalesce(parameters('tasks'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('{0}-Registry-Task-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "registryName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('tasks'), createArray())[copyIndex()].name]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('tasks'), createArray())[copyIndex()], 'location'), parameters('location'))]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('tasks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "platform": { - "value": "[tryGet(coalesce(parameters('tasks'), createArray())[copyIndex()], 'platform')]" - }, - "step": { - "value": "[tryGet(coalesce(parameters('tasks'), createArray())[copyIndex()], 'step')]" - }, - "trigger": { - "value": "[tryGet(coalesce(parameters('tasks'), createArray())[copyIndex()], 'trigger')]" - }, - "status": { - "value": "[tryGet(coalesce(parameters('tasks'), createArray())[copyIndex()], 'status')]" - }, - "timeout": { - "value": "[tryGet(coalesce(parameters('tasks'), createArray())[copyIndex()], 'timeout')]" - }, - "agentConfiguration": { - "value": "[tryGet(coalesce(parameters('tasks'), createArray())[copyIndex()], 'agentConfiguration')]" - }, - "agentPoolName": { - "value": "[tryGet(coalesce(parameters('tasks'), createArray())[copyIndex()], 'agentPoolName')]" - }, - "credentials": { - "value": "[tryGet(coalesce(parameters('tasks'), createArray())[copyIndex()], 'credentials')]" - }, - "isSystemTask": { - "value": "[tryGet(coalesce(parameters('tasks'), createArray())[copyIndex()], 'isSystemTask')]" - }, - "logTemplate": { - "value": "[tryGet(coalesce(parameters('tasks'), createArray())[copyIndex()], 'logTemplate')]" - }, - "managedIdentities": { - "value": "[tryGet(coalesce(parameters('tasks'), createArray())[copyIndex()], 'managedIdentities')]" - }, - "enableTelemetry": { - "value": "[variables('enableReferencedModulesTelemetry')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.42.1.51946", - "templateHash": "2468771835002458415" - }, - "name": "Container Registries Tasks", - "description": "Deploys an Azure Container Registry (ACR) Task that can be used to automate container image builds and other workflows ([ref](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-tasks-overview))." - }, - "definitions": { - "managedIdentityAllType": { - "type": "object", - "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." - } - }, - "userAssignedResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" - } - } - } - }, - "parameters": { - "registryName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "minLength": 5, - "maxLength": 50, - "metadata": { - "description": "Required. The name of the task." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all resources." - } - }, - "tags": { - "type": "object", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/tasks@2025-03-01-preview#properties/tags" - }, - "description": "Optional. Tags of the resource." - }, - "nullable": true - }, - "platform": { - "type": "object", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/tasks@2025-03-01-preview#properties/properties/properties/platform" - }, - "description": "Optional. The platform properties against which the task has to run." - }, - "nullable": true - }, - "step": { - "type": "object", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/tasks@2025-03-01-preview#properties/properties/properties/step" - }, - "description": "Optional. The task step properties. Exactly one of dockerBuildStep, encodedTaskStep, or fileTaskStep must be provided." - }, - "nullable": true - }, - "trigger": { - "type": "object", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/tasks@2025-03-01-preview#properties/properties/properties/trigger" - }, - "description": "Optional. The properties that describe all triggers for the task." - }, - "nullable": true - }, - "status": { - "type": "string", - "defaultValue": "Enabled", - "allowedValues": [ - "Disabled", - "Enabled" - ], - "metadata": { - "description": "Optional. The current status of task." - } - }, - "timeout": { - "type": "int", - "defaultValue": 3600, - "minValue": 300, - "maxValue": 28800, - "metadata": { - "description": "Optional. Run timeout in seconds." - } - }, - "agentConfiguration": { - "type": "object", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/tasks@2025-03-01-preview#properties/properties/properties/agentConfiguration" - }, - "description": "Optional. The machine configuration of the run agent." - }, - "nullable": true - }, - "agentPoolName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The dedicated agent pool for the task." - } - }, - "credentials": { - "type": "object", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/tasks@2025-03-01-preview#properties/properties/properties/credentials" - }, - "description": "Optional. The properties that describe the credentials that will be used when the task is invoked." - }, - "nullable": true - }, - "isSystemTask": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. The value of this property indicates whether the task resource is system task or not." - } - }, - "logTemplate": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The template that describes the repository and tag information for run log artifact." - } - }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityAllType", - "nullable": true, - "metadata": { - "description": "Optional. The managed identity definition for this resource." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - } - }, - "variables": { - "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]" - }, - "resources": { - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('46d3xbcp.res.containerregistry-registry-task.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", - "properties": { - "mode": "Incremental", - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "resources": [], - "outputs": { - "telemetry": { - "type": "String", - "value": "For more information, see https://aka.ms/avm/TelemetryInfo" - } - } - } - } - }, - "registry": { - "existing": true, - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2025-11-01", - "name": "[parameters('registryName')]" - }, - "task": { - "type": "Microsoft.ContainerRegistry/registries/tasks", - "apiVersion": "2025-03-01-preview", - "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", - "location": "[parameters('location')]", - "identity": "[variables('identity')]", - "tags": "[parameters('tags')]", - "properties": { - "agentConfiguration": "[parameters('agentConfiguration')]", - "agentPoolName": "[parameters('agentPoolName')]", - "credentials": "[parameters('credentials')]", - "isSystemTask": "[parameters('isSystemTask')]", - "logTemplate": "[parameters('logTemplate')]", - "platform": "[parameters('platform')]", - "status": "[parameters('status')]", - "step": "[parameters('step')]", - "timeout": "[parameters('timeout')]", - "trigger": "[parameters('trigger')]" - } - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the task." - }, - "value": "[parameters('name')]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the task was deployed into." - }, - "value": "[resourceGroup().name]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the task." - }, - "value": "[resourceId('Microsoft.ContainerRegistry/registries/tasks', parameters('registryName'), parameters('name'))]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('task', '2025-03-01-preview', 'full').location]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The principal ID of the system assigned identity." - }, - "value": "[tryGet(tryGet(reference('task', '2025-03-01-preview', 'full'), 'identity'), 'principalId')]" - } - } - } - }, - "dependsOn": [ - "registry" - ] - }, - "registry_webhooks": { - "copy": { - "name": "registry_webhooks", - "count": "[length(coalesce(parameters('webhooks'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('{0}-Registry-Webhook-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[coalesce(parameters('webhooks'), createArray())[copyIndex()].name]" - }, - "registryName": { - "value": "[parameters('name')]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'location'), parameters('location'))]" - }, - "action": { - "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'action')]" - }, - "customHeaders": { - "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'customHeaders')]" - }, - "scope": { - "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'scope')]" - }, - "status": { - "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'status')]" - }, - "serviceUri": { - "value": "[coalesce(parameters('webhooks'), createArray())[copyIndex()].serviceUri]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "enableTelemetry": { - "value": "[variables('enableReferencedModulesTelemetry')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.42.1.51946", - "templateHash": "3200175097987099858" - }, - "name": "Azure Container Registry (ACR) Webhooks", - "description": "This module deploys an Azure Container Registry (ACR) Webhook." - }, - "parameters": { - "registryName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "defaultValue": "[format('{0}webhook', parameters('registryName'))]", - "minLength": 5, - "maxLength": 50, - "metadata": { - "description": "Optional. The name of the registry webhook." - } - }, - "serviceUri": { - "type": "securestring", - "metadata": { - "description": "Required. The service URI for the webhook to post notifications." - } - }, - "status": { - "type": "string", - "defaultValue": "enabled", - "allowedValues": [ - "disabled", - "enabled" - ], - "metadata": { - "description": "Optional. The status of the webhook at the time the operation was called." - } - }, - "action": { - "type": "array", - "items": { - "type": "string" - }, - "defaultValue": [ - "chart_delete", - "chart_push", - "delete", - "push", - "quarantine" - ], - "metadata": { - "description": "Optional. The list of actions that trigger the webhook to post notifications." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all resources." - } - }, - "tags": { - "type": "object", - "metadata": { - "__bicep_resource_derived_type!": { - "source": "Microsoft.ContainerRegistry/registries/webhooks@2025-11-01#properties/tags" - }, - "description": "Optional. Tags of the resource." - }, - "nullable": true - }, - "customHeaders": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Custom headers that will be added to the webhook notifications." - } - }, - "scope": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The scope of repositories where the event can be triggered. For example, 'foo:*' means events for all tags under repository 'foo'. 'foo:bar' means events for 'foo:bar' only. 'foo' is equivalent to 'foo:latest'. Empty means all events." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - } - }, - "resources": { - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('46d3xbcp.res.containerregistry-registry-webhook.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", - "properties": { - "mode": "Incremental", - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "resources": [], - "outputs": { - "telemetry": { - "type": "String", - "value": "For more information, see https://aka.ms/avm/TelemetryInfo" - } - } - } - } - }, - "registry": { - "existing": true, - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2025-11-01", - "name": "[parameters('registryName')]" - }, - "webhook": { - "type": "Microsoft.ContainerRegistry/registries/webhooks", - "apiVersion": "2025-11-01", - "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "actions": "[parameters('action')]", - "customHeaders": "[parameters('customHeaders')]", - "scope": "[parameters('scope')]", - "serviceUri": "[parameters('serviceUri')]", - "status": "[parameters('status')]" - } - } - }, - "outputs": { - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the webhook." - }, - "value": "[resourceId('Microsoft.ContainerRegistry/registries/webhooks', parameters('registryName'), parameters('name'))]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the webhook." - }, - "value": "[parameters('name')]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the Azure container registry." - }, - "value": "[resourceGroup().name]" - }, - "actions": { - "type": "array", - "metadata": { - "description": "The actions of the webhook." - }, - "value": "[reference('webhook').actions]" - }, - "status": { - "type": "string", - "metadata": { - "description": "The status of the webhook." - }, - "value": "[reference('webhook').status]" - }, - "provistioningState": { - "type": "string", - "metadata": { - "description": "The provisioning state of the webhook." - }, - "value": "[reference('webhook').provisioningState]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('webhook', '2025-11-01', 'full').location]" - } - } - } - }, - "dependsOn": [ - "registry" - ] - }, - "registry_privateEndpoints": { - "copy": { - "name": "registry_privateEndpoints", - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('{0}-registry-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex()))]" - }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry')))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", - "subnetResourceId": { - "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" - }, - "enableTelemetry": { - "value": "[variables('enableReferencedModulesTelemetry')]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" - }, - "lock": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" - }, - "privateDnsZoneGroup": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "customDnsConfigs": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" - }, - "ipConfigurations": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" - }, - "applicationSecurityGroupResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" - }, - "customNetworkInterfaceName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.41.2.15936", - "templateHash": "18436885663402767850" - }, - "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint." - }, - "definitions": { - "privateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/privateDnsZoneGroupConfigType" - }, - "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type of a private dns zone group." + "__bicep_export!": true, + "description": "The type of a private dns zone group." } }, "lockType": { @@ -50554,99 +47614,34 @@ } }, "dependsOn": [ - "registry", - "registry_replications" + "[resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName'))]" ] } - }, + ], "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The Name of the Azure container registry." - }, - "value": "[parameters('name')]" - }, - "loginServer": { + "resourceId": { "type": "string", - "metadata": { - "description": "The reference to the Azure container registry." - }, - "value": "[reference('registry').loginServer]" + "value": "[resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName'))]" }, - "resourceGroupName": { + "name": { "type": "string", - "metadata": { - "description": "The name of the Azure container registry." - }, - "value": "[resourceGroup().name]" + "value": "[parameters('acrName')]" }, - "resourceId": { + "loginServer": { "type": "string", - "metadata": { - "description": "The resource ID of the Azure container registry." - }, - "value": "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName')), '2025-11-01').loginServer]" }, "systemAssignedMIPrincipalId": { "type": "string", - "nullable": true, - "metadata": { - "description": "The principal ID of the system assigned identity." - }, - "value": "[tryGet(tryGet(reference('registry', '2025-06-01-preview', 'full'), 'identity'), 'principalId')]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('registry', '2025-06-01-preview', 'full').location]" - }, - "credentialSetsSystemAssignedMIPrincipalIds": { - "type": "array", - "metadata": { - "description": "The Principal IDs of the ACR Credential Sets system-assigned identities." - }, - "copy": { - "count": "[length(range(0, length(coalesce(parameters('credentialSets'), createArray()))))]", - "input": "[tryGet(tryGet(reference(format('registry_credentialSets[{0}]', range(0, length(coalesce(parameters('credentialSets'), createArray())))[copyIndex()])).outputs, 'systemAssignedMIPrincipalId'), 'value')]" - } - }, - "credentialSetsResourceIds": { - "type": "array", - "metadata": { - "description": "The Resource IDs of the ACR Credential Sets." - }, - "copy": { - "count": "[length(range(0, length(coalesce(parameters('credentialSets'), createArray()))))]", - "input": "[reference(format('registry_credentialSets[{0}]', range(0, length(coalesce(parameters('credentialSets'), createArray())))[copyIndex()])).outputs.resourceId.value]" - } - }, - "privateEndpoints": { - "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointOutputType" - }, - "metadata": { - "description": "The private endpoints of the Azure container registry." - }, - "copy": { - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", - "input": { - "name": "[reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", - "resourceId": "[reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[tryGet(tryGet(reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", - "customDnsConfigs": "[reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", - "networkInterfaceResourceIds": "[reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" - } - } + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName')), '2025-11-01', 'full').identity.principalId]" } } } }, "dependsOn": [ - "appIdentity" + "appIdentity", + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').containerRegistry)]", + "virtualNetwork" ] }, "containerAppsEnvironment": { diff --git a/infra/modules/containerRegistry.bicep b/infra/modules/containerRegistry.bicep new file mode 100644 index 00000000..d95adede --- /dev/null +++ b/infra/modules/containerRegistry.bicep @@ -0,0 +1,104 @@ +metadata name = 'Container Registry Module' +metadata description = 'Deploys Azure Container Registry with support for both WAF (private networking) and non-WAF deployments' + +param acrName string +param location string +param acrSku string +param publicNetworkAccess string +param enablePrivateNetworking bool = false +param backendSubnetResourceId string = '' +param privateDnsZoneResourceId string = '' +param tags object = {} +param roleAssignments array = [] +param enableTelemetry bool = true + +// Azure Container Registry resource +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2025-11-01' = { + name: acrName + location: location + sku: { + name: acrSku + } + properties: { + adminUserEnabled: false + publicNetworkAccess: publicNetworkAccess + // Configure network rules for Premium SKU + networkRuleBypassAllowedForTasks: acrSku == 'Premium' ? true : null + // Set default network rule action based on deployment type + // For non-WAF (public access enabled): Allow all by default + // For WAF (private networking): Will be managed by post-deployment scripts during build + networkRuleSet: (acrSku == 'Premium' && publicNetworkAccess == 'Enabled') ? { + defaultAction: 'Allow' + } : null + } + tags: tags + identity: { + type: 'SystemAssigned' + } +} + +// Private endpoint for WAF deployments +module containerRegistryPrivateEndpoint 'br/public:avm/res/network/private-endpoint:0.12.0' = if (enablePrivateNetworking && !empty(privateDnsZoneResourceId)) { + name: take('pep-${acrName}', 64) + params: { + name: 'pep-${acrName}' + location: location + customNetworkInterfaceName: 'nic-${acrName}' + privateLinkServiceConnections: [ + { + name: '${acrName}-connection' + properties: { + privateLinkServiceId: containerRegistry.id + groupIds: ['registry'] + } + } + ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + name: 'acr-dns-zone-group' + privateDnsZoneResourceId: privateDnsZoneResourceId + } + ] + } + subnetResourceId: backendSubnetResourceId + tags: tags + } +} + +// Role assignments +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for assignment in roleAssignments: { + name: guid(containerRegistry.id, assignment.principalId, assignment.roleDefinitionIdOrName) + scope: containerRegistry + properties: { + principalId: assignment.principalId + principalType: assignment.principalType + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') // AcrPull + } +}] + +// AVM Telemetry +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2025-04-01' = if (enableTelemetry) { + name: take('46d3xbcp.ptn.acr.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}', 64) + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } +} + +// Outputs +output resourceId string = containerRegistry.id +output name string = containerRegistry.name +output loginServer string = containerRegistry.properties.loginServer +output systemAssignedMIPrincipalId string = containerRegistry.identity.principalId diff --git a/scripts/build_and_push_images.ps1 b/scripts/build_and_push_images.ps1 index c26b92f9..3248a2ad 100644 --- a/scripts/build_and_push_images.ps1 +++ b/scripts/build_and_push_images.ps1 @@ -1,91 +1,231 @@ #Requires -Version 7.0 +param( + [string]$ResourceGroupName +) + $ErrorActionPreference = 'Stop' # --------------------------------------------------------------------------- -# Post-provision hook (PowerShell) +# ACR Build and Push Script (PowerShell) +# Adapted for this accelerator from the CPSA acr_build_push.ps1 workflow. # -# Builds the backend and frontend container images remotely inside the -# deployment's Azure Container Registry (ACR) using 'az acr build' (no local -# Docker required) and then updates the container apps to run the freshly -# built images. +# Builds backend/frontend images remotely with az acr build, then updates the +# corresponding Container Apps. Supports: +# 1) azd environment output values (default path) +# 2) explicit resource group discovery (-ResourceGroupName) # -# The required values are provided by azd as environment variables (they are -# outputs of infra/main.bicep). When the script is run outside of an azd hook -# the values are read back with 'azd env get-value'. +# For WAF/private ACR deployments, the script temporarily relaxes ACR access +# restrictions and restores them in finally. # --------------------------------------------------------------------------- -Write-Host "==> Post-provision: building and pushing application images to ACR" +Write-Host '============================================================' +Write-Host 'ACR Build and Push - Starting...' +Write-Host '============================================================' -# Ensure the Azure CLI is available and authenticated (az acr build / containerapp -# update use the az CLI, which authenticates independently from azd). if (-not (Get-Command az -ErrorAction SilentlyContinue)) { - Write-Error "Azure CLI ('az') is not installed. Install it from https://aka.ms/azcli" - exit 1 + throw "Azure CLI ('az') is not installed. Install it from https://aka.ms/azcli" } + az account show --output none 2>$null if ($LASTEXITCODE -ne 0) { - Write-Error "You are not signed in to the Azure CLI. Run 'az login' and retry." - exit 1 + throw "You are not signed in to Azure CLI. Run 'az login' and retry." } function Get-EnvValue { - param([string]$Name) - $value = [Environment]::GetEnvironmentVariable($Name) - if ([string]::IsNullOrWhiteSpace($value)) { - $value = (azd env get-value $Name 2>$null) + param([string[]]$Names) + foreach ($name in $Names) { + $value = [Environment]::GetEnvironmentVariable($name) + if ([string]::IsNullOrWhiteSpace($value)) { + $value = azd env get-value $name 2>$null + } + if (-not [string]::IsNullOrWhiteSpace($value)) { + return $value + } + } + return $null +} + +function Get-ContainerAppsFromResourceGroup { + param([string]$ResourceGroup) + + $apps = az containerapp list --resource-group $ResourceGroup --output json | ConvertFrom-Json + $backend = $null + $frontend = $null + + foreach ($app in $apps) { + if ($null -eq $backend -and $app.name -match 'backend') { + $backend = $app.name + continue + } + if ($null -eq $frontend -and $app.name -match 'frontend') { + $frontend = $app.name + continue + } + } + + # Fallback when names do not contain backend/frontend. + if ($null -eq $backend -and $apps.Count -ge 1) { $backend = $apps[0].name } + if ($null -eq $frontend -and $apps.Count -ge 2) { $frontend = $apps[1].name } + + return @{ + BackendApp = $backend + FrontendApp = $frontend } - return $value } -$AcrName = Get-EnvValue 'AZURE_CONTAINER_REGISTRY_NAME' -$AcrEndpoint = Get-EnvValue 'AZURE_CONTAINER_REGISTRY_ENDPOINT' -$ResourceGroup = Get-EnvValue 'AZURE_RESOURCE_GROUP' -$BackendApp = Get-EnvValue 'BACKEND_CONTAINER_APP_NAME' -$FrontendApp = Get-EnvValue 'FRONTEND_CONTAINER_APP_NAME' -$ImageTag = Get-EnvValue 'AZURE_ENV_IMAGE_TAG' +$AcrName = $null +$AcrEndpoint = $null +$ResourceGroup = $null +$BackendApp = $null +$FrontendApp = $null + +if ([string]::IsNullOrWhiteSpace($ResourceGroupName)) { + Write-Host 'Using azd environment values...' + $AcrName = Get-EnvValue @('AZURE_CONTAINER_REGISTRY_NAME', 'CONTAINER_REGISTRY_NAME') + $AcrEndpoint = Get-EnvValue @('AZURE_CONTAINER_REGISTRY_ENDPOINT', 'CONTAINER_REGISTRY_LOGIN_SERVER') + $ResourceGroup = Get-EnvValue @('AZURE_RESOURCE_GROUP') + $BackendApp = Get-EnvValue @('BACKEND_CONTAINER_APP_NAME') + $FrontendApp = Get-EnvValue @('FRONTEND_CONTAINER_APP_NAME') +} +else { + Write-Host "Using existing deployment from resource group: $ResourceGroupName" + $ResourceGroup = $ResourceGroupName + + $acr = az acr list --resource-group $ResourceGroup --query '[0]' --output json | ConvertFrom-Json + if ($null -eq $acr) { + throw "No Azure Container Registry found in resource group '$ResourceGroup'." + } + + $AcrName = $acr.name + $AcrEndpoint = $acr.loginServer + + $apps = Get-ContainerAppsFromResourceGroup -ResourceGroup $ResourceGroup + $BackendApp = $apps.BackendApp + $FrontendApp = $apps.FrontendApp +} + +$ImageTag = Get-EnvValue @('AZURE_ENV_IMAGE_TAG') if ([string]::IsNullOrWhiteSpace($ImageTag)) { $ImageTag = 'latest' } -# Derive the login server from the registry name if the output was not available. if ([string]::IsNullOrWhiteSpace($AcrEndpoint) -and -not [string]::IsNullOrWhiteSpace($AcrName)) { $AcrEndpoint = "$AcrName.azurecr.io" } $missing = @() -if ([string]::IsNullOrWhiteSpace($AcrName)) { $missing += 'AZURE_CONTAINER_REGISTRY_NAME' } +if ([string]::IsNullOrWhiteSpace($AcrName)) { $missing += 'AZURE_CONTAINER_REGISTRY_NAME' } +if ([string]::IsNullOrWhiteSpace($AcrEndpoint)) { $missing += 'AZURE_CONTAINER_REGISTRY_ENDPOINT' } if ([string]::IsNullOrWhiteSpace($ResourceGroup)) { $missing += 'AZURE_RESOURCE_GROUP' } -if ([string]::IsNullOrWhiteSpace($BackendApp)) { $missing += 'BACKEND_CONTAINER_APP_NAME' } -if ([string]::IsNullOrWhiteSpace($FrontendApp)) { $missing += 'FRONTEND_CONTAINER_APP_NAME' } +if ([string]::IsNullOrWhiteSpace($BackendApp)) { $missing += 'BACKEND_CONTAINER_APP_NAME' } +if ([string]::IsNullOrWhiteSpace($FrontendApp)) { $missing += 'FRONTEND_CONTAINER_APP_NAME' } if ($missing.Count -gt 0) { - Write-Error "Missing required environment values: $($missing -join ', ')" - exit 1 + throw "Missing required values: $($missing -join ', ')" } -# Resolve the repository root (this script lives in /scripts). -$RepoRoot = Split-Path -Parent $PSScriptRoot -$BackendImage = "cmsabackend:$ImageTag" +$ScriptDir = $PSScriptRoot +$RepoRoot = Resolve-Path (Join-Path $ScriptDir '..') +$BackendImage = "cmsabackend:$ImageTag" $FrontendImage = "cmsafrontend:$ImageTag" -# Ensure the Microsoft.App resource provider is registered before updating the -# container apps. ARM registration can still be propagating after provisioning, -# which makes 'az containerapp update' fail with a "not registered" error. -Write-Host "==> Ensuring the Microsoft.App resource provider is registered" -az provider register --namespace Microsoft.App --wait -if ($LASTEXITCODE -ne 0) { throw "Failed to register the Microsoft.App resource provider." } +$acrInfo = az acr show --name $AcrName --resource-group $ResourceGroup --output json | ConvertFrom-Json +$acrPublicAccess = $acrInfo.properties.publicNetworkAccess +$acrSku = $acrInfo.sku.name + +$DeploymentType = az group show --name $ResourceGroup --query 'tags.Type' -o tsv 2>$null +$IsWafDeployment = ($DeploymentType -eq 'WAF') -or ($acrPublicAccess -eq 'Disabled') -Write-Host "==> Building backend image ($BackendImage) in ACR '$AcrName'" -az acr build --registry $AcrName --image $BackendImage --file "$RepoRoot/src/backend/Dockerfile" "$RepoRoot/src/backend" -if ($LASTEXITCODE -ne 0) { throw "Backend image build failed." } +Write-Host '' +Write-Host " ACR Name: $AcrName" +Write-Host " ACR Login Server: $AcrEndpoint" +Write-Host " Resource Group: $ResourceGroup" +Write-Host " Backend App: $BackendApp" +Write-Host " Frontend App: $FrontendApp" +Write-Host " Image Tag: $ImageTag" +Write-Host " ACR SKU: $acrSku" +Write-Host " ACR Public Access: $acrPublicAccess" +Write-Host '' -Write-Host "==> Building frontend image ($FrontendImage) in ACR '$AcrName'" -az acr build --registry $AcrName --image $FrontendImage --file "$RepoRoot/src/frontend/Dockerfile" "$RepoRoot/src/frontend" -if ($LASTEXITCODE -ne 0) { throw "Frontend image build failed." } +try { + if ($IsWafDeployment) { + Write-Host 'WAF/private deployment detected. Temporarily relaxing ACR restrictions...' -Write-Host "==> Updating backend container app '$BackendApp'" -az containerapp update --name $BackendApp --resource-group $ResourceGroup --image "$AcrEndpoint/$BackendImage" --output none -if ($LASTEXITCODE -ne 0) { throw "Backend container app update failed." } + az acr update --name $AcrName --resource-group $ResourceGroup --allow-exports true --output none + if ($LASTEXITCODE -ne 0) { throw 'Failed to enable ACR exports.' } -Write-Host "==> Updating frontend container app '$FrontendApp'" -az containerapp update --name $FrontendApp --resource-group $ResourceGroup --image "$AcrEndpoint/$FrontendImage" --output none -if ($LASTEXITCODE -ne 0) { throw "Frontend container app update failed." } + az acr update --name $AcrName --resource-group $ResourceGroup --public-network-enabled true --output none + if ($LASTEXITCODE -ne 0) { throw 'Failed to enable ACR public network access.' } -Write-Host "==> Done. Container apps are now running the freshly built images." + if ($acrSku -eq 'Premium') { + az acr update --name $AcrName --resource-group $ResourceGroup --default-action Allow --output none + if ($LASTEXITCODE -ne 0) { throw 'Failed to set ACR default action to Allow.' } + } + + $maxRetries = 30 + for ($i = 0; $i -lt $maxRetries; $i++) { + $status = az acr show --name $AcrName --resource-group $ResourceGroup --query 'properties.publicNetworkAccess' -o tsv 2>$null + if ($status -eq 'Enabled') { break } + Start-Sleep -Seconds 1 + } + } + + Write-Host '==> Ensuring the Microsoft.App resource provider is registered' + az provider register --namespace Microsoft.App --wait + if ($LASTEXITCODE -ne 0) { throw 'Failed to register Microsoft.App resource provider.' } + + Write-Host "==> Verifying ACR '$AcrName' connectivity" + $acrReachable = $false + for ($i = 0; $i -lt 5; $i++) { + az acr repository list --name $AcrName --output none 2>$null + if ($LASTEXITCODE -eq 0) { + $acrReachable = $true + break + } + Start-Sleep -Seconds 3 + } + if (-not $acrReachable) { + throw "ACR '$AcrName' is not reachable. Check network rules and access permissions." + } + + Write-Host '============================================================' + Write-Host 'Step 1: Building and pushing images to ACR...' + Write-Host '============================================================' + + Write-Host " Building $BackendImage" + az acr build --registry $AcrName --image $BackendImage --file "$RepoRoot/src/backend/Dockerfile" "$RepoRoot/src/backend" + if ($LASTEXITCODE -ne 0) { throw 'Failed to build backend image.' } + + Write-Host " Building $FrontendImage" + az acr build --registry $AcrName --image $FrontendImage --file "$RepoRoot/src/frontend/Dockerfile" "$RepoRoot/src/frontend" + if ($LASTEXITCODE -ne 0) { throw 'Failed to build frontend image.' } + + Write-Host '' + Write-Host '============================================================' + Write-Host 'Step 2: Updating Container Apps with new images...' + Write-Host '============================================================' + + Write-Host " Updating $BackendApp" + az containerapp update --name $BackendApp --resource-group $ResourceGroup --image "$AcrEndpoint/$BackendImage" --output none + if ($LASTEXITCODE -ne 0) { throw "Failed to update $BackendApp." } + + Write-Host " Updating $FrontendApp" + az containerapp update --name $FrontendApp --resource-group $ResourceGroup --image "$AcrEndpoint/$FrontendImage" --output none + if ($LASTEXITCODE -ne 0) { throw "Failed to update $FrontendApp." } + + Write-Host '' + Write-Host '============================================================' + Write-Host 'ACR Build and Push - Completed Successfully!' + Write-Host '============================================================' +} +finally { + if ($IsWafDeployment) { + Write-Host '' + Write-Host 'Restoring WAF/private ACR configuration...' + + if ($acrSku -eq 'Premium') { + az acr update --name $AcrName --resource-group $ResourceGroup --default-action Deny --output none 2>$null + } + az acr update --name $AcrName --resource-group $ResourceGroup --public-network-enabled false --output none 2>$null + az acr update --name $AcrName --resource-group $ResourceGroup --allow-exports false --output none 2>$null + + Write-Host 'ACR configuration restored.' + } +} diff --git a/scripts/build_and_push_images.sh b/scripts/build_and_push_images.sh index d1e80f1c..ce32a6e6 100644 --- a/scripts/build_and_push_images.sh +++ b/scripts/build_and_push_images.sh @@ -2,103 +2,230 @@ set -euo pipefail # --------------------------------------------------------------------------- -# Post-provision hook +# ACR Build and Push Script (Bash) # -# Builds the backend and frontend container images remotely inside the -# deployment's Azure Container Registry (ACR) using 'az acr build' (no local -# Docker required) and then updates the container apps to run the freshly -# built images. +# Builds backend/frontend images remotely with az acr build, then updates the +# corresponding Container Apps. Supports: +# 1) azd environment output values (default path) +# 2) explicit resource group discovery (first argument) # -# The required values are provided by azd as environment variables (they are -# outputs of infra/main.bicep). When the script is run outside of an azd hook -# the values are read back with 'azd env get-value'. +# For WAF/private ACR deployments, the script temporarily relaxes ACR access +# restrictions and restores them in the EXIT trap. # --------------------------------------------------------------------------- -echo "==> Post-provision: building and pushing application images to ACR" +RESOURCE_GROUP_INPUT="${1:-}" + +echo "============================================================" +echo "ACR Build and Push - Starting..." +echo "============================================================" -# Ensure the Azure CLI is available and authenticated (az acr build / containerapp -# update use the az CLI, which authenticates independently from azd). if ! command -v az >/dev/null 2>&1; then echo "ERROR: Azure CLI ('az') is not installed. Install it from https://aka.ms/azcli" >&2 exit 1 fi -if ! az account show >/dev/null 2>&1; then - echo "ERROR: You are not signed in to the Azure CLI. Run 'az login' and retry." >&2 + +if ! az account show --output none >/dev/null 2>&1; then + echo "ERROR: You are not signed in to Azure CLI. Run 'az login' and retry." >&2 exit 1 fi -# Reads an environment variable, falling back to the azd environment. -load_env_var() { - local name="$1" - local value="${!name:-}" - if [ -z "$value" ]; then - value=$(azd env get-value "$name" 2>/dev/null || true) +get_env_value() { + for name in "$@"; do + local value="${!name:-}" + if [ -z "$value" ]; then + value=$(azd env get-value "$name" 2>/dev/null || true) + fi + if [ -n "$value" ]; then + printf '%s' "$value" + return 0 + fi + done + printf '' +} + +get_apps_from_rg() { + local rg="$1" + local backend + local frontend + + backend=$(az containerapp list --resource-group "$rg" --query "[?contains(name, 'backend')].name | [0]" -o tsv) + frontend=$(az containerapp list --resource-group "$rg" --query "[?contains(name, 'frontend')].name | [0]" -o tsv) + + if [ -z "$backend" ]; then + backend=$(az containerapp list --resource-group "$rg" --query "[0].name" -o tsv) + fi + + if [ -z "$frontend" ]; then + frontend=$(az containerapp list --resource-group "$rg" --query "[1].name" -o tsv) fi - printf '%s' "$value" + + printf '%s|%s' "$backend" "$frontend" } -ACR_NAME=$(load_env_var AZURE_CONTAINER_REGISTRY_NAME) -ACR_ENDPOINT=$(load_env_var AZURE_CONTAINER_REGISTRY_ENDPOINT) -RESOURCE_GROUP=$(load_env_var AZURE_RESOURCE_GROUP) -BACKEND_APP=$(load_env_var BACKEND_CONTAINER_APP_NAME) -FRONTEND_APP=$(load_env_var FRONTEND_CONTAINER_APP_NAME) -IMAGE_TAG=$(load_env_var AZURE_ENV_IMAGE_TAG) +ACR_NAME="" +ACR_ENDPOINT="" +RESOURCE_GROUP="" +BACKEND_APP="" +FRONTEND_APP="" + +if [ -z "$RESOURCE_GROUP_INPUT" ]; then + echo "Using azd environment values..." + ACR_NAME=$(get_env_value AZURE_CONTAINER_REGISTRY_NAME CONTAINER_REGISTRY_NAME) + ACR_ENDPOINT=$(get_env_value AZURE_CONTAINER_REGISTRY_ENDPOINT CONTAINER_REGISTRY_LOGIN_SERVER) + RESOURCE_GROUP=$(get_env_value AZURE_RESOURCE_GROUP) + BACKEND_APP=$(get_env_value BACKEND_CONTAINER_APP_NAME) + FRONTEND_APP=$(get_env_value FRONTEND_CONTAINER_APP_NAME) +else + echo "Using existing deployment from resource group: $RESOURCE_GROUP_INPUT" + RESOURCE_GROUP="$RESOURCE_GROUP_INPUT" + + ACR_NAME=$(az acr list --resource-group "$RESOURCE_GROUP" --query "[0].name" -o tsv) + ACR_ENDPOINT=$(az acr list --resource-group "$RESOURCE_GROUP" --query "[0].loginServer" -o tsv) + + if [ -z "$ACR_NAME" ]; then + echo "ERROR: No Azure Container Registry found in resource group '$RESOURCE_GROUP'." >&2 + exit 1 + fi + + apps=$(get_apps_from_rg "$RESOURCE_GROUP") + BACKEND_APP="${apps%%|*}" + FRONTEND_APP="${apps##*|}" +fi + +IMAGE_TAG=$(get_env_value AZURE_ENV_IMAGE_TAG) IMAGE_TAG=${IMAGE_TAG:-latest} -# Derive the login server from the registry name if the output was not available. if [ -z "$ACR_ENDPOINT" ] && [ -n "$ACR_NAME" ]; then ACR_ENDPOINT="${ACR_NAME}.azurecr.io" fi missing="" [ -z "$ACR_NAME" ] && missing="$missing AZURE_CONTAINER_REGISTRY_NAME" +[ -z "$ACR_ENDPOINT" ] && missing="$missing AZURE_CONTAINER_REGISTRY_ENDPOINT" [ -z "$RESOURCE_GROUP" ] && missing="$missing AZURE_RESOURCE_GROUP" [ -z "$BACKEND_APP" ] && missing="$missing BACKEND_CONTAINER_APP_NAME" [ -z "$FRONTEND_APP" ] && missing="$missing FRONTEND_CONTAINER_APP_NAME" if [ -n "$missing" ]; then - echo "ERROR: Missing required environment values:$missing" >&2 + echo "ERROR: Missing required values:$missing" >&2 exit 1 fi -# Resolve the repository root (this script lives in /scripts). SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" - BACKEND_IMAGE="cmsabackend:${IMAGE_TAG}" FRONTEND_IMAGE="cmsafrontend:${IMAGE_TAG}" -# Ensure the Microsoft.App resource provider is registered before updating the -# container apps. ARM registration can still be propagating after provisioning, -# which makes 'az containerapp update' fail with a "not registered" error. +ACR_PUBLIC_ACCESS=$(az acr show --name "$ACR_NAME" --resource-group "$RESOURCE_GROUP" --query "properties.publicNetworkAccess" -o tsv) +ACR_SKU=$(az acr show --name "$ACR_NAME" --resource-group "$RESOURCE_GROUP" --query "sku.name" -o tsv) +DEPLOYMENT_TYPE=$(az group show --name "$RESOURCE_GROUP" --query "tags.Type" -o tsv 2>/dev/null || true) + +IS_WAF_DEPLOYMENT=false +if [ "$DEPLOYMENT_TYPE" = "WAF" ] || [ "$ACR_PUBLIC_ACCESS" = "Disabled" ]; then + IS_WAF_DEPLOYMENT=true +fi + +echo "" +echo " ACR Name: $ACR_NAME" +echo " ACR Login Server: $ACR_ENDPOINT" +echo " Resource Group: $RESOURCE_GROUP" +echo " Backend App: $BACKEND_APP" +echo " Frontend App: $FRONTEND_APP" +echo " Image Tag: $IMAGE_TAG" +echo " ACR SKU: $ACR_SKU" +echo " ACR Public Access: $ACR_PUBLIC_ACCESS" +echo "" + +cleanup() { + if [ "$IS_WAF_DEPLOYMENT" = true ]; then + echo "" + echo "Restoring WAF/private ACR configuration..." + + if [ "$ACR_SKU" = "Premium" ]; then + az acr update --name "$ACR_NAME" --resource-group "$RESOURCE_GROUP" --default-action Deny --output none >/dev/null 2>&1 || true + fi + + az acr update --name "$ACR_NAME" --resource-group "$RESOURCE_GROUP" --public-network-enabled false --output none >/dev/null 2>&1 || true + az acr update --name "$ACR_NAME" --resource-group "$RESOURCE_GROUP" --allow-exports false --output none >/dev/null 2>&1 || true + + echo "ACR configuration restored." + fi +} +trap cleanup EXIT + +if [ "$IS_WAF_DEPLOYMENT" = true ]; then + echo "WAF/private deployment detected. Temporarily relaxing ACR restrictions..." + + az acr update --name "$ACR_NAME" --resource-group "$RESOURCE_GROUP" --allow-exports true --output none + az acr update --name "$ACR_NAME" --resource-group "$RESOURCE_GROUP" --public-network-enabled true --output none + if [ "$ACR_SKU" = "Premium" ]; then + az acr update --name "$ACR_NAME" --resource-group "$RESOURCE_GROUP" --default-action Allow --output none + fi + + max_retries=30 + for ((i=0; i/dev/null || echo "") + if [ "$status" = "Enabled" ]; then + break + fi + sleep 1 + done +fi + echo "==> Ensuring the Microsoft.App resource provider is registered" az provider register --namespace Microsoft.App --wait -echo "==> Building backend image ($BACKEND_IMAGE) in ACR '$ACR_NAME'" +echo "==> Verifying ACR '$ACR_NAME' connectivity" +acr_reachable=false +for ((i=0; i<5; i++)); do + if az acr repository list --name "$ACR_NAME" --output none >/dev/null 2>&1; then + acr_reachable=true + break + fi + sleep 3 +done +if [ "$acr_reachable" != true ]; then + echo "ERROR: ACR '$ACR_NAME' is not reachable. Check network rules and access permissions." >&2 + exit 1 +fi + +echo "============================================================" +echo "Step 1: Building and pushing images to ACR..." +echo "============================================================" + +echo " Building $BACKEND_IMAGE" az acr build \ --registry "$ACR_NAME" \ --image "$BACKEND_IMAGE" \ --file "$REPO_ROOT/src/backend/Dockerfile" \ "$REPO_ROOT/src/backend" -echo "==> Building frontend image ($FRONTEND_IMAGE) in ACR '$ACR_NAME'" +echo " Building $FRONTEND_IMAGE" az acr build \ --registry "$ACR_NAME" \ --image "$FRONTEND_IMAGE" \ --file "$REPO_ROOT/src/frontend/Dockerfile" \ "$REPO_ROOT/src/frontend" -echo "==> Updating backend container app '$BACKEND_APP'" +echo "" +echo "============================================================" +echo "Step 2: Updating Container Apps with new images..." +echo "============================================================" + +echo " Updating $BACKEND_APP" az containerapp update \ --name "$BACKEND_APP" \ --resource-group "$RESOURCE_GROUP" \ --image "${ACR_ENDPOINT}/${BACKEND_IMAGE}" \ --output none -echo "==> Updating frontend container app '$FRONTEND_APP'" +echo " Updating $FRONTEND_APP" az containerapp update \ --name "$FRONTEND_APP" \ --resource-group "$RESOURCE_GROUP" \ --image "${ACR_ENDPOINT}/${FRONTEND_IMAGE}" \ --output none -echo "==> Done. Container apps are now running the freshly built images." +echo "" +echo "============================================================" +echo "ACR Build and Push - Completed Successfully!" +echo "============================================================" diff --git a/scripts/checkquota.sh b/scripts/checkquota.sh index 285d47e3..7245ba52 100644 --- a/scripts/checkquota.sh +++ b/scripts/checkquota.sh @@ -22,7 +22,7 @@ echo "✅ Azure subscription set successfully." # Define models and their minimum required capacities declare -A MIN_CAPACITY=( - ["OpenAI.GlobalStandard.gpt-4o"]=$GPT_MIN_CAPACITY + ["OpenAI.GlobalStandard.gpt-5.1"]=$GPT_MIN_CAPACITY ) VALID_REGION="" diff --git a/scripts/quota_check_params.sh b/scripts/quota_check_params.sh index f706c0bb..c5a5bc7f 100644 --- a/scripts/quota_check_params.sh +++ b/scripts/quota_check_params.sh @@ -46,7 +46,7 @@ log_verbose() { } # Default Models and Capacities (Comma-separated in "model:capacity" format) -DEFAULT_MODEL_CAPACITY="gpt-4o:150" +DEFAULT_MODEL_CAPACITY="gpt-5.1:150" # Convert the comma-separated string into an array IFS=',' read -r -a MODEL_CAPACITY_PAIRS <<< "$DEFAULT_MODEL_CAPACITY" diff --git a/src/backend/.env.sample b/src/backend/.env.sample index 3cced35e..c5b811d2 100644 --- a/src/backend/.env.sample +++ b/src/backend/.env.sample @@ -14,11 +14,11 @@ AZURE_BLOB_CONTAINER_NAME= # Azure AI Foundry Connections AZURE_OPENAI_ENDPOINT= -MIGRATOR_AGENT_MODEL_DEPLOY='gpt-4o' -PICKER_AGENT_MODEL_DEPLOY='gpt-4o' -FIXER_AGENT_MODEL_DEPLOY='gpt-4o' -SEMANTIC_VERIFIER_AGENT_MODEL_DEPLOY='gpt-4o' -SYNTAX_CHECKER_AGENT_MODEL_DEPLOY='gpt-4o' +MIGRATOR_AGENT_MODEL_DEPLOY='gpt-5.1' +PICKER_AGENT_MODEL_DEPLOY='gpt-5.1' +FIXER_AGENT_MODEL_DEPLOY='gpt-5.1' +SEMANTIC_VERIFIER_AGENT_MODEL_DEPLOY='gpt-5.1' +SYNTAX_CHECKER_AGENT_MODEL_DEPLOY='gpt-5.1' AZURE_AI_AGENT_PROJECT_CONNECTION_STRING = "" AZURE_AI_AGENT_SUBSCRIPTION_ID = "" AZURE_AI_AGENT_RESOURCE_GROUP_NAME = "" From 2854a95f492774d0d31e579f356332dbc6955c33 Mon Sep 17 00:00:00 2001 From: VishalSh-Microsoft Date: Thu, 16 Jul 2026 14:24:37 +0530 Subject: [PATCH 2/2] fix: update GPT model reference and remove unused AVM telemetry resource from container registry module --- docs/AzureGPTQuotaSettings.md | 2 +- infra/modules/containerRegistry.bicep | 20 -------------------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/docs/AzureGPTQuotaSettings.md b/docs/AzureGPTQuotaSettings.md index cc0fb1b2..95c73031 100644 --- a/docs/AzureGPTQuotaSettings.md +++ b/docs/AzureGPTQuotaSettings.md @@ -5,6 +5,6 @@ 3. **Go to** the `Management Center` from the bottom-left navigation menu. 4. Select `Quota` - Click on the `GlobalStandard` dropdown. - - Select the required **GPT model** (`GPT-4, GPT-5.1`). + - Select the required **GPT model** (`GPT-5.1`). - Choose the **region** where the deployment is hosted. 5. Request More Quota or delete any unused model deployments as needed. \ No newline at end of file diff --git a/infra/modules/containerRegistry.bicep b/infra/modules/containerRegistry.bicep index d95adede..2fff5eda 100644 --- a/infra/modules/containerRegistry.bicep +++ b/infra/modules/containerRegistry.bicep @@ -77,26 +77,6 @@ resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ } }] -// AVM Telemetry -#disable-next-line no-deployments-resources -resource avmTelemetry 'Microsoft.Resources/deployments@2025-04-01' = if (enableTelemetry) { - name: take('46d3xbcp.ptn.acr.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}', 64) - properties: { - mode: 'Incremental' - template: { - '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' - contentVersion: '1.0.0.0' - resources: [] - outputs: { - telemetry: { - type: 'String' - value: 'For more information, see https://aka.ms/avm/TelemetryInfo' - } - } - } - } -} - // Outputs output resourceId string = containerRegistry.id output name string = containerRegistry.name