Use the new Azure SDKs with Managed Identities

Over the years there's been a plethora of API's and approaches to work with artifacts and resources in Azure.

In July this year (2019), Microsoft announced the availability of the new Azure SDK API Standards, which is a new set of SDK's in the following languages: Python, Java, JavaScript and .NET.

These SDK's are supposed to be a unified approach to building the SDK's, and whatever goes into one API goes into all of them, so there's no disconnect between capabilities in different programming languages.

Now that some Azure services have matured and been adopted into  business-critical enterprise applications, we have been learning what  patterns and practices were critical to developer productivity around  these services
- Peter Marcu, Microsoft Azure Blog

In this post, I am focusing on showing how you can use these new libraries to work with Azure, and specifically with how to use these with Managed Identity to allow us to stay passwordless and without keys/credentials in code.

The how and why with Managed Identities

I will assume that you can enable a System Assigned Managed Identity for the Function App - there's already plenty of resources available for these things, so I'll try to focus on additional value in this post that hasn't been covered before.

If you haven't configured a Managed Identity, here's some guidelines:

The Object ID

Since the previous link explains exactly how to configure Managed Identities, I will only give you a glimps of what mine looks like - and the reason for this is that I'll need to know my objectId later when I examine any success or failures in the logs.

Azure Function System Assigned Managed Identity and the Object ID that we can use to digest logs later

Auditing and Logging

When using identities tied directly to a specific application or resource in Azure, it's easier to trace where requests are coming from, than if you'd use the storage account key that provides global access to the account - and could in theory be used from Azure Storage Explorer, command line, applications or individuals performing various tasks.

With auditing enabled for Azure resources, in my case a Storage Account, I can keep accountability and ensure that successful requets are indeed coming from where they are supposed to.

As an example, I have enabled Storage Logging as per this guide:

I want to know when any request is done to my queues, and hence I've configured just that. As a result, I now can see in the logs that I have requests coming in to my queues in the storage account, and that they are originating from a specific identity that happens to have the same objectId as my Function App's Managed Identity.

2.0;2019-12-16T14:10:00.0555879Z;CreateQueue;OAuthSuccess;204;17;17;bearer;azuresdkstorage123;azuresdkstorage123;queue;"https://azuresdkstorage123.queue.core.windows.net:443/queuedemo";"/azuresdkstorage123/queuedemo";cb1e1eae-f003-0010-2a1a-b45216000000;0;10.156.132.153:52054;2018-11-09;1755;0;117;0;0;;;;;;"azsdk-net-Storage.Queues/12.1.0+ee51b9a6328321b8bc491824116ec9038f2fed5b (.NET Core 3.1.0; Microsoft Windows 10.0.14393)";;"16b54336-43f7-41d1-acd0-ab29a4e2b1f2";"5e0c09ab-6922-4460-99df-1cf3de8a1bb0";"6134e5d3-a0f6-415f-8086-81b9ea6d93c5";"e6168112-9472-4416-9b35-75878c7609f4";"https://storage.azure.com";"https://sts.windows.net/6134e5d3-a0f6-415f-8086-81b9ea6d93c5/";;;"[{"action":"Microsoft.Storage/storageAccounts/queueServices/queues/write", "roleAssignmentId":"662b30d7-c8a3-4726-a840-fa5b6b8762c2", "roleDefinitionId":"17d1049b-9a84-46fb-8f53-869881c3d3ab", "principals": [{"id": "5e0c09ab-6922-4460-99df-1cf3de8a1bb0", "type":"ServicePrincipal"}], "denyAssignmentId":""}]"
2.0;2019-12-16T14:10:00.0735992Z;PutMessage;OAuthSuccess;201;5;4;bearer;azuresdkstorage123;azuresdkstorage123;queue;"https://azuresdkstorage123.queue.core.windows.net:443/queuedemo/messages";"/azuresdkstorage123/queuedemo/b7c25e2f-7d62-4e10-9eed-8522d99dcb5e";cb1e1ed1-f003-0010-491a-b45216000000;0;10.156.132.153:52054;2018-11-09;1793;73;153;407;73;;;;;;"azsdk-net-Storage.Queues/12.1.0+ee51b9a6328321b8bc491824116ec9038f2fed5b (.NET Core 3.1.0; Microsoft Windows 10.0.14393)";;"cd6d7e4a-b71c-4adc-8790-cbb3cadb3454";"5e0c09ab-6922-4460-99df-1cf3de8a1bb0";"6134e5d3-a0f6-415f-8086-81b9ea6d93c5";"e6168112-9472-4416-9b35-75878c7609f4";"https://storage.azure.com";"https://sts.windows.net/6134e5d3-a0f6-415f-8086-81b9ea6d93c5/";;;"[{"action":"Microsoft.Storage/storageAccounts/queueServices/queues/messages/write", "roleAssignmentId":"c3334a9f-4441-4375-8190-984bcbcf5ca6", "roleDefinitionId":"974c5e8b-45b9-4653-ba55-5f855dd0fb88", "principals": [{"id": "5e0c09ab-6922-4460-99df-1cf3de8a1bb0", "type":"ServicePrincipal"}], "denyAssignmentId":""}]"

It is not a particularly nice view, but you can now digest the logs into any great log viewer of choice.

This means that if I have multiple applications and individuals working with a resource, and we use RBAC and identities, we can track what's happening a little bit better.

Configure RBAC access to the Managed Identity

I am using a System Assigned Managed Identity behind an Azure Function App in my example. I need to tie that identity to the correct RBAC role in order to successfully complete any operations for the Azure Storage Queues.

Since I want to read-write to the queues, there's a single role I can assign to my identity, to avoid granting too much access:

  • Storage Queue Data Contributor
Azure Storage Account - Storage Queue Data Contributor RBAC

Once this role is granted to my Identity, the application can successfully do the read/write operations on the queues in that storage account, and I can relax knowing that we're not using a full-control full-access storage account key for the application.

As a sidenote, but still a bit related, I wrote about assigning RBAC on a Storage Account with the Storage Queue Data Reader role when it was in preview during 2018. Read more about that here:

Where do I see the new Azure SDK versions?

To see all available SDKs, including to learn about what the latest versions are, you can find more info here:

Today, these are the available SDKs and their latest versions:

Azure SDK for .NET Core and their respective versions.

When I first started trying these Azure SDK libraries in early August, they were mostly in preview. A lot of them have made it into released major versions and should be in a better posture and ready for reality today.

As you can see from the list above, there's quite a lot of SDKs coming out of preview and into General Availability, so if you want to excplicitly target Azure Key Vault or somethign else, keep an eye on the releases.

Let's move on to a practical example of how to use Managed Identity with these SDKs. In my example I am targeting an Azure Storage account Queue with read/write access, which requires me to delegate role based access to my identity. Tag along in the following sections.

Building an application using the new Azure SDK for .NET

In order to use these new libraries, we need to reference the NuGet packages.

With these new SDKs, you'll notice that they all start with "Azure." as opposed to "MicrosoftAzure." or "Microsoft.Azure." as the older ones are named.

We can get an indication of how new these SDKs are, and how they have been adopted compared to the ones that's been out there for years. If something works, there's no reason to change it - but if you start a new solution, there's a reason to contemplate whether it makes sense to move onto the new libraries. This is a decision only you can do for you and your team(s).

Azure Storage NuGet packages, the new Azure SDKs versus the older ones.

Some of my solutions rely heavily on the old WindowsAzure.Storage code still, while some newer ones rely solely on Azure.Storage.* for example. There's no black or white here - only "it depends".

Reference the new NuGet Packages

In my example I'll work with Azure Storage Queues, and there's a completely new SDK for this specific purpose called "Azure.Storage.Queues" .

When I initially wrote this draft a few months back, the SDKs were in preview and didn't have many downloads. Today they're available as Azure.Storage.Queues as NuGet packages, and as of this writing the version available is 12.1.0 - and downloads are picking up, which is an indicator that it's at least being discovered.

Azure.Storage.Queues NuGet added to a Visual Studio 2019 Console Application.

Since I also want to use Azure Identities to avoid using ClientId/Secret or Connection Strings from code, I'm adding Azure.Identity:

Azure.Identity NuGet added to a Visual Studio 2019 project.

Use the Azure SDK with Managed Identities

As per the documentation of Azure.Identity, to use a Managed Identity from the code base you can use the DefaultAzureCredentials() . See the link for additional options.

Connect using a Managed Identity:

[FunctionName("Function1")]
public static async Task Run([TimerTrigger("0 */5 * * * *", RunOnStartup = true)]TimerInfo myTimer, ILogger log)
{
    // Connect using DefaultAzureCredential with Managed Identities
    var queueClient = new QueueClient(
        new Uri("https://azuresdkstorage123.queue.core.windows.net/queuedemo"),
        new DefaultAzureCredential());

    // Create the queue if it isn't already there.
    await queueClient.CreateAsync();

    // Add a simple message to the queue.
    var addResponse = await queueClient.SendMessageAsync("I am a queue item");
}

If all things are good, you've got yourself a QueueClient object that can with with the storage account queues - and if you look at the Uri I've provided, it directly points to the queue I want to work with queuedemo.

Verifying that it works from the Storage Explorer is easy enough, albeit a simple example:

Azure Storage Account Queue populated from code behind Managed Identity using RBAC

Troubleshooting

During development I've bumped into several things I had to work out. Most of the things work if you follow the steps in this post. However depending on how you assign permissions, and what role you select versus what operations you're trying to perform, you may encounter some errors like the ones listed here.

ErrorCode: 403 - AuthorizationPermissionMismatch

Depending on how you assign permissions to your identity, you may end up with an AuthorizationPermissionMismatch error. This is usually happening when a request cannot be completed due to insufficient permissions for the given operation.

Assigning "Owner" on the storage account doesn't implicitly grant the identity permissions to work with the services within, so we need to explicitly grant RBAC permissions where applicable.

In my code example for this post, I am working with queues - read and write. Hence, I need to ensure that I can assign that RBAC permission to my identity.

  • Storage Queue Data Contributor

For your specific scenario, just ensure that you target the correct permissions/roles and know that "Owner" doesn't automatically enable all operations.

Summary

In this post we took at look at how to use the new Azure SDK libraries with .NET Core, and how we could utilize Azure Managed Identities to access resources from code, rather than using ClientId/Secret or connection strings.

Often I see, and I've done that myself as well, that you'd use Managed Identity to access the Key Vault, but inside the key vault you have the full-control connection strings to the Storage Accounts and other Azure resources. Instead, I try to design it in a way where I don't need to access a connection string at all, and instead solely rely on identity and RBAC, when applicable.

I wanted to focus specifically on Managed Identity to stay on path with a passwordless approach to development, but there's a growing pile of resources available for these new SDKs and there's already examples for how to use the actual code logic. Below are some additional resources.

Azure SDK links

Managed Identity links