Problem
I’m having a bit of a stumbling block with the implementation logic.
To build those resources, I’m collaborating with Bicep.
The first three steps have been completed. If I declare two storage accounts, it will generate two secret connection strings and two keys automatically. They match in every pairing (storage name and connection string).
Now the issue I am facing is the following, to start with, this is my code.
param tenantCode array = [
'dsec'
'sdre'
]
var storageName = [for item in tenantCode :{
name: string('sthrideveur${item}')
}]
var connectionStringSecretName = [for connection in storageName :{
name: '${connection.name}'
}]
output connectionStringSecretName array= [for connection in storageName :{
name: '${connection.name}'
}]
resource storage_Accounts 'Microsoft.Storage/storageAccounts@2021-06-01' = [ for name in storageName :{
name: '${name.name}'
location: 'westeurope'
sku: {
name: 'Standard_RAGRS'
}
kind: 'StorageV2'
properties: {
allowCrossTenantReplication: true
minimumTlsVersion: 'TLS1_2'
allowBlobPublicAccess: false
allowSharedKeyAccess: true
networkAcls: {
bypass: 'AzureServices'
virtualNetworkRules: []
ipRules: []
defaultAction: 'Allow'
}
supportsHttpsTrafficOnly: true
encryption: {
services: {
file: {
keyType: 'Account'
enabled: true
}
blob: {
keyType: 'Account'
enabled: true
}
}
keySource: 'Microsoft.Storage'
keyvaultproperties: {
keyname: '${tenantKey[0]}'
keyvaulturi: keyVault.id
}
}
accessTier: 'Cool'
}
}]
resource keyVault 'Microsoft.KeyVault/vaults@2019-09-01' existing = {
name : 'XXX'
}
// Store the connection string in KV if specified
resource storageAccountConnectionString 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = [for name in storageName :{
name: '${keyVault.name}/${name.name}'
properties: {
value: 'DefaultEndpointsProtocol=https;AccountName=${storage_Accounts[0].name};AccountKey=${listKeys('${storage_Accounts[0].id}', '${storage_Accounts[0].apiVersion}').keys[0].value};EndpointSuffix=${environment().suffixes.storage}'
}
}]
resource tenantKey 'Microsoft.KeyVault/vaults/keys@2021-06-01-preview' = [for tenant in tenantCode : {
name: '${keyVault.name}/Client-Key-${tenant}'
properties: {
keySize: 2048
kty: 'RSA'
}
}]
I’d like to setup two storage accounts. The keys, on the other hand, include the storage code. What I want to do, and what I’m having trouble doing, is figure out how to match the right key to the right storage account. In this instance, I must use the following codes:
dsec
sdre
The bicep script will create two storage accounts with the following names:
sthrideveurdsec
sthrideveursdre
AND 2 secrets with the same name
sthrideveurdsec
sthrideveursdre
AND 2 Keys named
Client-Key-dsec
Client-Key-sdre
What I’d like to do is encrypt the DSEC storage account with the DSEC key and the SDRE storage account with the SDRE key. But, because I’m new to biceps, I’m having trouble putting this together.
I’d appreciate it if someone could explain how to get this correct matching.
UPDATE: After testing Thomas solution, this is the error I am getting:
{"status":"Failed","error":{"code":"DeploymentFailed","message":"At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/DeployOperations for usage details.","details":[{"code":"BadRequest","message":"{\r\n \"error\": {\r\n \"code\": \"KeyVaultPolicyError\",\r\n \"message\": \"Keyvault policy recoverable is not set\"\r\n }\r\n}"},{"code":"BadRequest","message":"{\r\n \"error\": {\r\n \"code\": \"KeyVaultPolicyError\",\r\n \"message\": \"Keyvault policy recoverable is not set\"\r\n }\r\n}"}]}}
Asked by Nayden Van
Solution #1
I’m presuming the key vault has been built and that access policies are being used.
If you want to build a storage with customer-managed keys, the storage must first gain access to the key vault, therefore in my example, I’m using a user-assigned identity.
The steps are as follows:
// Default values I'm using to test
param keyVaultName string = 'kvthomastest'
param managedIdentityName string = 'mi-storage-encryption-thomas-test'
param tenantCodes array = [
'dsec'
'sdre'
]
// I'm using prefix so I dont need to create additional arrays
var keyVaultKeyPrefix = 'Client-Key-'
var storagePrefix = 'stthomastest'
// Get a reference to key vault
resource keyVault 'Microsoft.KeyVault/vaults@2021-06-01-preview' existing = {
name: keyVaultName
}
// Create a managed identity
resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
name: managedIdentityName
location: resourceGroup().location
}
// Grant permissions to key vault
resource accessPolicy 'Microsoft.KeyVault/vaults/accessPolicies@2019-09-01' = {
name: '${keyVault.name}/add'
properties: {
accessPolicies: [
{
tenantId: subscription().tenantId
objectId: managedIdentity.properties.principalId
permissions: {
// minimum required permissions
keys: [
'get'
'unwrapKey'
'wrapKey'
]
}
}
]
}
}
// Create key vault keys
resource keyVaultKeys 'Microsoft.KeyVault/vaults/keys@2021-06-01-preview' = [for tenantCode in tenantCodes: {
name: '${keyVault.name}/${keyVaultKeyPrefix}${tenantCode}'
properties: {
keySize: 2048
kty: 'RSA'
// storage key should only needs these operations
keyOps: [
'unwrapKey'
'wrapKey'
]
}
}]
// Create storage accounts
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-04-01' = [for tenantCode in tenantCodes: {
name: '${storagePrefix}${tenantCode}'
location: resourceGroup().location
kind: 'StorageV2'
sku: {
name: 'Standard_RAGRS'
}
// Assign the identity
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${managedIdentity.id}': {}
}
}
properties: {
allowCrossTenantReplication: true
minimumTlsVersion: 'TLS1_2'
allowBlobPublicAccess: false
allowSharedKeyAccess: true
networkAcls: {
bypass: 'AzureServices'
virtualNetworkRules: []
ipRules: []
defaultAction: 'Allow'
}
supportsHttpsTrafficOnly: true
encryption: {
identity: {
// specify which identity to use
userAssignedIdentity: managedIdentity.id
}
keySource: 'Microsoft.Keyvault'
keyvaultproperties: {
keyname: '${keyVaultKeyPrefix}${tenantCode}'
keyvaulturi: keyVault.properties.vaultUri
}
services: {
file: {
keyType: 'Account'
enabled: true
}
blob: {
keyType: 'Account'
enabled: true
}
}
}
accessTier: 'Cool'
}
}]
Answered by Thomas
Post is based on https://stackoverflow.com/questions/69665879/bicep-loop-over-key-vault-keys-and-encrypt-storage-account