Posted in:

Note: this is an updated version of my earlier post Managing Azure Functions Keys to use the new Functions ARM APIs

Azure Functions allows you to protect access to your HTTP triggered functions by means of authorization keys. For each function you can choose an "authorization level":

  • anonymous means no API key is required,
  • function means an API key is required.
    • This can be a function-specific key (each function can have its own collection of keys)
    • Or it can be one of the "host" keys which are keys that can be used for all functions with this authorization level
  • admin means you are required to provide the special "master" host key, which is a single key that can be used to call any function in your function app.

To call a protected function you either provide the key as a query string parameter (in the form code=<API_KEY>) or you can provide it as a HTTP x-functions-key header.

Accessing and managing keys in the portal

The Azure portal makes it nice and simple to discover the values these keys. First of all, if we navigate to any HTTP-triggered function in the portal, you'll see a "Get Function URL" link:

image

When we click it, it constructs the URL we need to call including the code query string parameter. This dialog also lets us access values for both types of key - the "function" keys specific to this function, and the "host" keys that can be used on all functions, including the special "_master" host key. You can read more about these key types here.

image

We can manage the keys for an individual function by heading into the "manage" tab for that function:

image

In here we get the ability to view, renew or revoke each individual function key as well as the host keys. You can create multiple function or host keys, which is great as it allows you to provide separate keys to every client you want to grant access to your function, or to implement key cycling.

image

Using the Functions ARM APIs to manage keys

Although its very convenient to manage keys in the portal, before long you'll probably want to manage these values programatically. One way to do that is with the key management API which is what I showed how to use in the original version of this tutorial.

However, it was quite tricky to use, and recently some new Functions ARM APIs were added that be used to do all the same things, and are easier to use.

Let's see how we can get the values of function keys, as well as generate our own, and update them with new values.

Listing keys for a specific function

To use these APIs you need to know how to generate an ARM resource id for your Function App. This requires the Function App name, the Resource Group name and the Azure subscription id.

Here's how to build the resource id for a Function App, using the Azure CLI's az account show method to look up the subscription id from the subscription name.

$subscriptionName = "My Azure Subscription"
$subscriptionId = az account show -s "$subscriptionName" --query id -o tsv
$resourceGroup = "MyResourceGroup"
$webAppName = "myfunctionapp"
$resourceId = "/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/sites/$webAppName"

Next, we're going to call the listKeys endpoint to list the keys for a function. To start with, I'll show how we can do this with PowerShell's Invoke-RestMethod.

First, we need to get hold of a valid access token to call the ARM APIs, which we can get with Azure CLI's very convenient az account get-access-token. Then we can use that to POST to the listKeys endpoint for the specific function we want.

# get an access token
$accessToken = az account get-access-token --query accessToken -o tsv
$functionName = "MyFunctionName"
$listFunctionKeysUrl = "https://management.azure.com$resourceId/functions/$functionName/listKeys?api-version=2018-02-01"
$functionKeys = Invoke-RestMethod -Method Post -Uri $listFunctionKeysUrl `
    -Headers @{ Authorization="Bearer $accessToken"; "Content-Type"="application/json" }

This will return us a JSON object containing all the keys defined for your function. Typically there will just be one called default, so you can access the value with $functionKeys.default.

There is, however, an easier way for users of the Azure CLI. You can use the new preview az rest command which will get hold of the access token for you. So we can get the keys for a function with a single line:

az rest --method post --uri "https://management.azure.com$resourceId/functions/$functionName/listKeys?api-version=2018-02-01"

Listing host keys

You can also ask for the "host keys", with a similar technique:

az rest --method post --uri "$resourceId/host/default/listKeys?api-version=2018-11-01"

This will return something looking like this (no these aren't my actual keys!):

{
  "functionKeys": {
    "default": "VyEOe9oIHkIz6sj+Are3ffHcP7ptHKAidUAxgDMkdSSaYuGDraek5Q=="
  },
  "masterKey": "N9scClcIIRhRYGJN2JHfJatPbkmjvb08QZNixfwuoJrRIrj12iryKA==",
  "systemKeys": {
    "durabletask_extension": "vlNYs6GwWntJ7GXb9/MJ4A36XzM4a+06GFWZrPUqpQL5EBhYA0cHUg==",
    "eventgrid_extension": "UYe7XF4rpC0yhokdOIWEsMWfWQFlpLtf4usgZJq1tC6RHzr0BUKCrw=="
  }
}

Let me briefly explain what these all are:

  • functionKeys are the "host" keys shown in the portal. They can be used to call any HTTP triggered function. There will be a default key out of the box, and you can add your own.
  • masterKey is a special key (shown as _master in the portal) that can be used to access admin authorization level functions. This key should be guarded very carefully.
  • systemKeys contains keys used by any extensions you have installed. In this Function App I have the Durable Functions and Event Grid extensions installed, and they both add their own keys. It's really useful to access these if you want to call the Durable Functions REST API, or if you want to create a new Event Grid subscription.

By the way, if you're wondering how I knew what values are valid for the api-version (which is required on all REST API calls), I found this helpful article from Tobias Zimmergren, showing how to list the available versions with Azure PowerShell. I was using the newer Az PowerShell module, so I got the list of API versions like this:

((Get-AzResourceProvider -ProviderNamespace Microsoft.Web).ResourceTypes `
    | Where-Object ResourceTypeName -eq sites).ApiVersions

Adding and removing a function key

To add a new function key, we perform a HTTP PUT to the key we want to add (or update). You pass in an object containing the key name and value to be updated.

$keyName = "MyFunctionKey"
$payload = (@{ properties=@{ name=$keyName; value="abcd1234" } } `
    | ConvertTo-Json -Compress).Replace('"', '\"')
az rest --method put `
    --uri "$resourceId/functions/$functionName/keys/$($keyName)?api-version=2018-11-01" `
    --body "$payload"

To remove the key, it's just a HTTP DELETE method to the same endpoint:

az rest --method delete `
    --uri "$resourceId/functions/$functionName/keys/$($keyName)?api-version=2018-11-01"

Adding and removing a host key

The technique for replacing host keys is the same, just now we call the /host/default/functionkeys endpoint. Here I'm adding a new host key.

$keyName = "MyHostKey"
$payload = (@{ properties=@{ name=$keyName; value="efgh5678" } } | ConvertTo-Json -Compress).Replace('"', '\"')
az rest --method put --uri "$resourceId/host/default/functionkeys/$($keyName)?api-version=2018-11-01" --body "$payload"

I assume you can also use this technique to update the "system keys" and master key although I've not tried that yet. Don't try to delete them though.

To delete the host key we added, it's again very straightforward

az rest --method delete `
    --uri "$resourceId/host/default/functionkeys/$($keyName)?api-version=2018-11-01"

Summary

It's now possible to completely automate the retrieval, creation and update of keys using the new Functions ARM APIs. This is a lot simpler than the previous key management API, and has the benefit of being usable from ARM templates as well. Check out this GitHub thread for some examples of how to use these APIs in your ARM templates.

Want to learn more about how easy it is to get up and running with Azure Functions? Be sure to check out my Pluralsight courses Azure Functions Fundamentals and Microsoft Azure Developer: Create Serverless Functions

Comments

Comment by Yury Schkatula

Nice to see Azure CLI did a step to the CURL direction. Keep going!

Yury Schkatula
Comment by Neothoms

Can I suggest to use a powershell az function instead of a string concatenation to get the Function full resource ID ?
Your way :

$resourceId = "/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/sites/$webAppName"

A better (or less trivial) way :

$resourceId(Get-AzResource -Name $webAppName -ResourceGroupName $resourceGroup).ResourceId

Neothoms
Comment by Mattias Nordqvist

where did the subscription parameter go?

Mattias Nordqvist
Comment by Neothoms

You don't need one

Neothoms
Comment by cpmills

You are a legend of the highest order, this helped me greatly. Thank you!

cpmills
Comment by bob bob

I really love the az rest method, however I don't think you've explained how you login to azure with the appropriate credentials.
For example, if you've set up your application based on a specific user principal or service principal, what az login command would you use to login with that id?
I've been looking for a good reference for how to login as the service principal for an application but I can't find it (yet).
OR are you saying it's NOT your login credentials that matter, it's the 'master' key assigned to the application? Since when you set up the application it's the API permissions that determine the level of access? (maybe I just answered my question).
I'm assuming that when you use az login it's different than if you're using curl, so you don't need to specify the tenant sinc. you're already logged in, so you just need to specify the appID and the secret.

bob bob
Comment by Mark Heath

I've blogged about creating service principals and logging in with them using az login here: https://markheath.net/post/...

Mark Heath
Comment by lankaapura

for anyone who is looking to update the master key.

az rest --method put --uri "$resourceId/host/default/functionkeys/_master?api-version=2018-11-01" --body "$payload"

lankaapura
Comment by Lyu

In PowerShell you can also use Invoke-AzResourceAction, e.g. Invoke-AzResourceAction -ResourceId $func.Id -Action 'host/default/listkeys'

Lyu