Ditch Connection Strings, Embrace Secure Azure Access

Introduction

As a developer who is using Azure most of the times, one of the first tasks I am doing when setting up my developer laptop is to make sure the Azure environment considers it as a trusted machine. Doing so enables me to use RBAC permission in Azure without being worried to keep connection string all over my development machine and stop being worried about whether I have accidentally exposed some secrets into an appsettings.json like files and maybe even worse, push them into a repository! 🤷🏻‍♂️

Authenticate your machine

The first step is to download Azure CLI on you machine, depending on your developer environment you could use different approaches! Here you could find how to install it whether you are on Linux, Windows, or MacOS.

To make sure the installation was successful you could run az version, once running it, you should see some output similar to mine, indicating which version of the tool is installed!

The next step is to run az login, if it automatically opened the browser, then follow the steps, if not, copy the URL and you should be able to follow the steps provided but the authentication process. It could differ, based on whether you have been already logged into Azure or not. It will ask you about the user name and password, and if the 2-FA (Two-Factor Authentication) is activated on your account, it will ask for some extra steps to enter or confirm the login attempt.

After a successful login, the output in the console will show you the active subscriptions this user has access to, of which you could then select to be the default one, either by entering the subscription name or the number: Now your could issue commands from the terminal against the selected azure subscription, for instance the next code snippet shows the output of running az group list which lists all the resource groups in the default subscription I chose before:

❯ az group list
[
  {
    "id": "/subscriptions/<subscription-id>/resourceGroups/rg-learning",
    "location": "westeurope",
    "managedBy": null,
    "name": "rg-learning",
    "properties": {
      "provisioningState": "Succeeded"
    },
    "tags": null,
    "type": "Microsoft.Resources/resourceGroups"
  }
]

depending on the permissions this user has, one could definitely issue different commands. But, wait, I am not going to always communicate with my Azure environment via a terminal 🤔 how is this going to help me avoid connection strings on the development machine? Well, let's take a look into that!

Authenticate you Application

Let's say I have an application that wants to connect to Azure Blob Storage, one way is to grab the connection strings from the Azure Portal, select your storage account and navigate to the Access keys section, there you could find the primary and secondary connection strings:

Now, in the code, when I want to use the BlobServiceClient to connect to the Azure Blob Storage, there are several overrides to connect to the service, one of which is providing the complete connection string, which is exactly the one we are trying to avoid (red-arrow), the other one (highlighted in green-yellow) is accepting a TokenCredential and is the one we are interested in:

There are several credential providers that could be used to create a token credential. And to use them one need to add the Azure.Identity NuGet package to the project, at the time of writing this post it is on version 1.13.2.

❯ dotnet add package Azure.Identity --version 1.13.2

After adding the package we could create a secure connection to the service, by just providing the public URI of the service, depending on the service it could have different formats, however, in this example it would be like https://<storage-account-name>.blob.core.windows.net; by using the AzureCliCredential class we could authenticate the application against Azure environment, it uses the exact same user as is connected to Azure through the az login command provided earlier in this post.

var blobServiceClient = new BlobServiceClient(
    new Uri("https://<storage-account-name>.blob.core.windows.net"),
    new AzureCliCredential());

The same approach could be done for any other service that exists on Azure. There is one tiny problem though; 😜 the application can now only authenticate itself, if and only if, there is an azure cli command available on the environment it is running on; this is not the case for every environment! What if we deploy the application to Azure itself, there is no Azure Cli 🤷🏻‍♂️

To overcome this issue, there is another credential provider named DefaultAzureCredential it has a chain of other concrete providers and will check which one of them is available in the running environment, the first one that is available will be used. The list is as the following:

  • EnvironmentCredential
  • WorkloadIdentityCredential
  • ManagedIdentityCredential
  • SharedTokenCacheCredential
  • VisualStudioCredential
  • VisualStudioCodeCredential
  • AzureCliCredential
  • AzurePowerShellCredential
  • AzureDeveloperCliCredential
  • InteractiveBrowserCredential

For instance it is best practice to use ManagedIdentity when running applications inside the Azure environment. By using the DefaultAzureCredential not only we could eliminate the connection string on our local machine, we could eliminate them altogether and rely on more trusted approaches like ManagedIdentity.

Conclusion

Using connection strings to connect to any service might at first sight be the easiest way to go with, however, it comes with challenges of maintaining them and being sure that thy are not accidentally revealed! A better and more secure approach would be to authenticate our (local) environments or machines against Azure, and using the credential classes for managing a TokenCredential for accessing Azure resources.

At the end, thanks for reading, enjoy coding and Dametoon Garm [**]

Resources

Buy Me a Coffee at ko-fi.com

Create Syntax Trees using Roslyn APIs

Sometimes when we want to generate code whether it is source generators or code fixes for a code analyzer, it is required to know how a syntax tree could be created using the Roslyn Compiler API. There are two ways, that we will discuss them in this post.

Test Your Roslyn Code Analyzers

In the previous post we have seen how it is possible to create a diagnostic analyzer using Roslyn APIs. Being a TDD advocate it would be disappointing not to talk about how we could test our code analyzer! So let's take a look into it.

Ensure Uniqueness of Nullable Columns

How do you ensure that duplicate keys are prevented on a column that allows NULL values?

An error has occurred. This application may no longer respond until reloaded. Reload x