Exploring Azure App Service Managed identity

Olav Nybø

Quite often we want to give an app service access to resources such as a database, a keyvault or a service bus. We used to do this by configuring the app service with secrets that enabled the application to access these protected resources.

However, passwords and secrets are always a hassle. The fewer the better in my opinion. Managed identity for app service is a feature that almost magically do away with the need for secrets in the application.

With managed identity we can treat the application in much the same way as we do with users. We can give the application access to resources, just like we would give users or groups access.

But how do this magic work?

Well, first lets see how to enable managed identity.

Enable Managed identity

This definitely is a job for Azure CLI:
az webapp identity assign -g my-resource-group -n my-super-app

The output will be something like this:

{
  "identityIds": null,
  "principalId": "3c0ce698-24bf-43f2-bfc8-b25df0e0625a",
  "tenantId": "<tenantId GUID>",
  "type": "SystemAssigned"
}

Ok, if you absolutely insist on doing it the hard way, you can do the same in the portal as well.

This will enable System assigned identity for your app service.
What this actually means is that a service principal with the given principalId (called Object ID in the portal) has been created for you. The name of the service principal is the same as the name of your app service.

To see the details of the service principal that has been created you can do the following:
az ad sp show --id <principalId or Object ID here>

Giving access

As we now have a service principal we can give this service principal access to the resources we want allow the app service access to.

Giving the access to a resource such as a service bus is as simple as adding a few switches to the command we used to enable managed identity.
These are --role and --scope.
Role is any of the Azure roles. E.g. reader or contributor are valid roles.
Scope is the id of the resource you want to give access to.

Example - get the id of a servicebus namespace

az servicebus namespace show -g <resource group> -n <namespace name> --query id

should display something like this:
/subscriptions/<subscription guid>/resourceGroups/my-resource-group/providers/Microsoft.ServiceBus/namespaces/my-service-bus

Azure CLI command to give read access to the my-super-app app service

az webapp identity assign -g my-resource-group -n my-super-app --role read --scope /subscriptions/<subscription guid>/resourceGroups/my-resource-group/providers/Microsoft.ServiceBus/namespaces/my-service-bus

Output:

{
  "identityIds": null,
  "principalId": "3c0ce698-24bf-43f2-bfc8-b25df0e0625a",
  "tenantId": "<tenantId GUID>",
  "type": "SystemAssigned"
}

The magic

Now that we have enabled managed identity and given the app access to a resource we are good to go?
No not quite yet. All resources that support Azure AD authentication, and thus works with managed identity use oauth access tokens for authorization.
This means we first need to get a token before we can access resources.

This is where the magic starts. When managed identity is enabled on a app service a local http endpoint that can provide access tokens will be available on the app service (see figure below).
This local http endpoint can only be reached from code running on the app service. This means that only code running on our web site can get these access tokens.

+----------------------------------+
|                                  |
| +-----------+                    |
| |           |                    |
| |  Managed  |<---------+         |
| |  Identity |          |         |
| |           |          |         |
| +-----------+     +----|-------+ |
|                   |            | |
|                   |            | |
|                   |    App     | |
|                   |            | |
|                   |            | |
|                   +------------+ |
+----------------------------------+

When managed identity is enabled two environment variables will be available on the app service.

  • MSI_ENDPOINT - the URL to the local token service.
  • MSI_SECRET - a secret that needs to be passed in the header of a GET request which should be sent to the MSI_ENDPOINT URL

We can check this from the kudu Powershell command prompt.
Navigate to https://<app name>.scm.azurewebsites.net/DebugConsole/?shell=powershell
In my case this would be:
https://my-super-app.scm.azurewebsites.net/DebugConsole/?shell=powershell

On my test site I get the following results, you should get similar values:

PS D:\home> $env:MSI_ENDPOINT
$env:MSI_ENDPOINT
http://127.0.0.1:41641/MSI/token/
PS D:\home> $env:MSI_SECRET
$env:MSI_SECRET
A133C1AB48A24A4F88D7DC99E2B5D6FE

We can test the endpoint from the Powershell prompt like this:

Invoke-RestMethod -Method Get -Headers @{"Secret"="$env:MSI_SECRET"} -Uri ($env:MSI_ENDPOINT + "?resource=https://management.azure.com&api-version=2017-09-01")

This will display something like this

access_token                                                                   
------------                                                                   
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkhCeGw5bUFlNmd4YXZDa2NvT1UyVEhz...

Cool!
GET http://127.0.0.1:41641/MSI/token/?resource=https://management.azure.com&api-version=2017-09-01
Secret: A133C1AB48A.....

Returns an access token that can be used when accessing Azure resources!

Resources

Ok, so we just saw that enabling managed identity starts a local web endpoint on our app service that will return access tokens if we pass it a secret, api-version and some resource URL.
The api-version should always be 2017-09-01 and the Secret we got from an environment variable, but what should we put into that resource parameter?
It turns out that there is a list here
I did not quite understand the information on the at the mentioned link at first, but it seems to be something like the following:

Azure Resource Manager - https://management.azure.com/

Use this when you want to manage resources. I.e. create, delete, update Azure resources. This is when you would do stuff programmatically that you would otherwise do using Azure CLI or in the portal.

Resources supporting managed identity

If you want to interact with one of the APIs for a specific type of service use the following URIs for the resource parameter.
Keyvault: https://vault.azure.net
Datalake: https://datalake.azure.net/
Azure SQL: https://database.windows.net/
Eventhub: https://eventhubs.azure.net
Service Bus: https://servicebus.azure.net
Storage blobs and queues: https://storage.azure.com/

Next step

Now we know how to enable managed identity and how it works, and we know how to get tokens from the powershell console in an app service. Great!
I suppose a more common scenario is to get this token into a running .net core app. And how should we use this token when accessing Azure resources? Very soon I will do a follow post and show how to get tokens using Azure.Services.AppAuthentication