Add Key Vault references to Azure Functions

Last Update: 12/28/2019
Azure
Key Vault
ARM Template

October this year, a feature called Key Vault references was GA 3. This is a function to store secrets handled by Azure Functions and App Service in Key Vault, and to use them transparently from applications (use as environment variables without being aware of Key Vault from applications). This article describes how to achieve this Key Vault references. The basic contents are the same as those described in Microsoft's official document 1, but I will add some notes and tips on my own. In addition, this article describes how to implement using ARM Template and Azure CLI instead of Azure portal, which will help you construct CI / CD infrastructure.

1. Add secrets and permissions to Key Vault

Add secrets and permissions of Azure Functions in Key Vault.

keyvault.json
{
  "$schema": "https://schema.management.azure.com/schemas/2018-05-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "keyVaultName": {
      "type": "string",
      "metadata": {
        "description": "Key Vault Name"
      }
    },
    "secretName": {
      "type": "string",
      "metadata": {
        "description": "Secret Name"
      }
    },
    "functionAppName": {
      "type": "string",
      "metadata": {
        "description": "Function App Name"
      }
    },
    "storageAccountName": {
      "type": "string",
      "metadata": {
        "description": "Storage Account Name"
      }
    }
  },
  "variables": {},
  "resources": [
    {
      "name": "[parameters('keyVaultName')]",
      "type": "Microsoft.KeyVault/vaults",
      "apiVersion": "2018-02-14",
      "location": "[resourceGroup().location]",
      "properties": {
        "tenantId": "[subscription().tenantId]",
        "sku": {
          "family": "A",
          "name": "standard"
        },
        "accessPolicies": [
          {
            "tenantId": "[subscription().tenantId]",
            "objectId": "[reference(resourceId('Microsoft.Web/sites', parameters('functionAppName')), '2016-03-01', 'Full').identity.principalId]",
            "permissions": {
              "keys": [],
              "secrets": ["get"],
              "certificates": [],
              "storage": []
            }
          }
        ],
        "enabledForDeployment": false,
        "enabledForDiskEncryption": false,
        "enabledForTemplateDeployment": false
      },
      "resources": [
        {
          "name": "[concat(parameters('keyVaultName'), '/', parameters('secretName'))]",
          "type": "Microsoft.KeyVault/vaults/secrets",
          "apiVersion": "2018-02-14",
          "dependsOn": [
            "[resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName'))]"
          ],
          "properties": {
            "value": "[concat('DefaultEndpointsProtocol=https;AccountName=',parameters('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2015-05-01-preview').key1)]"
          }
        }
      ]
    }
  ]
}

Access rights are set in the accessPolicies part. In the property called objectId, specify the Managed ID of the target Azure Functions. Managed ID is described later, but they are IDs assigned to Azure Functions that can be used to identify Azure Functions and manage access rights. For permissions, specify the permissions you want to grant. In the example above, Azure functions will have permission to view the secret. Set any permissions here according to the application.

The secret is set in the resource of Microsoft.KeyVault/vaults/secrets. Specify the secret value in value of properties. In this example, the key of the storage account is set. Also specify any secret here according to the purpose.

2. Add application settings to Azure Functions

Next, let's deploy the application settings. For simplicity, only the settings that are relevant to Key Vault references are listed.

appsettings
{
  "$schema": "https://schema.management.azure.com/schemas/2018-05-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "keyVaultName": {
      "type": "string",
      "metadata": {
        "description": "Key Vault Name"
      }
    },
    "secretName": {
      "type": "string",
      "metadata": {
        "description": "Secret Name"
      }
    },
    "functionAppName": {
      "type": "string",
      "metadata": {
        "description": "Function App Name"
      }
    }
  },
  "variables": {
    "secretResourceId": "[resourceId(resourceGroup().name, 'Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), 'parameters('secretName')')]"
  },
  "resources": [
    {
      "name": "[concat(parameters('functionAppName'), '/appsettings')]",
      "type": "Microsoft.Web/sites/config",
      "apiVersion": "2018-11-01",
      "location": "[resourceGroup().location]",
      "properties": {
        "YOUR_SECRET": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('secretResourceId'), '2018-02-14').secretUriWithVersion, ')')]"
      }
    }
  ]
}

Within the ARM Template, set values as follows:

  1. Generate resourceId indicating secret resource (defined in variables part)
  2. Get the Key Vault's secret URI with the property secretUriWithVersion for the resourceId of step 1
  3. Add the uri to application settings in the format of @Microsoft.KeyVault(SecretUri={secret uri})

The resourceId and reference that appear in Template examples are called template functions. For more information, see the official Microsoft documentation 4.

3. Deploy resources with attention to dependencies

When using Key Vault references, keep in mind the order of deployment. The basic order is as follows:

  1. Azure Functions resources (Microsoft.Web/sites)
  2. Key Vault
  3. Azure Functions appsettings
  4. Deploy Azure Functions zip (build artifacts)

First, deploy Azure Functions. This is because when you deploy Key Vault in step 2, the Managed ID for Azure Functions must have been issued in advance to set access policy to Key Vault. Managed ID can be issued with the following Template.

AzureFunction_ManagedId
{
  "apiVersion": "2016-03-01",
  "name": "[parameters('functionAppName')]",
  "type": "Microsoft.Web/sites",
  "identity": {
    "type": "SystemAssigned"
  },
  "properties": {
    "name": "[parameters('functionAppName')]",
    "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName'))]",
    "hostNameSslStates": [
      {
        "name": "[concat(variables('functionAppName'),'.azurewebsites.net')]",
        "sslState": "Disabled",
        "virtualIP": null,
        "thumbprint": null,
        "toUpdate": null,
        "hostType": "Standard"
      },
      {
        "name": "[concat(variables('functionAppName'),'.scm.azurewebsites.net')]",
        "sslState": "Disabled",
        "virtualIP": null,
        "thumbprint": null,
        "toUpdate": null,
        "hostType": "Repository"
      }
    ],
    "siteConfig": null,
    "clientAffinityEnabled": false,
    "reserved": false,
    "httpsOnly": true
  },
  "location": "[resourceGroup().location]",
  "kind": "functionapp"
}

Specify the property identity with SystemAssigned type. At this time, Key Vault references are only available for SystemAssigned type of ID 1.

However, you cannot deploy the appsettings yet at step 1. Because at this point, Key Vault is not deployed and you cannot specify a SecretUri, which is mandatory for the Key Vault references. After Key Vault is deployed in step 2, deploy the Azure Functions appsettings. Finally, deploy the Azure Functions build artifacts. The caveat here is that you need to deploy the application zip after the appsettings has been deployed. If you deploy the build artifacts first and then deploy the appsettings, the function will disappear.

Precautions when setting applications using Azure CLI

The example above uses the ARM Template, but of course you can leverage the Azure CLI as well. While ARM Template always overwrites existing application settings, Azure CLI can add new settings to existing application settings, thus reducing the above dependency constraints. For example, we have stated that the appsettings in step 3 must be after the Key Vault deployment in step 2 and before extracting the zip in step 4. But if you can add (not overwrite) the appsettings using the Azure CLI, you can first deploy the application settings other than the Key Vault references at the timing of step 1, and after deploying the Key Vault or extracting the zip, then add the rest of the appsettings related to the Key Vault references using the Azure CLI.

This is how to specify application settings for Key Vault in Azure CLI.

keyvault_secreturi
try {
    $ret = az keyvault secret show --vault-name $keyVaultName --name $secretName
}
catch
{
    $message = $_.Exception.message
    throw "Failed to get key vault secret: ${message}."
}
$secretObj = $ret | ConvertFrom-Json
$url = $secretObj.id
$secretUri = '"@Microsoft.KeyVault(SecretUri={0})"' -f $url

# Add appsettings for azure functions
az functionapp config appsettings set --name $functionAppName --resource-group $resourceGroup --settings YOUR_SECRET=$secretUri

It should be noted that you need to set Key Vault references in the format of $secretUri = '"@Microsoft.KeyVault(SecretUri={0})"' -f $url. If you write like $secretUri = "@Microsoft.KeyVault(SecretUri=${url})", the last ) will be incorrectly set and an error will occur 2.

Another point to note is the permissions required to execute the above commands. The user or service principal who runs this command must have read permission for the Key Vault secret. In the Key Vault template described above, only Azure Functions were granted read permission, but let's temporarily grant read permission to the command executor as well.

4. Getting a secret in your application

All you have to do is just accessing the environment variables in your application code.

get_secret
// C#
var env = System.Environment.GetEnvironmentVariable("YOUR_SECRET", EnvironmentVariableTarget.Process);

// Javascript
let env = process.env["YOUR_SECRET"];

// Java
String env = System.getenv("YOUR_SECRET");

Summary

This article described how to use Key Vault references that has been GA since October. The order of deployment is a bit tricky, but otherwise it should not be too difficult. Please try using it.

2019 Copyright Channel.241