Skip to content

Commit 07e148f

Browse files
karthiksaligramaKarthik SaligramaCopilot
authored
feat: add optional ACR with Private Endpoint to Bicep VNet scenarios (#519)
* feat: add optional ACR with Private Endpoint to all Bicep VNet scenarios Add an enableContainerRegistry parameter (bool, default false) to all 8 Bicep VNet infrastructure scenarios. When enabled, creates: - Azure Container Registry (Premium SKU, no public access, no admin user) - Private Endpoint in the PE subnet (subresource: registry) - Private DNS Zone (privatelink.azurecr.io) with VNet link - DNS Zone Group for the Private Endpoint Implemented as a shared reusable module (container-registry.bicep) in each scenario's modules-network-secured/ directory. Scenarios updated: - 10-private-network-basic - 11-private-network-basic-vnet - 15-private-network-standard-agent-setup - 15a-private-network-evaluation-only-setup - 16-private-network-standard-agent-apim-setup - 17-private-network-standard-user-assigned-identity-agent-setup - 18-managed-virtual-network - 19-private-network-agent-tools Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: remove ACR from scenarios without network injection (10, 18) The optional ACR with Private Endpoint should only be available in scenarios that have network injection (agent VNet integration), not in PE-only or managed-network scenarios. Removed from: - 10-private-network-basic (PE-only, no agent subnet) - 18-managed-virtual-network (managed network, no agent subnet injection) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: default enableContainerRegistry to true for network injection scenarios Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add canadacentral to allowed locations in scenario 11 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add optional developer IP CIDR allowlist for ACR push access Adds a developerIpCidr parameter (string, default empty) to the ACR module. When provided, enables public network access on the ACR with a network rule that only allows the specified CIDR (e.g., /26). When empty, public access remains fully disabled. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: clarify developerIpCidr accepts any CIDR size Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: make uniqueSuffix deterministic for idempotent re-deploys (scenario 11) Remove utcNow() from the uniqueSuffix calculation so that re-deploying to the same resource group produces the same resource names. This makes deployments idempotent (patch-on-existing) rather than creating new resources on each run. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: make all VNet templates idempotent by removing utcNow from suffix Remove deploymentTimestamp/utcNow() from uniqueSuffix calculation in all templates. The suffix is now derived solely from resourceGroup().id, making deployments idempotent — re-deploying to the same RG updates existing resources in-place rather than creating duplicates. Affected templates: 15, 15a, 16, 17, 18, 19 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: grant AcrPull role to project managed identity on ACR Adds an AcrPull role assignment in the container-registry module so the project's managed identity can pull images from the ACR. The principal ID is passed from each scenario's main.bicep (system-assigned identity for most, user-assigned for scenario 17). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: rebuild compiled JSON templates to include canadacentral Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add canadacentral to allowed locations in scenario 19 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add canadacentral to allowed locations in scenario 15 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Karthik Saligrama <ksaligrama@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 73df0d7 commit 07e148f

15 files changed

Lines changed: 1685 additions & 69 deletions

File tree

infrastructure/infrastructure-setup-bicep/11-private-network-basic-vnet/azuredeploy.json

Lines changed: 300 additions & 27 deletions
Large diffs are not rendered by default.

infrastructure/infrastructure-setup-bicep/11-private-network-basic-vnet/main.bicep

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ are used instead.
3030
'australiaeast'
3131
'swedencentral'
3232
'canadaeast'
33+
'canadacentral'
3334
'westeurope'
3435
'westus3'
3536
'uksouth'
@@ -59,9 +60,10 @@ param modelSkuName string = 'GlobalStandard'
5960
param modelCapacity int = 30
6061

6162
// Create a short, unique suffix, that will be unique to each resource group
62-
param deploymentTimestamp string = utcNow('yyyyMMddHHmmss')
63-
var uniqueSuffix = substring(uniqueString('${resourceGroup().id}-${deploymentTimestamp}'), 0, 4)
63+
// Uses only resourceGroup().id for determinism — re-deploys target the same resources
64+
var uniqueSuffix = substring(uniqueString(resourceGroup().id), 0, 4)
6465
var accountName = toLower('${aiServices}${uniqueSuffix}')
66+
var acrName = toLower('acr${uniqueSuffix}')
6567

6668
@description('Name for your project resource.')
6769
param firstProjectName string = 'project'
@@ -94,6 +96,12 @@ param agentSubnetPrefix string = ''
9496
@description('Address prefix for the private endpoint subnet')
9597
param peSubnetPrefix string = ''
9698

99+
@description('Enable Azure Container Registry with Private Endpoint. When true, creates an ACR (Premium SKU) with a PE in the private endpoints subnet.')
100+
param enableContainerRegistry bool = true
101+
102+
@description('Optional developer IP CIDR to allowlist for ACR push access (e.g., 203.0.113.0/26 or 10.0.0.0/16). When empty, public access remains disabled.')
103+
param developerIpCidr string = ''
104+
97105
// DNS zone parameters
98106
@description('Subscription ID where existing private DNS zones are located. Leave empty to use current subscription.')
99107
param dnsZonesSubscriptionId string = ''
@@ -103,8 +111,17 @@ param existingDnsZones object = {
103111
'privatelink.services.ai.azure.com': ''
104112
'privatelink.openai.azure.com': ''
105113
'privatelink.cognitiveservices.azure.com': ''
114+
'privatelink.azurecr.io': ''
106115
}
107116

117+
@description('Zone Names for Validation of existing Private Dns Zones')
118+
param dnsZoneNames array = [
119+
'privatelink.services.ai.azure.com'
120+
'privatelink.openai.azure.com'
121+
'privatelink.cognitiveservices.azure.com'
122+
'privatelink.azurecr.io'
123+
]
124+
108125
@description('The name of the project capability host to be created')
109126
param projectCapHost string = 'caphostproj'
110127

@@ -185,6 +202,25 @@ module privateEndpointAndDNS 'modules-network-secured/private-endpoint-and-dns.b
185202
}
186203
}
187204

205+
// Optional: Azure Container Registry with Private Endpoint
206+
module acr 'modules-network-secured/container-registry.bicep' = if (enableContainerRegistry) {
207+
name: 'acr-${uniqueSuffix}-deployment'
208+
params: {
209+
acrName: acrName
210+
location: location
211+
peSubnetId: vnet.outputs.peSubnetId
212+
vnetId: vnet.outputs.virtualNetworkId
213+
suffix: uniqueSuffix
214+
existingDnsZoneResourceGroup: existingDnsZones['privatelink.azurecr.io']
215+
dnsZonesSubscriptionId: resolvedDnsZonesSubscriptionId
216+
developerIpCidr: developerIpCidr
217+
projectPrincipalId: project.identity.principalId
218+
}
219+
dependsOn: [
220+
privateEndpointAndDNS
221+
]
222+
}
223+
188224
/*
189225
Step 4: Create a Project
190226
- Sub-resource of the AI Services account

0 commit comments

Comments
 (0)