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.
Key Vault
Add secrets and permissions of Azure Functions
in Key Vault
.
{
"$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.
Azure Functions
Next, let's deploy the application settings. For simplicity, only the settings that are relevant to Key Vault references
are listed.
{
"$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:
resourceId
indicating secret resource (defined in variables
part)secretUriWithVersion
for the resourceId
of step 1@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.
When using Key Vault references
, keep in mind the order of deployment.
The basic order is as follows:
Azure Functions
resources (Microsoft.Web/sites
)Key Vault
Azure Functions
appsettingsAzure 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.
{
"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.
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
.
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.
All you have to do is just accessing the environment variables in your application code.
// C#
var env = System.Environment.GetEnvironmentVariable("YOUR_SECRET", EnvironmentVariableTarget.Process);
// Javascript
let env = process.env["YOUR_SECRET"];
// Java
String env = System.getenv("YOUR_SECRET");
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.