If we want to access protected resources from our apps, we usually have to ship a key and secret in our app. This traditionally meant registering an application/service principal in Azure AD, getting an id + secret, then granting permissions to that principal in things like Key Vault. At runtime, our code would use that ID + secret to authenticate to AAD and get an access token to use to connect to the other service.
Operationally, this is pretty inconvenient — now I have app registrations and secrets that all need to be managed — secret lifecycles in particular. Plus I have to ship those secrets in config or appsettings and go to lengths to secure those secrets in whatever environment I’m running in.
The docs cover a lot of frequent use cases — accessing Azure Storage, SQL DB, Data Lake Store, Resource Manager, etc, but don’t address using this with our own apps. As we continue down the path of broad, distributed suites of services that all need to communicate between each other, there’s a huge advantage in having the identity of my apps being managed by the platform instead of relying on writing more automation, alerting or human processes.
I’m using Azure Functions, but this will work in full App Service, as well as Azure VMs and VM Scale Sets. It’s actually pretty simple. Here’s our simple landscape:
- Function app 1: msi-func, MSI-enabled App Service
- Function app 2: aad-func, Azure AD registered application
This represents a typical API — just an AAD-protected service. If you use the built-in Authentication/Authorization functionality of App Service for your function, you get the added benefit of App Service validating the
Authorization header for you.
Next we’ll go create an
appRole in our app registration. This is effectively an Application scope we can assign to our MSI. You’ll find the app’s registration in AAD like you would normally, as the ‘Express’ management mode in App Service just automatically creates you an app registration.
Now we’ll give our function a little bit of code — nothing too exciting here.
In our MSI function, we want to enable MSI first.
Next we’ll add a bit of code. A bit more than our other one, but still not much.
We’ll need to add a package.json too, to pull in the magic assembly that handles asking the local MSI endpoint for a token:
Or call the endpoint manually with
HttpClient or whatever in your language of choice.
Lastly, let’s get our app added to the appRole we created earlier. This is optional, so if you don’t have permissions in your directory, it’s ok to skip.
Crack open Azure AD Powershell v2
We’re ready! Go back to your MSI function and click Run — you should get a set of claims returned in the response. Let’s take a look:
I removed a few for brevity, but here are some important ones:
- aud(ience): this is our AAD app’s ID, so this token is valid for our app
- iss(uer): this was issued by our AAD tenant, so we can trust the source
- appid: this is the ID of the application that requested the token, in our case it matches our MSI’s app ID (not object ID)
- identityprovider: also our tenant
- roles: here’s our role — DoAll
- nameidentifier: the MSI app’s object ID (not app ID)
- tenantid: our tenant again, just in case
And all without shipping a secret in our code! Try out MSI today.