Coder Perfect

Retrieving key vault secret from a keyvault created in same bicep

Problem

I’m having a problem that’s similar to the one described in Retrieve a secret from a keyvault and use it as input for Synapse Workspace creation in Bicep.

(Though I’m afraid I won’t be able to continue the conversation there because I don’t have enough rep.)

Keyvaults and virtual machines must be generated in the same single deployment template file. The password for the VM’s local admin should be established using a secret produced in the keyvault bicep module file. Although it is not required that the secret be produced at the keyvault module level, I do not believe that there is any other way to ensure that the deployment is secure.

This is the section of the main bicep template that is relevant:

module kv './modules/keyvault.bicep' = {     
  name: '${deployment().name}-kv'     
  scope: rg     
  params: {     
    keyvaultName: kvName    
    createLocalAdmin: true     
  }     
} 

resource kvRef 'Microsoft.KeyVault/vaults@2019-09-01' existing = {     
  name: kvName     
  scope: rg     
}      

module vm './modules/vm.bicep' = {     
  name: '${deployment().name}-vm-${vm.hostShortName}'     
  scope: rg     
  params: {     
    adminPassword: kvRef.getSecret('localadmin')     
    adminUsername: 'localadmin'     
  }     
  dependsOn: [     
    network     
    kv     
  ]     
} 

When I deploy the bicep, I receive the following error: The given KeyVault ‘KEYVAULT ID>’ could not be discovered. The template works great if I replace kvRef.getSecret(‘localadmin’) with a random valued string.

This can be solved using the ARM template, for example:

"adminPassword": {"Value":{
    "reference": { 
        "keyVault": { 
            "id":"concat(subscription().Id,'/resourceGroups/',variables('rgName'),'/providers/Microsoft.KeyVault/vaults/',reference('deploy-keyvault').outputs.KeyvaultName.value)]" 
         }, 
        "secretName": "[variables('localAdminSecretname')]" 
        } 
    } 
} 

Is there a method to use the same approach in biceps as in the ARM example, or is there another way to do this?

Asked by Sam Hall

Solution #1

This is a difficulty with parallelism and order of operations.

module kv './modules/keyvault.bicep' = {     
  name: '${deployment().name}-kv'     
  scope: rg     
  params: {     
    keyvaultName: kvName    
    createLocalAdmin: true     
  }     
} 

resource kvRef 'Microsoft.KeyVault/vaults@2019-09-01' existing = {     
  name: kvName     
  scope: rg     
}      

Each module is deployed as a sub-deployment, which can execute in parallel. To generate dependsOn references, Bicep relies on reference usage. For example, if your module outputs a name property, refer to it as follows:

resource kvRef 'Microsoft.KeyVault/vaults@2019-09-01' existing = {     
  name: kv.outputs.name
  scope: rg     
}   

Bicep would be able to understand that one has to run before the other automatically. Since you’re not doing that, you have to explicitly tell it.

The solution is to add a dependsOn to your existing keyvault, which tells it to wait for the previous module to finish before proceeding.

resource kvRef 'Microsoft.KeyVault/vaults@2019-09-01' existing = {     
  name: kvName     
  scope: rg     
  dependsOn: [ kv ]
}   

Answered by Daniel Mann

Solution #2

Perhaps you could divide the deployment of Key Vault and the resources that require the instance you just built as follows:

https://github.com/mariomeyrelles/bicep-functions-cosmos-keyvault/blob/main/src/main.bicep

Answered by Mário Meyrelles

Post is based on https://stackoverflow.com/questions/70436348/retrieving-key-vault-secret-from-a-keyvault-created-in-same-bicep