As you might know, Microsoft is working hard to create brand new SDKs for most of its services. The goal of this effort is to simplify development, increase productivity, introduce uniformization across SDKs and to focus hard on documentation and samples. An overview of all .NET SDKs can be found here, remark that many are still in preview. This blog outlines some examples of how the new Azure SDKs simplify the development with Managed Identities.
Get your credentials
The SDK has to retrieve the credentials that will be used to authenticate against Azure AD. There are many ways to achieve this, these are the most common ones:
DefaultAzureCredential
The DefaultAzureCredential tries different authentication methods in a cascading way. The first authentication method that provides valid authentication information, will be executed. The aim is that this single credential gets resolved in both your local development environment and Azure. The following authentication types are evaluated, in this particular order (preview):
- EnvironmentCredential: looks for environment variables, such as AZURE_CLIENT_ID and AZURE_CLIENT_SECRET to authenticate
- ManagedIdentityCredential: attempts to authenticate using a managed identity that is assigned to the deployment environment (if any)
- SharedTokenCacheCredential: tries to authenticate using tokens in the local cache shared between Microsoft applications
- VisualStudioCredential: tries authentication to Azure AD via the logged-in user in Visual Studio
- VisualStudioCodeCredential: tries authentication to Azure AD via the logged-in user in Visual Studio Code
- AzureCliCredential: attempts to authenticate by using the current user context of Azure CLI
- InteractiveBrowserCredential: launches the browser to interactively authenticate a user
You can obtain your AD credential like this:
using Azure.Identity; //v1.2.0-preview.4 | |
var credential = new DefaultAzureCredential(); |
While this approach is very simple, it has some downsides:
- The cascade of authentication attempts can take too much time
- It is not always clear what authentication method got executed
If you want to be in charge of what authentication methods get evaluated, you have the use the DefaultAzureCredentialOptions.
using Azure.Identity; //v1.2.0-preview.4 | |
var options = new DefaultAzureCredentialOptions | |
{ | |
ExcludeEnvironmentCredential = true, | |
ExcludeManagedIdentityCredential = false, | |
ExcludeSharedTokenCacheCredential = true, | |
ExcludeVisualStudioCredential = true, | |
ExcludeVisualStudioCodeCredential = true, | |
ExcludeAzureCliCredential = false, | |
ExcludeInteractiveBrowserCredential = true | |
}; | |
var credential = new DefaultAzureCredential(options); |
ChainedTokenCredential
My favorite way is using the ChainedTokenCredential, where you can define your preferred authentication methods and their respective order of execution. This is a simple example in which I use Managed Identity when deployed in Azure and Azure CLI for local development purposes.
using Azure.Identity; //v1.2.0-preview.4 | |
var credential = new ChainedTokenCredential( | |
new ManagedIdentityCredential(), | |
new AzureCliCredential() | |
); |
Use your credentials
Regardless of the environment our application is deployed (local or Azure), we have an easy way to obtain our credentials. Now we have to use them in order to access Azure AD-protected resources. Let’s have a look at some common examples:
Azure Key Vault
This snippet shows how to retrieve a secret from Azure Key Vault, using the credential that we retrieved earlier. Because this credential is used in the context of the Azure Key Vault secret client, the resulting token will be addressed to the https://vault.azure.net audience.
using Azure.Security.KeyVault.Secrets; //v4.0.3 | |
var secretClient = new SecretClient( | |
new Uri($"https://{_keyVaultName}.vault.azure.net/"), | |
credential | |
); | |
var secret = await secretClient.GetSecretAsync("Test"); |
Azure Service Bus
This snippet shows how to send a message to Azure Service Bus, using the credential that we retrieved earlier. Because this credential is used in the context of the Azure Service Bus client, the resulting token will be addressed to the https://servicebus.azure.net audience.
using Azure.Messaging.ServiceBus; //v7.0.0-preview.4 | |
var serviceBusClient = new ServiceBusClient( | |
$"{_serviceBusName}.servicebus.windows.net", | |
credential | |
); | |
var queueSendClient = serviceBusClient.CreateSender(_queueName); | |
var message = new ServiceBusMessage(File.ReadAllText(_testFilePath)); | |
await queueSendClient.SendMessageAsync(message); |
Azure Blob Storage
This snippet shows how to upload a file to Azure Blob Storage, using the credential that we retrieved earlier. Because this credential is used in the context of the Azure Blob Storage client, the resulting token will be addressed to the https://storage.azure.com audience.
using Azure.Storage.Blobs; //v12.4.3 | |
var blobClient = new BlobClient( | |
new Uri($”https://{_storageAccountName}.blob.core.windows.net/{_blobContainerName}/msi-{Guid.NewGuid()}.txt”), | |
credential | |
); | |
var testFileStream = File.OpenRead(_testFilePath); | |
await blobClient.UploadAsync(testFileStream, true); | |
testFileStream.Close(); |
Azure SQL Database
This snippet shows how to insert a row in an Azure SQL Database, using the credential that we retrieved earlier. Because the System.Data.SqlClient is not an Azure-specific library, we have to explicitly get the access token (JWT) for the https://database.windows.net resource. This access token can be passed to the SqlConnection.
using System.Data.SqlClient; //v4.8.1 | |
var accessToken = credential.GetToken(new TokenRequestContext(new[] { "https://database.windows.net/.default"})).Token; | |
using (var sqlConnection = new SqlConnection($"Server=tcp:{_sqlServerName},1433;Database={_sqlDatabaseName}")) | |
{ | |
sqlConnection.AccessToken = accessToken | |
using (var sqlCommand = new SqlCommand()) | |
{ | |
sqlCommand.CommandType = System.Data.CommandType.Text; | |
sqlCommand.CommandText = "INSERT INTO dbo.Tests VALUES(@Input)"; | |
sqlCommand.Parameters.AddWithValue("@Input", File.ReadAllText(_testFilePath)); | |
sqlCommand.Connection = sqlConnection; | |
sqlConnection.Open(); | |
await sqlCommand.ExecuteNonQueryAsync(); | |
} | |
} |
Conclusion
As you can see, the new Azure SDKs provide seamless support for Azure Managed Identity, all in a consist manner. You have a lot of control on how you want to deal with the authentication part for local development, which is really great! It was a very pleasant experience to discover this smooth security integration!
Happy coding!
Toon