So yesterday, I posted a blog post about setting up azure storage accounts for Customer-Managed keys. You can see that post here. Now, I will say the implementation of this with regard to container registry is tricky because it doesn’t look the same for container registry.
Setting up Key Vault
The following are the required steps for setting up a keyvault:
param location string
param key_vault_name string
param private_dns_zone_name string = 'privatelink.vaultcore.azure.net'
param key_vault_sku string = 'standard'
param key_vault_sku_family string = 'A'
param subnet_id string
param vnet_id string
// Managed Identity Configuration:
param registry_managed_identity_name string = 'registry-managed-identity'
// Role Configuration
param registry_account_role_definition_id string = 'e147488a-f6f5-4113-8e2d-b22465e65bf6' // Key Vault Crypto Service Encryption User
// Key Configuration:
param registry_account_key_name string
// Tag Configuration:
param default_tag_name string
param default_tag_value string
var randomSuffix = uniqueString(resourceGroup().id, key_vault_name)
var maxLength = 24
var truncatedKeyVaultName = substring('${key_vault_name}-${randomSuffix}', 0, maxLength)
//var randomSuffix = uniqueString(subscription().subscriptionId, location)
resource key_vault 'Microsoft.KeyVault/vaults@2022-07-01' = {
name: truncatedKeyVaultName
location: location
tags: {
'${default_tag_name}': default_tag_value
}
properties: {
sku: {
name: key_vault_sku
family: key_vault_sku_family
}
tenantId: subscription().tenantId
accessPolicies: []
enabledForDeployment: true
enabledForDiskEncryption: true
enabledForTemplateDeployment: true
enableSoftDelete: true
softDeleteRetentionInDays: 90
enableRbacAuthorization: true
enablePurgeProtection: true
publicNetworkAccess: 'Disabled'
networkAcls: {
defaultAction: 'Deny'
ipRules: []
virtualNetworkRules: []
}
}
}
// DNS Configuration:
resource private_dns_zone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
name: private_dns_zone_name
location: 'global'
tags: {
'${default_tag_name}': default_tag_value
}
}
resource private_endpoint 'Microsoft.Network/privateEndpoints@2022-05-01' = {
name: '${key_vault.name}-pe'
location: location
tags: {
'${default_tag_name}': default_tag_value
}
properties: {
privateLinkServiceConnections: [
{
name: '${key_vault.name}-plsc'
properties: {
privateLinkServiceId: key_vault.id
groupIds: [
'vault'
]
}
}
]
subnet: {
id: subnet_id
}
}
}
resource privateDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2018-09-01' = {
name: '${private_dns_zone.name}-link'
tags: {
'${default_tag_name}': default_tag_value
}
parent: private_dns_zone
location: 'global'
properties: {
registrationEnabled: false
virtualNetwork: {
id: vnet_id
}
}
}
resource private_dns_zone_group 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-09-01' = {
name: 'default'
parent: private_endpoint
properties: {
privateDnsZoneConfigs: [
{
name: 'keyVaultConfig1'
properties: {
privateDnsZoneId: private_dns_zone.id
}
}
]
}
}
And then you need to create the managed identity:
resource registry_managed_identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: registry_managed_identity_name
location: resourceGroup().location
tags: {
'${default_tag_name}': default_tag_value
}
}
And then create the role assignment:
resource registry_roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
scope: key_vault
name: guid(key_vault.id, registry_managed_identity.id, registry_account_role_definition_id)
properties: {
roleDefinitionId: '/providers/Microsoft.Authorization/roleDefinitions/${registry_account_role_definition_id}' // Key Vault Crypto Service Encryption User
principalId: registry_managed_identity.properties.principalId
}
}
And then you need to create the key:
resource registry_key 'Microsoft.KeyVault/vaults/keys@2024-11-01' = {
parent: key_vault
name: registry_account_key_name
properties: {
attributes: {
enabled: true
exportable: false
}
kty: 'RSA'
keySize: 2048
}
}
Configure the registry:
The following then in the module that will setup for customer-managed keys for the container registry:
param acr_name string
param location string
param sku string = 'Premium'
param private_dns_zone_name string = 'privatelink.azurecr.io'
param subnetId string
param vnet_id string
// Identity Configuration:
param registry_managed_identity_id string
param registry_managed_identity_principal_id string
param registry_managed_identity_client_id string
param registry_role_definition_id string = 'e147488a-f6f5-4113-8e2d-b22465e65bf6' // AcrKeyVaultReader role for Azure Government
// Key Vault Configuration:
param registry_key_uri string // Key Vault URI for the customer-managed key
param default_tag_name string
param default_tag_value string
resource container_registry 'Microsoft.ContainerRegistry/registries@2021-06-01-preview' = {
name: acr_name
location: location
tags: {
'${default_tag_name}': default_tag_value
}
sku: {
name: sku
}
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${registry_managed_identity_id}': {}
}
}
properties: {
adminUserEnabled: false
publicNetworkAccess: 'Disabled'
encryption: {
status: 'Enabled'
keyVaultProperties: {
identity: registry_managed_identity_client_id
keyIdentifier: registry_key_uri
}
}
policies: {
quarantinePolicy: {
status: 'disabled'
}
trustPolicy: {
type: 'Notary'
status: 'disabled'
}
retentionPolicy: {
days: 7
status: 'disabled'
}
}
}
}
resource private_endpoint 'Microsoft.Network/privateEndpoints@2020-11-01' = {
name: '${acr_name}-pe'
location: location
tags: {
'${default_tag_name}': default_tag_value
}
properties: {
privateLinkServiceConnections: [
{
name: '${acr_name}-plsc'
properties: {
privateLinkServiceId: container_registry.id
groupIds: [
'registry'
]
}
}
]
subnet: {
id: subnetId
}
}
}
resource private_dns_zone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
name: private_dns_zone_name
location: 'global'
tags: {
'${default_tag_name}': default_tag_value
}
}
resource privateDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2018-09-01' = {
name: '${private_dns_zone.name}-link'
parent: private_dns_zone
location: 'global'
tags: {
'${default_tag_name}': default_tag_value
}
properties: {
registrationEnabled: false
virtualNetwork: {
id: vnet_id
}
}
}
resource registry_dns_zone_group 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2020-11-01' = {
name: 'default'
parent: private_endpoint
properties: {
privateDnsZoneConfigs: [
{
name: 'config1'
properties: {
privateDnsZoneId: private_dns_zone.id
}
}
]
}
}
And then make sure to add the following to create the appropriate role assignment:
resource acr_role_assignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
name: guid(container_registry.id, registry_managed_identity_id, 'AcrKeyVaultReader')
scope: container_registry
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', registry_role_definition_id) // AcrKeyVaultReader role
principalId: registry_managed_identity_principal_id
}
}
If you want a full version of the file, look key-vault.bicep, registry.bicep, and main.bicep.