azureazure-resource-managerazure-keyvaultazure-sql-serverazure-bicep

Configuring encryption protection in Azure SQL server using ARM/Bicep


I'm trying to build both ARM and Bicep templates for enabling BYOK/CMK/TDE on Azure SQL server (and databases).

The challenge I'm having is that templates expect KeyVault Key Version to be passed in as an input. I'd really like to avoid that, as version could eventually change and it's not a value I'd like to maintain as an input parameter.

what I've tried so far is to create these 2 resources for SQL:

Microsoft.Sql/servers/keys@2022-05-01-preview
Microsoft.Sql/servers/encryptionProtector@2022-05-01-preview

encryptionProtector seems pretty straighforward, which just uses servers/keys resource. And that's where I'm stuck.

It requires KV key version for 'name' field, which I expected to be able to get from Microsoft.KeyVault/vaults/keys existing resource. However it only has this property:

keyVaultKey.properties.keyUriWithVersion

My next option was to parse the value, like:

var sqlServerKeyName = '${keyVaultName}_${keyVaultKeyName}_${last(split(keyVaultKey.properties.keyUriWithVersion, '/'))}'

but this results in warning:

his expression is being used in an assignment to the "name" property of the "Microsoft.Sql/servers/keys" type, which requires a value that can be calculated at the start of the deployment. You are referencing a variable which cannot be calculated at the start ("keyVaultKeyName" -> "keyVaultKey"). Properties of keyVaultKey which can be calculated at the start include "apiVersion", "id", "name", "type"

So my question is: is it possible to get KV Key Version from Bicep/ARM template and if yes - how? Or is it generally not recommended to do that (especially in the context of transparent data encryption)?

lastly, if there are no ARM/Bicep based solutions, I guess next best solution could be to try to retrieve latest version via powershell and then pass it as input. any suggestions/examples on this approach maybe?

note: KeyVault and Keys are created in separate deployment so I cannot use KV deployment output for this


Solution

  • The error is just about the name of the resource: the value has to be calculated when the deployment starts which is not possible in your case because the name is generated from another resource.

    You would need to invoke it through another module:

    // sqlserver-keyvault-encryption.bicep
    param sqlServerName string
    param keyVaultName string
    param keyName string
    param keyVersion string
    param keyUri string
    
    resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' existing = {
      name: sqlServerName
    }
    
    // Create sql server key from key vault
    resource sqlServerKey 'Microsoft.Sql/servers/keys@2022-05-01-preview' = {
      name: '${keyVaultName}_${keyName}_${keyVersion}'
      parent: sqlServer
      properties: {
        serverKeyType: 'AzureKeyVault'
        uri: keyUri
      }
    }
    
    // Create the encryption protector
    resource propector 'Microsoft.Sql/servers/encryptionProtector@2022-05-01-preview' = {
      name: 'current'
      parent: sqlServer
      properties: {
        serverKeyType: 'AzureKeyVault'
        serverKeyName: sqlServerKey.name
      }
    }
    

    Then you can invoke it from a parent module:

    param sqlServerName string
    param keyVaultName string
    param keyName string
    
    resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = {
      name: keyVaultName
    }
    
    resource keyVaultKey 'Microsoft.KeyVault/vaults/keys@2022-07-01' existing = {
      name: keyName
      parent: keyVault
    }
    
    module encryption 'sqlserver-keyvault-encryption.bicep' = {
      name: 'sqlserver-keyvault-encryption'
      params: {
        sqlServerName: sqlServerName
        keyVaultName: keyVault.name
        keyName: keyVaultKey.name
        keyVersion: last(split(keyVaultKey.properties.keyUriWithVersion, '/'))
        keyUri: keyVaultKey.properties.keyUriWithVersion
      }
    }