Azure function apps require access to a storage account so they may store host keys in a blob container.
The traditional way to provide this access is to include a configuration setting of AzureWebJobsStorage with a value of the connection string. However, in order to remove the need to manage the connection string (including the key), Microsoft introduced the ability to configure the function app to use its managed identity to access the storage account. This is described here
I have found that I can change an existing function app to use the managed identity approach. However, if I deploy a new app via bicep then the storage account container and host keys are not created. I've gone back to basics and used a simple sample bicep template from here
I updated the bicep so that a user managed identity is created. This is granted storage contributor and storage blob owner roles. The updated bicep is shown below:
@description('Specifies region of all resources.')
param location string = resourceGroup().location
@description('Suffix for function app, storage account, and key vault names.')
param appNameSuffix string = uniqueString(resourceGroup().id)
@description('Storage account SKU name.')
param storageSku string = 'Standard_LRS'
var functionAppName = 'fn-${appNameSuffix}'
var appServicePlanName = 'FunctionPlan'
var storageAccountName = 'fnstor${replace(appNameSuffix, '-', '')}'
var functionNameComputed = 'MyHttpTriggeredFunction'
var functionRuntime = 'dotnet'
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-04-01' = {
name: storageAccountName
location: location
sku: {
name: storageSku
}
kind: 'StorageV2'
properties: {
supportsHttpsTrafficOnly: true
encryption: {
services: {
file: {
keyType: 'Account'
enabled: true
}
blob: {
keyType: 'Account'
enabled: true
}
}
keySource: 'Microsoft.Storage'
}
accessTier: 'Hot'
}
}
// Create Managed Identity for the funciton app
var managedIdName = 'id-${functionAppName}'
resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
name: managedIdName
location: location
}
var blobOwnerRoleId = 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b'
resource roleDefinitionBlobContributor 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' existing = {
scope: resourceGroup()
name: blobOwnerRoleId
}
// Role assignment
resource roleAssignmentBlob 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
name: guid(functionAppName, blobOwnerRoleId)
scope: storageAccount
properties: {
roleDefinitionId: roleDefinitionBlobContributor.id
principalId: managedIdentity.properties.principalId
}
}
var storageContributorRoleId = '17d1049b-9a84-46fb-8f53-869881c3d3ab'
resource roleDefinitionStorageContributor 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' existing = {
scope: resourceGroup()
name: storageContributorRoleId
}
resource roleAssignmentContrib 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
name: guid(functionAppName, storageContributorRoleId)
scope: storageAccount
properties: {
roleDefinitionId: roleDefinitionStorageContributor.id
principalId: managedIdentity.properties.principalId
}
}
resource plan 'Microsoft.Web/serverfarms@2020-12-01' = {
name: appServicePlanName
location: location
kind: 'functionapp'
sku: {
name: 'Y1'
}
properties: {}
}
resource functionApp 'Microsoft.Web/sites@2020-12-01' = {
name: functionAppName
location: location
kind: 'functionapp'
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${managedIdentity.id}': {}
}
}
properties: {
serverFarmId: plan.id
siteConfig: {
appSettings: [
{
name: 'AzureWebJobsStorage__accountname'
value: storageAccountName
}
{
name: 'AzureWebJobsStorage__blobServiceUri'
value: 'https://${storageAccount.name}.blob.core.windows.net'
}
{
name: 'FUNCTIONS_WORKER_RUNTIME'
value: functionRuntime
}
{
name: 'FUNCTIONS_EXTENSION_VERSION'
value: '~4'
}
]
}
httpsOnly: true
}
dependsOn: [
roleAssignmentBlob
]
}
Has anyone had any success in using bicep/arm to deploy a function app and storage account that uses managed identity?