Storing and using secrets in Azure
Most applications need access to secret information in order to function: it could be an API key, database credentials, or something else. In this post, we’ll create a simple service that will compare the temperatures in Seattle and Paris using the OpenWeatherMap API, for which we’ll need a secret API key. I’ll walk you through the usage of Azure’s Key Vault for storing the key, then I’ll show how to retrieve and use it in a simple Azure function.
In order to be able to follow along, you’ll need an Azure subscription. It’s very easy to get a trial subscription, and get started for free.
Where to store secrets?
There are of course many different places where people store such secrets. From worst to best, one could think of the following: in your source code repository on GitHub (of course, nobody should ever do that), in configuration files (encrypted or not), in environment variables, or in specialized secret vaults.
Which one you choose depends on the level of security your application requires. Oftentimes, storing an API key in an environment variable will be adequate (what is never adequate is hard-coded values in code or configuration files checked into source control). If you require a higher level of security, however, you’ll need a specialized vault such as Azure Key Vault.
Azure Key Vault is a service that stores and retrieves secrets in a secure fashion. Once stored, your secrets can only be accessed by applications you authorize, and only on an encrypted channel. Each secret can be managed in a single secure place, while multiple applications can use it.
Setting up Key Vault
First, we’re going to set-up Key Vault. This requires a few steps, but only steps 4 and 5 have to be repeated for new secrets, the others being the one-time building of the vault.
Open the Azure portal and click on Resource groups. Choose an existing group, or create a new one. This is a matter of preference, but is otherwise inconsequential from a security standpoint. For this tutorial, we’ll create a new one called “sample-weather-group”. After clicking the Create button, you may have to wait a few seconds and refresh the list of resource groups.
Select the newly created group, then click the Add button over its property page. Enter “Key Vault” in the search box, select the Key Vault service, then click Create.
Enter “sample-weather-vault” as the name of the new vault. Select the right subscription and location, and leave the “sample-weather-group” resource group selected. Click Create.
If you refresh the resource group property page, you’ll see the new vault appear. We’re now ready to add a key to it. Get an API key from OpenWeatherMaps. Select the vault in the list of resources under the resource group, then select Secrets. You can now click Add to add a new secret. Under Upload options, select Manual. Enter “open-weather-map-key” as the name of the secret, and paste the API key from OpenWeatherMaps into the value field. Click Create.
We will later need the URL for the secret we just created. This can be found under Secret Identifier on the property page of the current version of the secret, which can be reached by navigating to Secrets under the key vault, then clicking the secret, and then its latest version.
And this is it for now for Key Vault: we now have a vault, containing our secret. Next, we’ll need to setup access so that we can securely retrieve the key from our application.
Preparing Active Directory authentication
The application will need to securely connect to the vault, for which it will have to prove its identity, using some form of master secret. This is similar to the master password that a password vault uses, and makes sure the identity of our application can be managed independently from the secret, which may be shared with more than one application. We’ll use Active Directory for this.
In this post, we’ll authenticate using a secret key, but it’s important to note that it is possible to use a certificate instead for added security. Please refer to Authenticate with a Certificate instead of a Client Secret for more information.
To access Active Directory, in the Azure portal, select More Services and choose Azure Active Directory (currently in preview). In the next menu that will appear, click App registrations. Click the Add button above the list of applications. You’ll be asked for a name for the application. We’ll choose “sample-weather-ad”. For the application type, leave the default Web app / API selected. We also have to provide a sign-on URL. For our purposes, this doesn’t need to actually exist, but only to be unique. Click the Create button.
Now that the application has been created, select it in the application list, so that you can see your application’s security principal ID, which can be found under Managed Application In Local Directory in the application’s property page. This principal ID will represent the authenticated identity of our application. You’ll need that and a key.
Currently, this principal ID does not get automatically generated until the Active Directory application is logged into for the first time. This is a temporary issue that is being looked into. In the meantime, it can be worked around by browsing to
<oauth 2.0 authorization endpoint URL>?client_id=<application_id>, where
<oauth 2.0 authorization endpoint URL>can be found by clicking the Endpoints button above the list of registered apps, and
<application_id>can be found under the property page for the application. For example, if your authorization endpoint URL is
https://login.windows.net/00000000-0000-0000-0000-000000000000/oauth2/authorize, and your application id is
11111111-1111-1111-1111-111111111111, the URL to browse to would be
Navigating to the URL we composed will require you to authenticate with the credentials of a user that has admin rights on the subscription, and then it will yield an error page that can be safely ignored. If all went well, you should now be able to see your application’s principal ID in the property page.
We can now proceed to create credentials that our Azure Function will be able to use to authenticate to Active Directory as the application we created earlier. Click on All settings, then select Keys. We can add a new key by entering a description, selecting an expiration, and hitting the Save button. If you do choose to have the key expire, you should also take the time to create a reminder on the schedule of the team in charge of managing this application to renew it.
Note that using a different key and id for each application that will use the secrets makes it possible to revoke access to the whole vault for a specific application in one operation.
Once you’ve saved, the key can be viewed and copied to a safe place. Do it now, because this is the last time the Azure portal is going to show it.
We’ll also need the URL of the Active Directory endpoint. This is not the URL that we manually entered earlier when we created the AD application. It can be obtained by clicking the Endpoints button above the list of applications.
The URL we want to copy for later use is the one under OAuth 2.0 Token Endpoint.
We’re now ready to authorize the application to access the vault and get values out of it. Navigate back to the key vault’s property page, and select the vault we created earlier, then Access policies. Click Add new, then Select principal. Paste the principal ID into the text box. After a few seconds, the principal should appear checked. Click the Select button. Then click on Secret permissions and check Get, then click OK, then Save.
We now have a set of Active Directory credentials that our Azure Function will be able to use, that will enable read access to the secrets in the key vault. Our last remaining step is to create the actual code.
Creating the Azure function
We’re going to use Azure Functions to implement the actual service, because it’s the easiest way to write code on Azure, but roughly the same steps would apply to any other kind of application.
From the resource group’s property page, click Add, and type “Function App” in the filter box. Select Function App, then click Create. Name your new function app “sample-weather”. Select the relevant subscription, resource group, plan, and location.
Refresh the resource group’s property page, then select the new “sample-weather” app service. Create a new function under the function app. Name it “ParisSeattleWeatherComparison” and choose the empty C# template:
Now we have an empty function. Let’s add some code.
Click View Files under the code editor. This shows the list of files in the function’s directory. Click the + icon to add a new file, and name it “weather.csx”. Enter the following code in that file:
Those classes will help deserialize the response from the weather server. The structure of the types
WeatherDatareflects the schema of the JSON documents that the weather service will return. It does not, nor needs to reflect the entirety of the schema returned by the API: the
Microsoft.AspNet.WebApi.Clientlibrary will figure out which parts to deserialize and how to map them onto the provided object model.
Enter the following code as the body of the function:
We also need to define the bindings for the function. Open
function.jsonand enter the following as its content.
This tells Azure Functions what objects to inject into the function.
In the code above, you’ll notice that we’re reading the AD URL, client ID and key from configuration, because of course we haven’t done all this to end up storing secrets in code. For the code to function, we’ll have to enter that information into the function’s Azure configuration. This can be done by clicking Function app settings on the bottom-left of the function editing screen.
In the screen this brings up, you’ll want to select the Configure app settings option. Once there, you’ll see a few general settings, but what we’re interested in is the table of custom App settings. We’ll add new key-value pairs in there with the names we used in the code:
- “WeatherADClientID” with the Active Directory application ID we got in the previous section.
- “WeatherADKey” with the Active Directory application key.
- “WeatherKeyUrl” with the URL for the secret API key we stored in the vault earlier.
Don’t forget to hit Save on top of the panel.
Go back to the function editor and add a
project.jsonfile to import the NuGet packages we need.
Enter the following code into the file.
And this is it. Once you’ve saved all the files, you should see the trace of the package restoration and compilation of the function in the logs window. After that’s completed, you should be able to click the Run button and figure out where the weather is nicest, between Paris and Seattle.
We now have a function that connects to an Azure Key Vault using Azure Active Directory authentication, and then uses a secret stored in the vault to query a remote service.
Wait, what about .NET Core?
Before I conclude, and now that we’ve made this work in Azure Functions, how about re-using the knowledge we’ve gained in a different kind of application, such as a .NET Core console application? We actually wouldn’t have to change much. First, we’d need to check our dependencies to make sure that everything we need is available on .NET Core, then we’d have to modify the code so that it reads configuration using the new
ConfigurationBuilder. Finally, we’d just change
req.CreateResponse(HttpStatusCode.OK, $"..."); into simple
Here’s what the code looks like once ported to a .NET Core console app.
And here’s the
project.json that enables it to restore the right packages.
The weather data model remains unchanged from the Azure Functions version.
And that’s it, this console application will, like the Azure Function, authenticate to Active Directory, get the API key from Key Vault, and then query the API and tell you about the weather.
Azure services are evolving constantly, and so is .NET support for them. While I was writing this article, I was able to transfer some of the steps that previously required command-line operations, to using the portal, making them a lot easier, as well as more discoverable. One of the things to look forward to is a new ASP.NET configuration provider that will enable developers to get rid of much of the code I had to write to access the key vault.
Please let us know if tutorials like these are helpful. Happy programming!