Managed Identity – Part II
By rickvdbosch
- 4 minutes read - 752 wordsThis post is part of a series on Managed Identity. For an introduction, see Managed Identity – Part I. Stay tuned for future posts.
Introduction
Elaborating on Part I, this blog post will show you how to connect your application to different types of Azure resources using Managed Identity.
To use the Managed Identity to actually connect to Azure Resources, you’re going to need the NuGet package Microsoft.Azure.Services.AppAuthentication. This package enables a service to authenticate to Azure services using the developer’s Azure Active Directory/ Microsoft account during development, and authenticate as itself (using OAuth 2.0 Client Credentials flow) when deployed to Azure.
Key Vault
The Azure resource that is easiest to connect to is probably Key Vault. There are two ways of connecting: through code, or by using Key Vault references (currently in preview).
Key Vault references
With these Key Vault references you simply put a reference to a secret in Key Vault in your Application Settings, like @Microsoft.KeyVault({referenceString})
where {referenceString}
is either a full URI to a secret including its version, or a set of values in the form VaultName=<em>vaultName</em>;SecretName=<em>secretName</em>;SecretVersion=<em>secretVersion</em>
.
Accessing Key Vault from code
As already mentioned a couple of times, there’s an awesome NuGet package that helps you connect using Managed Identity. Within this package is the AzureServiceTokenProvider
which has a KeyVaultTokenCallback
. This is, not surprisingly, a callback that you can pass into the Key Vault client to create an instance that uses Managed Identity to connect to the Key Vault. In code, this looks something like this:
private const string SECRET_NAME = "<YOUR_SECRET'S_NAME>";
private const string VAULT_URL = "https://<YOUR_VAULT'S_NAME>.vault.azure.net/";
var tokenProvider = new AzureServiceTokenProvider();
using (var kvc = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(tokenProvider.KeyVaultTokenCallback)))
{
var x = await kvc.GetSecretAsync(VAULT_URL, SECRET_NAME);
}
SQL
When you want to connect to Azure SQL, there’s a slightly different approach you need to take. The SqlConnection
class has a property called AccessToken
. And of course, the AzureServiceTokenProvider
provides tokens 😁.
When asking for a token, the provider needs to know what resource you’re asking a token for. Here’s an example (notice the fact there’s no username or password in the connection string!):
private const string RESOURCE = "https://database.windows.net/";
private const string CONNECTIONSTRING = "Server=tcp:<YOUR_SERVER_NAME>.database.windows.net,1433;Initial Catalog=<YOUR_DATABASE_NAME>;";
using (var connection = new SqlConnection(CONNECTIONSTRING))
{
var tokenProvider = new AzureServiceTokenProvider();
connection.AccessToken = await tokenProvider.GetAccessTokenAsync(RESOURCE);
await connection.OpenAsync();
// Do your magic to the database!
}
Service Bus
As with SQL, for Service Bus you need to get a token for a specific resource too. And for Service Bus, this resource is https://servicebus.azure.net/
. Using the token is just a bit different yet again.
private const string NAMESPACE = "https://<YOUR_NAMESPACE>.servicebus.windows.net";
private const string QUEUE_NAME = "<YOUR_QUEUE_NAME>";
var tokenProvider = new ServiceBusTokenProvider();
var queueClient = new QueueClient(NAMESPACE, QUEUE_NAME, tokenProvider);
Azure Storage
Connecting to Azure Storage using Managed Identity has the most elaborate example code. First of all you need to create a StorageCredential
that you pass into for instance the CloudBlobClient
. That credential takes a TokenCredential
instance which needs, among other things, a method that renews a token. And when renewing a token, you need to specify the resource you need the token for.
In code, it looks something like this:
private const string BLOB_URI = "<COMPLETE_URI_TO_A_BLOB>";
var azureServiceTokenProvider = new AzureServiceTokenProvider();
// Get the initial access token and the interval at which to refresh it.
var tokenAndFrequency = await TokenRenewerAsync(azureServiceTokenProvider, CancellationToken.None);
// Create a TokenCredential which can be used to pass into the StorageCredentials constructor.
var tokenCredential =
new TokenCredential(tokenAndFrequency.Token,
TokenRenewerAsync,
azureServiceTokenProvider,
tokenAndFrequency.Frequency.Value);
var storageCredentials = new StorageCredentials(tokenCredential);
var blob = new CloudBlockBlob(new Uri(BLOB_URI), storageCredentials);
var content = await blob.DownloadTextAsync();
With the TokenRenewerAsync
looking a bit like this:
private static async Task<NewTokenAndFrequency> TokenRenewerAsync(Object state, CancellationToken cancellationToken)
{
// Note: you can also specify the root URI for your storage account.
const string STORAGE_RESOURCE = "https://storage.azure.com/";
var authResult = await ((AzureServiceTokenProvider)state).GetAuthenticationResultAsync(STORAGE_RESOURCE);
// Renew the token 5 minutes before it expires.
var next = (authResult.ExpiresOn - DateTimeOffset.UtcNow) - TimeSpan.FromMinutes(5);
if (next.Ticks < 0)
{
next = default(TimeSpan);
Console.WriteLine("Renewing token...");
}
// Return the new token and the next refresh time.
return new NewTokenAndFrequency(authResult.AccessToken, next);
}
Conclusion
Managed Identities for Azure Resources are awesome. They provide a great level of security. Using it, however, might take some getting used to. Especially since there are so many different ways necessary to get a token for different types of resources.
Resources
You can find all of the examples that I’ve used in this article on GitHub: rickvdbosch/managed-identity-example. This repo will be updated regularly with new examples.
If you have any questions, don’t hesitate to contact me.