Azure Bicep & User Assigned Managed Identity

This will be a quick one!

A colleague asked me if it was easier to use user assigned managed identities in Bicep versus ARM. Well, challenge accepted!

After about 45 minutes of hacking, I created the following:

@description('Web app name.')
@minLength(2)
param webAppName string = 'webApp-${uniqueString(resourceGroup().id)}'

@description('Location for all resources.')
param location string = resourceGroup().location

@description('The SKU of App Service Plan.')
param sku string = 'S1'

@description('The Runtime stack of current web app')
param linuxFxVersion string = 'DOTNETCORE|5.0'

param uaminame string = 'uamitest'

param kvName string = 'kv-${uniqueString(resourceGroup().id)}'

@secure()
param secretValue string = uniqueString(newGuid())

var appServicePlanPortalName_var = '${webAppName}-plan'

resource appServicePlanName 'Microsoft.Web/serverfarms@2020-06-01' = {
  name: appServicePlanPortalName_var
  location: location
  sku: {
    name: sku
  }
  kind: 'linux'
  properties: {
    reserved: true
  }
}

resource webApp_resource 'Microsoft.Web/sites@2020-12-01' = {
  name: webAppName
  location: location
  identity: {
    type: 'UserAssigned'
    userAssignedIdentities: {
      '${uami.id}': {}
    }
  }
  properties: {
    serverFarmId: appServicePlanName.id
    siteConfig: {
      linuxFxVersion: linuxFxVersion
    }
    keyVaultReferenceIdentity: uami.id
  }

  resource settings 'config' = {
    name: 'appsettings'
    properties: {
      //SuperSecret: '@Microsoft.KeyVault(SecretUri=${keyVault::secret.properties.secretUriWithVersion})'
      SuperSecret: '@Microsoft.KeyVault(SecretUri=${keyVault::secret.properties.secretUri})'
    }
  }
}

resource webAppSlot 'Microsoft.Web/sites/slots@2020-06-01' = {
  name: '${webAppName}/staging'
  location: location
  kind: 'app'
  properties: {
    serverFarmId: appServicePlanPortalName_var
  }
  dependsOn: [
    webApp_resource
  ]
}

// Create a keyvault, and use a nested resource to set a secret
resource keyVault 'Microsoft.KeyVault/vaults@2019-09-01' = {
  name: kvName
  location: resourceGroup().location
  properties: {
    enabledForDeployment: false
    enabledForTemplateDeployment: false
    enabledForDiskEncryption: false
    tenantId: subscription().tenantId
    enablePurgeProtection: false
    accessPolicies: [
      {
        tenantId: subscription().tenantId
        objectId: uami.properties.principalId
        permissions: {
          keys: [
            'get'
          ]
          secrets: [
            'list'
            'get'
          ]
        }
      }
    ]
    sku: {
      name: 'standard'
      family: 'A'
    }
  }

  resource secret 'secrets' = {
    name: 'mysecret'
    properties: {
      value: secretValue
    }
  }
  dependsOn: [
    uami
  ]
}

// create user assigned managed identity
resource uami 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
  name: uaminame
  location: resourceGroup().location
}

// create role assignment
var KEY_VAULT_SECRETS_USER_ROLE_GUID = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')

resource keyVaultWebsiteUser 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
  name: guid('SecretsUser', webAppName)
  scope: keyVault
  properties: {
    principalId: uami.properties.principalId
    roleDefinitionId: KEY_VAULT_SECRETS_USER_ROLE_GUID
  }
}

Key things to point out here with the web app config:

  • The set of lines
    userAssignedIdentities: {
        '${uami.id}': {}
        }
    
    are where you associate the user-assigned identity id with the web app
  • You also need the line keyVaultReferenceIdentity: uami.id to tell the app service which identity to use when contacting the key vault

Cool stuff!