8 min read

Azure Managed Identity : Demystified

Azure Managed Identity : Demystified
Azure Managed Identity

In simple words, a managed identity in Azure is a great feature which can must be used to shift the credentials management to infra. Confused? Let's try to understand it bit by bit.

To better grasp it's concept, it is paramount to recognize some challenges first.

We will take a simple .net core application example & try to understand the challenges/resolutions with following approaches -

  • Simple approach
  • Azure Key Vault approach
  • Managed Identity approach
💡
If you are not familiar with Azure Key Vault then I will highly recommend to understand it first by visiting here.

Let's suppose there is a .NET Core web application, say A. It is hosted on Azure App Service & uses a third party(or internal) microservice, say B, for executing some functionality. In normal scenarios, the overly high level communication between A & B will happen like this -

  • A calls B with a fixed ClientID & Client Secret for authentication purpose.
  • B checks if the ClientID & Client Secret sent are valid or not.
  • If valid, B returns the required response.
  • If not valid, B returns unauthorized response.

The application A keeps the ClientID & Secret in appsettings.json file like this -

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "ClientID": "TestClientID",
  "ClientSecret": "TestClientSecret",
  "AllowedHosts": "*"
}

and reads them in the controller something like below -

public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
        private readonly IConfiguration _configuration;

        public HomeController(ILogger<HomeController> logger, IConfiguration                                  configuration)
        {
            _logger = logger;
            _configuration = configuration;
        }
        [HttpGet]
        public IActionResult Index()
        {
            var ClientID = _configuration["ClientID"].ToString();
            var ClientSecret = _configuration["ClientSecret"].ToString();
           ..............................................................
           ..............................................................
           return View();
        }

Now you would ask what is the issue in that, it seems to be pretty fine.

No, its not!

If you look closely, you would realize that the credentials for accessing service B is stored inside code i.e. inside application A. So what? You would again ask. Well, below are some of the challenges in this approach -

  • The credentials to access the service B are stored in clear text. Anybody who has access to appsettings.json file has access to the credentials which is a big security risk.
  • The onus of managing the credentials comes on developer's shoulders.
  • If the credentials are refreshed, then the exercise of changing them in appsettings will need to be executed every time.

But we can use environment variables here! You would argue. This way the credentials are not stored inside code but separately on Azure App Service end. Yes, we definitely can use this approach. But the credentials will still be in clear text & anybody who has access to Azure App Service has access to the credentials.

So is there anything we can do to mitigate this issue?

Yes, we can!

We can use Azure Key Vault. It's an Azure Service specifically designed for storing & managing critical credentials in a highly secured way.

Let's use an Azure Key Vault in application A. For using it, we will need 2 azure resources -

  • An Azure Key Vault
  • Azure Active Directory App Registration

Let's create a Key Vault first.

Login to azure portal & search for key vault -

Click on Key vaults & create a new one. Enter all the required information.

Click Review + create. Once created, open it, click Secrets & then Generate/Import

Create a secret ClientID with value "TestClientID" as below -

Create one more secret in the same way with name ClientSecret. Now we have created the same 2 secrets in key vault which were kept in appsettings.json file in our example of application A. Now to use these values from inside our code we will need an authenticated mechanism to connect to the Key Vault. And for that, we will have to register an app in Azure Active Directory. Let's do that.

Search Azure Active Directory. Open & click on App registrations and then New registration -

Register an application.

Remember that the name you give here is just for referencing it in Azure AD & this is not the Azure App Service name. Many folks sometimes do this mistake(at least I did when I first tried it 🙂) so thought to mention it. Click Register.

Now once this app is registered, we will need a TenantID, ClientID & ClientSecret to connect to this azure ad app. Notice that this clientid & secret is different from the one which we created on Azure Key Vault earlier as below -

  • The previous clientid & clientsecret we created in key vault are the credentials for connecting to service B.
  • The current clientid & secret which we have to create are the credentials for connecting to the app we just created/registered in Azure AD.

I hope the difference should be clear now.

You can see your registered app in App registrations section -

Now click on the app & go to Overview & copy the ClientID & TenantID in a notepad.

Now that we have ClientID, we will need ClientSecret as well. Let's create that one also.

Click on Certificates & secrets, and then click New client secret -

Create the secret. Once created, copy it & save in a notepad.

We have created an app in azure ad now lets give access rights to this app in the key vault we created.

Go to the key vault we created earlier, click on Access policies & then click on Create.

Set permissions in Secret permissions section.

Click Next & search for the previously created azure ad app.

Select this & click Next, and then create it. Once created it will show up in access polices like this -

Now the azure ad app has access to the newly created key vault. Let's modify our code of application A.

Open appsettings.json file & paste following code -

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "KeyVault": {
    "Vault": "aad-keyvault-app-thetruecode",
    "TenantID": "adae09c6-f4ad-4a93-bd40-571b0e36d4a5",
    "ClientID": "b1009f89-ef7a-44ae-809e-bc1e0025cfc4",
    "ClientSecret": "h_f8Q~Yk76xH8lT3TU7rjRLxNvvYS~kVtyegEccT"
  },
  "AllowedHosts": "*"
}

Open Program.cs & paste following code -

builder.Configuration.AddAzureKeyVault
    (new Uri($"https://{builder.Configuration["KeyVault:Vault"]}.vault.azure.net/")
    , new ClientSecretCredential(builder.Configuration["KeyVault:TenantID"]
    , builder.Configuration["KeyVault:ClientID"], builder.Configuration["KeyVault:ClientSecret"]));

Now in the controller, the same code which we have written will fetch the values of ClientID & ClientSecret to connect to service B. Hurray!

To summarize here is the high level flow -

  • Application A with azure ad app credentials will connect to the azure ad app
  • Azure ad app will connect to the key vault. It should be able to connect easily as per the access policy.

Through the use of Key Vault, we have made sure that the critical credentials are stored in highly secured way & if anybody has access to azure app service of A, he still won't be able to see those credentials. What's more that we can store other critical credentials as well in azure key vault at a single place resulting better security & management of credentials. Problem solved! Or is it?

If you look closely, the challenge we faced earlier is still there albeit in a different way as below -

We have stored the critical credentials for service B in azure key vault but the credentials for connecting to azure key vault are still stored in appsettings.json file. That means one set of creds have been shifted but another set of creds made their way into our code! The same problem still exists!

Now what? We are back to square one. Can we do something more to get rid of this issue?

Enter Managed Identity!

Yes. Azure Managed Identity is the missing piece of this puzzle which will resolve this predicament. Let's see how.

💡
Remove the azure ad app related changes from the code. In managed identity approach, there is no need of any azure ad app registration process.

Go to the azure app service of application A & click Identity. You will see System assigned & User assigned tabs. Click System assigned, then click On Status & copy Principal ID.

And what does it do? Actually managed identity is nothing but a layer above Azure AD itself. When you choose System assigned & turn on the status, azure assigns a unique identity(principal id) to the resource(here app service), and then registers it with azure ad.

Now we have principal(unique)id of our app service. We have to give access rights to this resource for the key vault in the same way we did before. Go to the key vault and go to access policies. Follow the same process.

Click Next and now search for the copied principal id of app service.

Select & click Next, and then create.

A new access policy has been created.

Now let's modify the code of application A a bit.

Open appsettings.json file & paste following -

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "KeyVault": {
    "Vault": "keyvault-thetruecode"
  },
  "AllowedHosts": "*"
}

You can see that there are no credentials in appsettings.json now! Only the vault name.

Open program.cs file & remove the previously added code

builder.Configuration.AddAzureKeyVault
  (new Uri($"https://{builder.Configuration["KeyVault:Vault"]}.vault.azure.net/")
 , new ClientSecretCredential(builder.Configuration["KeyVault:TenantID"]
    , builder.Configuration["KeyVault:ClientID"], builder.Configuration["KeyVault:ClientSecret"]));

with this one -

builder.Configuration.AddAzureKeyVault
    (new Uri($"https://{builder.Configuration["KeyVault:Vault"]}.vault.azure.net/")
    , new DefaultAzureCredential());

No credentials here as well!

Did you notice that there are no credentials stored in code now. None whatsoever! All the credentials are stored either in key vault(for connecting to service B) or at azure's end through managed identity(for connecting to key vault).

Can you see the power of managed identity now? With the use of this feature, the storing & management of credentials is shifted completely to azure infra end, and the dev teams do not need to worry about managing them now. Moreover, they can now be controlled effectively by creating different access policies resulting in greater protection against security risks.

So a managed identity, in overly simple words, is -

a mechanism through which one azure resource can connect to another azure resource securely with no need of storing the required credentials inside the code.

I hope the article helped in clearing some of your doubts around azure managed identity concepts. Please post your honest feedback in the comments section.