So for security levels, especially in government when you starting talking Impact Levels of 4+, one of the key requirements is customer-managed keys. The reason for this being is that it ensures that no one can gain access to the encrypted data without using a key that yourself control, not even a cloud service provider. So the question becomes how to enable that in bicep.
Setting up the key vault
So the first step of that, is how do we configure a keyvault with managed identity to support this. You can do that with the following:
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 storage_managed_identity_name string = 'storage-managed-identity'
// Role Configuration
param storage_account_role_definition_id string = 'e147488a-f6f5-4113-8e2d-b22465e65bf6' // Key Vault Crypto Service Encryption User
// Key Configuration:
param storage_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: []
}
}
}
Next step is to setup the managed identity, which can be done in the following manner:
resource storage_managed_identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: storage_managed_identity_name
location: resourceGroup().location
tags: {
'${default_tag_name}': default_tag_value
}
}
Next you need to configure the role assignment for “Key Vault Crypto Service Encryption User”:
// Managed Identity Role Assignments
resource storage_roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
scope: key_vault
name: guid(key_vault.id, storage_managed_identity.id, storage_account_role_definition_id)
properties: {
roleDefinitionId: '/providers/Microsoft.Authorization/roleDefinitions/${storage_account_role_definition_id}' // Key Vault Crypto Service Encryption User
principalId: storage_managed_identity.properties.principalId
}
}
And then we need to create the key in the keyvault to be used:
resource storage_key 'Microsoft.KeyVault/vaults/keys@2024-11-01' = {
parent: key_vault
name: storage_account_key_name
properties: {
attributes: {
enabled: true
exportable: false
}
kty: 'RSA'
keySize: 2048
}
}
Setting up the storage account
Next up, we have to configure the storage account, which can be done in the following:
param location string = resourceGroup().location
param storage_account_name string
param privateDnsZoneName string = 'privatelink.${environment().suffixes.storage}'
param subnet_id string
param vnet_id string
param default_tag_name string
param default_tag_value string
// Key Vault Configuration:
param key_vault_uri string // Key Vault URI for the customer-managed key
param key_name string // Key name in the Key Vault
// Identity Configuration:
param storage_managed_identity_id string
resource storage_account 'Microsoft.Storage/storageAccounts@2022-05-01' = {
name: storage_account_name
location: location
tags: {
'${default_tag_name}': default_tag_value
}
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${storage_managed_identity_id}': {}
}
}
properties: {
publicNetworkAccess: 'Disabled'
supportsHttpsTrafficOnly: true
allowBlobPublicAccess: false
allowSharedKeyAccess: false
encryption: {
identity: {
userAssignedIdentity: storage_managed_identity_id
}
keySource: 'Microsoft.Keyvault'
keyvaultproperties: {
keyvaulturi: key_vault_uri
keyname: key_name
}
services: {
file: {
keyType: 'Account'
enabled: true
}
table: {
keyType: 'Account'
enabled: true
}
queue: {
keyType: 'Account'
enabled: true
}
blob: {
keyType: 'Account'
enabled: true
}
}
}
networkAcls: {
defaultAction: 'Deny'
bypass: 'AzureServices'
ipRules: []
virtualNetworkRules: []
resourceAccessRules: []
}
}
}
For this to work, you need to associate the identity with the storage account AND configure it in the encryption section to make this success. f
If you want a full version of the file, look key-vault.bicep, storage.bicep, and main.bicep.
Hope this helps!