Using a certificate stored in Key Vault in an Azure App Service
For the last two days, I’ve been trying to deploy some new microservices using a certificate stored in Key Vault in an Azure App Service. By now, you’ve probably figured out that we love them around here. I’ve also been slamming my head against the wall because of some not-well-documented functionality about granting permissions to the Key Vault.
As a quick primer, here’s the basics of what I was trying to do:
resource "azurerm_app_service" "centralus-app-service" { name = "${var.service-name}-centralus-app-service-${var.environment_name}" location = "${azurerm_resource_group.centralus-rg.location}" resource_group_name = "${azurerm_resource_group.centralus-rg.name}" app_service_plan_id = "${azurerm_app_service_plan.centralus-app-service-plan.id}" identity { type = "SystemAssigned" } } data "azurerm_key_vault" "cert" { name = "${var.key-vault-name}" resource_group_name = "${var.key-vault-rg}" }
resource "azurerm_key_vault_access_policy" "centralus" { key_vault_id = "${data.azurerm_key_vault.cert.id}" tenant_id = "${azurerm_app_service.centralus-app-service.identity.0.tenant_id}" object_id = "${azurerm_app_service.centralus-app-service.identity.0.principal_id}" secret_permissions = [ "get" ] certificate_permissions = [ "get" ] }
resource "azurerm_app_service_certificate" "centralus" { name = "${local.full_service_name}-cert" resource_group_name = "${azurerm_resource_group.centralus-rg.name}" location = "${azurerm_resource_group.centralus-rg.location}" key_vault_secret_id = "${var.key-vault-secret-id}" depends_on = [azurerm_key_vault_access_policy.centralus] }
and these are the relevant values I was passing into the module:
key-vault-secret-id = "https://example-keyvault.vault.azure.net/secrets/cert/0d599f0ec05c3bda8c3b8a68c32a1b47" key-vault-rg = "example-keyvault" key-vault-name = "example-keyvault"
But no matter what I did, I kept bumping up against this error:
Error: Error creating/updating App Service Certificate "example-app-dev-cert" (Resource Group "example-app-centralus-rg-dev"): web.CertificatesClient#CreateOrUpdate: Failure responding to request: StatusCode=400 -- Original Error: autorest/azure: Service returned an error. Status=400 Code="BadRequest" Message="The service does not have access to '/subscriptions/[SUBSCRIPTIONID]/resourcegroups/example-keyvault/providers/microsoft.keyvault/vaults/example-keyvault' Key Vault. Please make sure that you have granted necessary permissions to the service to perform the request operation." Details=[{"Message":"The service does not have access to '/subscriptions/[SUBSCRIPTIONID]/resourcegroups/example-keyvault/providers/microsoft.keyvault/vaults/example-keyvault' Key Vault. Please make sure that you have granted necessary permissions to the service to perform the request operation."},{"Code":"BadRequest"},{"ErrorEntity":{"Code":"BadRequest","ExtendedCode":"59716","Message":"The service does not have access to '/subscriptions/[SUBSCRIPTIONID]/resourcegroups/example-keyvault/providers/microsoft.keyvault/vaults/example-keyvault' Key Vault. Please make sure that you have granted necessary permissions to the service to perform the request operation.","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/[SUBSCRIPTIONID]/resourcegroups/example-keyvault/providers/microsoft.keyvault/vaults/example-keyvault"]}}]
I checked and re-checked and triple-checked and had colleagues check, but no matter what I did, it kept puking with this permissions issue. I confirmed that the App Service’s identity was being provided and saved, but nothing seemed to work.
Then I found this blog post from 2016 talking about a magic Service Principal (or more specifically, a Resource Principal) that requires access to the Key Vault too. All I did was add the following resource with the magic SP, and everything worked perfectly.
resource "azurerm_key_vault_access_policy" "azure-app-service" { key_vault_id = "${data.azurerm_key_vault.cert.id}" tenant_id = "${azurerm_app_service.centralus-app-service.identity.0.tenant_id}" # This object is the Microsoft Azure Web App Service magic SP # as per https://azure.github.io/AppService/2016/05/24/Deploying-Azure-Web-App-Certificate-through-Key-Vault.html object_id = "abfa0a7c-a6b6-4736-8310-5855508787cd" secret_permissions = [ "get" ] certificate_permissions = [ "get" ] }
It’s frustrating that Microsoft hasn’t documented this piece (at least officially), but hopefully with this knowledge, you’ll be able to automate using a certificate stored in Key Vault in your next Azure App Service.