How to use Managed Identity in Azure Functions with Service bus trigger

Iqan Shaikh
5 min readJul 8, 2023

--

In this article, we will be creating a simple Azure Function which is triggered by a notification in Azure Service bus topic subscription. The function will simply log message content to the app insights.

Aim is to utilise managed identity for azure function to connect to the service bus without the connection string. We will be using user assigned managed identity for role assignments.

Setting up infrastructure

We will be using Bicep to deploy infrastructure. Below is a visualisation of provisioning templates generated using a utility from VS Code’s bicep extension.

Provisioning templates are in service-bus-trigger-demo/infra directory.

To deploy templates:

az deployment sub create --location <your-preferred-location> --template-file main.bicep

Templates Overview

main.bicep — deploys resource group and invokes resources module

resources.bicep — deploys all resources needed for the demo. Azure Function, App Service plan, Storage Account, Service bus, Role Assignments and Application Insights.

The key parts are role assignments and function app settings.

1. User assigned Identity

resource userIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: '${resourcePrefix}id'
location: location
}

This will create a user assigned identity which can be assigned to azure resources and used for role assignments.

2. Identity assignments for Function App

resource functionApp 'Microsoft.Web/sites@2021-03-01' = {
name: '${resourcePrefix}func'
location: location
kind: 'functionapp'
identity: {
type: 'SystemAssigned, UserAssigned'
userAssignedIdentities: {
'${userIdentity.id}': {}
}
}
}

This will assign identities for function app. For demo purpose, we are using both (system assigned and user assigned identities) in this function app.

3. Storage account role assignment

var storageBlobDataOwnerRoleId = 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b'

resource storageRoleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(storageAccount.name, functionApp.name, storageBlobDataOwnerRoleId)
scope: storageAccount
properties: {
principalId: functionApp.identity.principalId
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', storageBlobDataOwnerRoleId)
principalType: 'ServicePrincipal'
}
}

Azure function needs a storage account to store state and runtime related data. Generally, a storage connection string is used. But here we are assigning “Storage Blob Data Owner” role to the function’s system assigned identity to provide necessary permissions.

You can find all built-in roles here — Azure built-in roles

4. Service bus subscription role assignment

var azureServiceBusDataOwnerRoleId = '090c5cfd-751d-490a-894a-3ce6f1109419'

resource subscriptionRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid('${servicebus.name}${servicebusSubscription.name}', userIdentity.name, azureServiceBusDataOwnerRoleId)
scope: servicebusSubscription
properties: {
principalId: userIdentity.properties.principalId
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', azureServiceBusDataOwnerRoleId)
principalType: 'ServicePrincipal'
}
}

This will assign the user assigned identity “Service bus Data Owner” role. Which will allow function to be triggered from notification in service bus as well as provides necessary permissions to perform operations on messages.

This is how role assignments would look like in azure portal.

5. Function app settings

siteConfig: {
appSettings: [
{
name: 'FUNCTIONS_EXTENSION_VERSION'
value: '~4'
}
{
name: 'FUNCTIONS_WORKER_RUNTIME'
value: 'dotnet'
}
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: applicationInsights.properties.InstrumentationKey
}
{
name: 'AzureWebJobsStorage__accountName'
value: storageAccount.name
}
{
name: 'demoservicebus__fullyQualifiedNamespace'
value: '${servicebus.name}.servicebus.windows.net'
}
{
name: 'demoservicebus__credential'
value: 'managedidentity'
}
{
name: 'demoservicebus__clientID'
value: userIdentity.properties.clientId
}
]
ftpsState: 'FtpsOnly'
minTlsVersion: '1.2'
}

As we can see from the app settings of the function, we are not using any connection string for storage account or service bus, but utilising managed identity. Note the difference between settings for system assigned identity connection and user assigned one.

By default, function app will use the system assigned identity to connect to resources. So if we want to use user assigned identity, we need to provide additional settings for <service-connection-name>__credential and <service-connection-name>__cliendID.

Creating function app with service bus trigger

Once we have infrastructure setup, let’s move on to the development of the function app.

Functions project

Create or use existing function app. I have used CLI to create function app and service bus trigger, but you can use any IDE such as Visual Studio to do the same.

Add new service bus trigger function and use service bus trigger option when prompted.

func new DemoServicebusTrigger

Make appropriate changes to the app. Change service bus topic and subscription name in the trigger attribute to match infrastructure that was deployed using bicep, and use connection name to match the one specified in function app settings.

[FunctionName("DemoServicebusTrigger")]
public void Run([ServiceBusTrigger("demotopic", "demosub", Connection = "demoservicebus")]string mySbMsg)
{
_logger.LogInformation($"C# ServiceBus topic trigger function processed message: {mySbMsg}");
}

By default, logs from custom namespaces are filtered out. To opt-in for custom logging, update host.json file logging section with below settings.

"logLevel": {
"default": "Information"
}

Once happy with the changes, deploy the function app.

func azure functionapp publish <your-function-app-name>

Testing

Once everything is set up and deployed to azure, it’s time to test it. We will use azure portal to send service bus message.

Once the message is sent, you can verify function invocation in the function app or in the application insights. As we have configured the app insights, we can see traces logged as shown below.

Summary

As you can see, how simple it is to set up azure function app with managed identity to access resources. No more worrying about managing/storing/rotating connection-strings/keys. While using Azure RBAC, we should make sure we assign the least privilege access to the service.

Thank you for taking the time to read. Have a great day ahead. :)

Code used in this tutorial is source-controlled in GitHub repository, here: azure-function-managed-identity-demo/service-bus-trigger-demo at main · iqan/azure-function-managed-identity-demo (github.com)

Please have a look, try it out and if there’s any issue or doubts, feel free to raise it as an issue in the repository or post in the comments.

--

--

Iqan Shaikh

Sr. Full Stack Engineer | C# .NET | Azure | AWS | DevOps | React | Node | Docker | Flutter | ReactNative