App Service Resource Provider Access to Keyvault
Recently, I was trying to deploy an Azure App Service which was in need for a couple of certificates, which are stored in Azure Key Vault.
Our ARM template looked very similar to the one below in order to install & configure the certificates in our App Service.
"resources": [
{
"type": "Microsoft.Web/certificates",
"name": "[parameters('certificateName')]",
"apiVersion": "2019-08-01",
"location": "[parameters('existingAppLocation')]",
"properties": {
"keyVaultId": "[parameters('existingKeyVaultId')]",
"keyVaultSecretName": "[parameters('existingKeyVaultSecretName')]",
"serverFarmId": "[parameters('existingServerFarmId')]"
}
},
{
"type": "Microsoft.Web/sites",
"name": "[parameters('existingWebAppName')]",
"apiVersion": "2019-08-01",
"location": "[parameters('existingAppLocation')]",
"dependsOn": [
"[resourceId('Microsoft.Web/certificates', parameters('certificateName'))]"
],
"properties": {
"hostNameSslStates": [
{
"name": "[parameters('hostname')]",
"sslState": "SniEnabled",
"thumbprint": "[reference(resourceId('Microsoft.Web/certificates', parameters('certificateName'))).Thumbprint]",
"toUpdate": true
}
]
}
}
]
When deploying said template, we quickly received a message the deployment had failed.
Resource Microsoft.Web/certificates ‘[myCert]’ failed with message
{ “Code”: “BadRequest”, “Message”: “The service does not have access to ‘/subscriptions/[subscription]/resourcegroups/[resourcegroup]/providers/microsoft.keyvault/vaults/[myVault]’ Key Vault. Please make sure that you have granted necessary permissions to the service to perform the request operation.”, “Target”: null, “Details”:
[
{
"Message": "The service does not have access to '/subscriptions/[subscription]/resourcegroups/[resourcegroup]/providers/microsoft.keyvault/vaults/[myVault]' Key Vault. Please make sure that you have granted necessary permissions to the service to perform the request operation."
},
{
"Code": "BadRequest"
},
{
"ErrorEntity": {
"ExtendedCode": "59716",
"MessageTemplate": "The service does not have access to '{0}' Key Vault. Please make sure that you have granted necessary permissions to the service to perform the request operation.",
"Parameters": [ "/subscriptions/[subscription]/resourcegroups/[resourcegroup]/providers/microsoft.keyvault/vaults/[myVault]"
],
"Code": "BadRequest",
"Message": "The service does not have access to '/subscriptions/[subscription]/resourcegroups/[resourcegroup]/providers/microsoft.keyvault/vaults/[myVault]' Key Vault. Please make sure that you have granted necessary permissions to the service to perform the request operation."
It goes without saying, our Azure DevOps environment and the used service principal do have enough permission to access the Key Vault.
Apparently, your certificates aren’t being deployed by the Azure DevOps service principal, but via the Microsoft Azure App Service
service principal (or Resource Provider) which doesn’t has access to your Key Vaults.
By default, ‘Microsoft.Azure.WebSites’ Resource Provider (RP) doesn’t have access to the Key Vault specified in the template hence you need to authorize it by executing the following PowerShell commands before deploying the template:
Source: https://github.com/Azure/azure-quickstart-templates/tree/master/201-web-app-certificate-from-key-vault
Every subscription has this one and it has the same application id on every subscription (abfa0a7c-a6b6-4736-8310-5855508787cd
). Therefore, you also have to grant this principal enough permission to retrieve certificates from your Key Vault.
To find out what the actual object id of this service principal is you can use the following Azure CLI command.
az ad sp list --display-name "Microsoft Azure App Service"
You’ll get a big blob of JSON and somewhere in there you’ll find the object id which has to be used inside your Key Vault access policies.
{
"type": "Microsoft.KeyVault/vaults",
"name": "[variables('instanceName')]",
"apiVersion": "2016-10-01",
"location": "[resourceGroup().location]",
"tags": "[variables('tags')]",
"properties": {
"enabledForDeployment": false,
"enabledForTemplateDeployment": true,
"enabledForVolumeEncryption": false,
"tenantId": "[subscription().tenantId]",
"sku": {
"name": "Standard",
"family": "A"
},
"accessPolicies": [
{
"tenantId": "[subscription().subscriptionId]",
"objectId": "[parameters('theObjectIdFromTheAppServicePrincipal')]",
"permissions": {
"keys": [
],
"secrets": [
],
"certificates": [
"Get"
],
"storage": [
]
}
}
]
}
}
You’re all set to deploy certificates for your SSL bindings and other stuff on your App Service now.
Just make sure you deploy the new Key Vault access policies, with Azure App Service Resource Provider policies, before trying to deploy the App Service again.
Do note, the App Service Resource Provider principal exists inside your tenant. Because of this, if you add this access policy, EVERY App Service inside your tenant will be able to GET certificates from this specific Key Vault. You’ve read this right, every App Service inside your TENANT! It doesn’t matter if you use multiple subscriptions, most of the time/a lot of the times the tenant is shared between the subscriptions.
I became aware of this after Joonas Westlin pointed this out on Twitter as I hadn’t thought of this possible security vulnerability before. As Louis Simonetti points out, you probably don’t want to have these configurations inside your Production environment because of this.
While this is a useful access policy, use it with care and know what you are doing.