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.
Hi Justin, thanks for your post here. Till now, it was working fine for us with the “magic Service Principal” but as I was trying to create a new app service with the same configuration as my existing services, it’s giving me …./mykeyVault/objectId/abfa0a7c-a6b6-4736-8310-5855508787cd already exists – to be managed via Terraform this resource needs to be imported into the State. Please see the resource documentation for “azurerm_key_vault_access_policy” for more information. Are you aware if something has changed with the azurerm update. We recently updated our terraform to use version 2.15 of azurerm.
So what that is saying is that the provider thinks that Key Vault object already exists. Did someone create it manually (e.g. through the portal or through az cli) and then you tried to replicate it in Terraform? Or is this an actually new resource?
Thanks, Justin. As I imported the resource directly to the state file, it worked fine. Thanks for your help. Keep posting more like these articles.
That’s great, I’m glad to hear that worked!
You have to add “Microsoft Azure App Service” object in the Key Vault access policy then It’ll work
Yeah, that’s the “magic” Resource Principal that isn’t listed anywhere (or wasn’t, at the time that I wrote this article). Thanks for clarifying!