How to deploy a host key to Azure Functions

The reason for this post is faulty documentation, and there are even some other posts showing ARM-templates with errors in them.

Creating a host-key using ARM/Bicep

You need to add a hostkey to an existing function during deployment. Here is how to do it. As ever if you just want the code, scroll to the ARM-code towards the end of the post.

Why?

There might be many reasons for doing this, but mine was to allow authentication from an API. The function is already using the built in AAD functionality (see this blog post for more info). Azure Functions also have a built in functionality of using host keys for authentication. It is just like an API-key, and I would like to keep that security feature.

ALM cycles

You often might deploy a Function and API at the same time, within the same pipeline, but I encourage you to rethink this behaviour. Make your APIm a first level citizen in your integration platform. An API that calls a Function today, might call additional services tomorrow, and any update to that API would require a redeploy of the Function.

This is why you need to keep your API in its own ALM cycle.

When to deploy the key

Since we need to separate the Function and the API we cannot deploy the host key with the function. Once again: we would need to redeploy the Function every time we update the API.

I deploy the host key when I deploy the API. The API, and its authentication should be created at the same time.

You could argue that the key is deployed with the function, and the API deployment gets the key from the Function at deploy time. This hinges on the function deployment being responsible for creating all the keys that are needed for the callers. My way allows you to deploy the Function and focus on the code and leave the authorization to the APIm, which is kind of why APIm is used in the first place.

The function does not reset

If you deploy your Azure Functions in a standardized way, the key created by the API deployment will not be deleted during a redeploy. So any post release bug fix does not affect the APIs access to the function.

It will delete the key if you delete the whole function before redeploy, but why would you do that?

Deploying the Key

I use Azure Devops and ARM but I have provided a Bicep version as well. When using Azure Devops I get all the upsides of release management, which is always useful.

The ARM Template

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "FunctionAppName": {
            "type": "String"
        },
        "FunctionAppKey": {
            "type": "SecureString"
        }
    },
    "variables": { "keyName": "MyAPIName-key')]" },
    "functions": [],
    "resources": [
        {
            "type": "Microsoft.Web/sites/host/functionKeys",
            "apiVersion": "2018-11-01",
            "name": "[concat(parameters('FunctionAppName'), '/default/', variables('keyName'))]",
            "properties": {
                "name": "[variables('keyName')]",
                "value": "[parameters('FunctionAppKey')]"
            }
        }
    ],
    "outputs": {}
}

Notice that the FunctionAppKey is a SecureString. This is for keeping the value hidden during and after deployment.

The Context

Deploying the ARM using CI/CD you simply use the ever useful Deploy ARM Template step and refer to the ARM template. Here are some pointers:
– Remeber that the Function is usually in another resource group than the API manager. Update the Resource Group setting.
– Store the Function App key as a variable in the release. Either as a secret value, or as a variable group (pointing to a keyvault).
– Supply the Function App key in the “Override Template parameters” input box.

For more information about deploying values using either parameters or a Keyvault read my post “How I deploy Keyvault values”.

The conclusion

Make your APIm a first level citizen in your ALM cycle. As such, the API deploys anything relating to its access and the best way to do that is using a separate ARM template and run that template at the same time you deploy any update to the API.