I wanted to set a password for the admin account of the VM I was creating via Bicep. But I ran into some limitations there. Here how I solved it.
The first issue I ran into: I setup my bicep template in such a way, that a new password was generated on each deployment and added to the KeyVault. The new value was also assigned to the VM admin account in the bicep template. However, when the VM already existed, the password wasn’t changed. This caused some pain after a few deployments to figure out which password was valid.
So I figured I need to change my bicep template in a way, so it only generates a new password when the secret is not already present in KeyVault. If not present, it will generate one. Of course you could also check if the VM already exists, but I chose for the secret check.
Unfortunately there is no built in functionality in bicep (yet) that let’s you do these things. So I had to use some custom powershell script as a deployment script within bicep to support this scenario.
Here the bicep I came up with:
@description('The name of the key vault.')
param keyVaultName string
@description('The name of the secret that needs to be added.')
param secretName string
param location string = resourceGroup().location
param utcValue string = utcNow()
var userAssignedIdentityName = 'keyVaultHandler'
// First create a user assigned managed identity, that has access to the keyvault and can list and add the secret
resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
name: userAssignedIdentityName
location: location
}
resource keyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = {
name: keyVaultName
}
resource userAssignedIdentityKeyVaultAccessPolicy 'Microsoft.KeyVault/vaults/accessPolicies@2021-06-01-preview' = {
name: 'add'
parent: keyVault
properties: {
accessPolicies: [
{
permissions: {
secrets: [
'get'
'list'
'set'
]
}
tenantId: userAssignedIdentity.properties.tenantId
objectId: userAssignedIdentity.properties.principalId
}
]
}
}
// Check if secret exists.
resource checkIfSecretExistsInKeyVault 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
name: 'checkIfSecretExistsInKeyVault'
location: location
kind: 'AzurePowerShell'
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${ userAssignedIdentity.id }': {}
}
}
properties: {
azPowerShellVersion: '8.1'
forceUpdateTag: utcValue // required for unique per deployment
retentionInterval: 'PT1H'
timeout: 'PT30M'
cleanupPreference: 'Always'
arguments: '-keyVaultName ${keyVaultName} -secretName ${secretName}'
scriptContent: '''
param([string] $keyVaultName, [string]$secretName)
# Check if the secret already exists
$secret = Get-AzKeyVaultSecret -VaultName $keyVaultName -Name $secretName
if($secret -eq $null){
Write-Host "Secret [$secretName] doesn't exist yet in keyVault [$keyVaultName]"
#Generate a random password with length 20.
$secretValue = ConvertTo-SecureString (-join([char[]](42..122) | Get-Random -Count 20)) -AsPlainText -Force
Set-AzKeyVaultSecret -VaultName $keyVaultName -Name $secretName -SecretValue $secretValue
}
Write-Host "Secret exists."
'''
}
dependsOn: [
userAssignedIdentityKeyVaultAccessPolicy
]
}
First I had to create a user assigned identity that has access to the KeyVault to verify if the secret is already present and create one if needed.
Next I use a deployment script running as the just created user assigned identity, to check if the secret is present. If that’s the case it will end there. If it doesn’t exists it generates a random password with the length of 20.
Hope this helps.