TLDR: Full code example on GitHub.

💡
Previously, I wrote a blog post on how to connect to an Azure SQL database using Managed Identity instead of the traditional connection string containing username and password.


I believe that secretless is the way <insert Mandalorian meme>, and wanted to explore how this can be done for an Azure Function reading from an Azure Service Bus queue.

Last year (10/28/21) Microsoft announced GA for libraries supporting secretless configuration for Storage, Event Hubs and Service Bus via Azure Active Directory 🥳.

Now, before we start looking at some code.. Azure Functions running on .NET5/6 can be run in-process or out-of-process/isolated process. There are some differences there, but both are able to use secretless configuration. The notable difference is that they use different packages for binding extensions:

NB! The Microsoft roadmap states that only isolated process will be supported from .NET7 and onwards, so my code examples will be based on that.

Setting up the infrastructure

I'll need the following infrastructure in Azure, using naming conventions from the Microsoft docs:

  • Resource group named rg-demo
  • Azure Function named func-demo-westeu
    Install packages:
    Azure.Identity >= 1.5.0 (enable support for Managed identity)
    Microsoft.Azure.Functions.Worker.Extensions.ServiceBus >= 5.0.0 (beta is ok)
  • User-Assigned Managed Identity named id-demo-westeu
  • Associate the Managed Identity with the Azure Function (two steps).
    * Assign the Managed Identity to the function.
    * In the function application settings, add AZURE_CLIENT_ID with the clientId of the Managed Identity.
  • Azure Service Bus namespace named sb-demo-westeu
  • Azure Service Bus queue named sbq-demo-westeu
  • For the queue, assign role (using IAM) Azure Service Bus Data Receiver to the Managed Identity (id-demo-westeu). Best practices dictate that it's always best to grant only the narrowest possible scope, so this should be scoped to the queue. (Not the namespace or resource group).

What happened here?

The Managed Identity now have access to read from the queue. Any app associated with this Managed Identity will have this access, meaning the Azure Function will have this access.

The code

The next step is to create the Azure Function that will use a ServiceBusTrigger to retrieve messages from the queue.

I create a new template Azure Function project (.NET 6 Isolated), with one function:

public class ProcessQueueItem
{
    private readonly ILogger _logger;
    public ProcessQueueItem(ILoggerFactory loggerFactory) => _logger = loggerFactory.CreateLogger<ProcessQueueItem>();

    [Function("ProcessQueueItem")]
    public void Run([ServiceBusTrigger("%QueueName%", Connection = "ServiceBusConnection")] string myQueueItem)
    {
        _logger.LogInformation($"C# ServiceBus queue trigger function processed message: {myQueueItem}");
    }
}

Nothing fancy here.. the magic lies in the connection string, which is specified like this:

<connection_name>__fullyQualifiedNamespace=<service_bus_namespace>.servicebus.windows.net

For my app that resolves to:
ServiceBusConnection__fullyQualifiedNamespace=sb-demo-westeu.servicebus.windows.net

No secrets.

Testing

After deploying the function to Azure and specifying the Service Bus connection string in Application settings, I'm ready to test if the function is able to retrieve a message from the queue.

I use Service Bus Explorer to connect to the Azure Service Bus, and it has the capability of sending messages on the queue:

The log indicates that the message was processed successfully:

And there you go, the Azure Function has secretless access to the Azure Service Bus queue.

See the full code example on GitHub.

Error messages you might encounter

Microsoft.Azure.WebJobs.ServiceBus: Microsoft Azure WebJobs SDK ServiceBus connection string 'ServiceBusConnection' is missing or empty

This means that you are not using the correct package.
Be sure that the package Microsoft.Azure.Functions.Worker.Extensions.ServiceBus is >= 5.0.0 (beta is ok).
For in-process functions the package Microsoft.Azure.WebJobs.Extensions.ServiceBus must be >= 5.0.0.

'Listen' claim(s) are required to perform this operation

This means that the Managed Identity part is not working:
* Be sure that the package Azure.Identity is installed >= 1.5.0
* That the Managed Identity has correct access/role.
* That the AZURE_CLIENT_ID is correct in the application settings for the function.

Conclusion

By using the newest libraries from Microsoft, which supports secretless configuration, you can easily grant access to Azure resources by using Managed Identity.

The connection string will only contain the Azure Service Bus namespace, which can easily be set up using IaC provisioning.