locked
Cannot set Custom Hostname Certificate to use Key Vault RRS feed

  • Question

  • I am attempting to switch my custom hostnames over to use a SSL certificate stored in azure key vault vs one that is uploaded to the service directly to help in certificate rotation.

    Both the portal and gateway are already configured and working correctly with a certificate that was manually uploaded (pfx) originally.

    I have done the following:

    • Enabled the managed service identity (MSI) on my APIM instance.
    • Added this identity (via APIM resource name) to my Key vault in the "access policies" section with the following permission: Certificates (Get)
    • Go to APIM instance and select Custom Domains > {Portal | Gateway}
    • Switch Certificate to "Key Vault"
    • Select the correct certificate from the drop-downs. (This currently is working for azure websites, so I know it is good.)
    • Hit the "Save" button.

    I get the following error when hitting save:

    Failed to update API Management service hostnames
    Request to resource 'https://{vaultname}.vault.azure.net/secrets/{certname}/?api-version=7.0' failed with StatusCode: Forbidden for RequestId: . Exception message: Operation returned an invalid status code 'Forbidden'

    Is there something I am doing wrong or additional permissions that need to be setup in the keyvault for this to work?

    Thursday, June 6, 2019 6:23 PM

Answers

  • If you use Azure Key Vault to manage the custom domain SSL certificate, make sure the certificate is inserted into Key Vault as a certificate, not a secret.

    Also can you try this using ARM. This scenario using ARM is described here. 

    https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-use-managed-service-identity#use-the-managed-service-identity-to-access-other-resources

    The following example shows an Azure Resource Manager template that contains the following steps:

    1. Create an API Management instance with a managed identity.
    2. Update the access policies of an Azure Key Vault instance and allow the API Management instance to obtain secrets from it.
    3. Update the API Management instance by setting a custom domain name through a certificate from the Key Vault instance.

    {
    	"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    	"contentVersion": "1.0.0.0",
    	"parameters": {
    		"publisherEmail": {
    			"type": "string",
    			"minLength": 1,
    			"metadata": {
    				"description": "The email address of the owner of the service"
    			}
    		},
    		"publisherName": {
    			"type": "string",
    			"defaultValue": "Contoso",
    			"minLength": 1,
    			"metadata": {
    				"description": "The name of the owner of the service"
    			}
    		},
    		"sku": {
    			"type": "string",
    			"allowedValues": ["Developer",
    			"Standard",
    			"Premium"],
    			"defaultValue": "Developer",
    			"metadata": {
    				"description": "The pricing tier of this API Management service"
    			}
    		},
    		"skuCount": {
    			"type": "int",
    			"defaultValue": 1,
    			"metadata": {
    				"description": "The instance size of this API Management service."
    			}
    		},
    		"keyVaultName": {
    			"type": "string",
    			"metadata": {
    				"description": "Name of the vault"
    			}
    		},
    		"proxyCustomHostname1": {
    			"type": "string",
    			"metadata": {
    				"description": "Proxy Custom hostname."
    			}
    		},
    		"keyVaultIdToCertificate": {
    			"type": "string",
    			"metadata": {
    				"description": "Reference to the KeyVault certificate. https://contoso.vault.azure.net/secrets/contosogatewaycertificate."
    			}
    		}
    	},
    	"variables": {
    		"apiManagementServiceName": "[concat('apiservice', uniqueString(resourceGroup().id))]",
    		"apimServiceIdentityResourceId": "[concat(resourceId('Microsoft.ApiManagement/service', variables('apiManagementServiceName')),'/providers/Microsoft.ManagedIdentity/Identities/default')]"
    	},
    	"resources": [{
    		"apiVersion": "2017-03-01",
    		"name": "[variables('apiManagementServiceName')]",
    		"type": "Microsoft.ApiManagement/service",
    		"location": "[resourceGroup().location]",
    		"tags": {
    		},
    		"sku": {
    			"name": "[parameters('sku')]",
    			"capacity": "[parameters('skuCount')]"
    		},
    		"properties": {
    			"publisherEmail": "[parameters('publisherEmail')]",
    			"publisherName": "[parameters('publisherName')]"
    		},
    		"identity": {
    			"type": "systemAssigned"
    		}
    	},
    	{
    		"type": "Microsoft.KeyVault/vaults/accessPolicies",
    		"name": "[concat(parameters('keyVaultName'), '/add')]",
    		"apiVersion": "2015-06-01",
    		"dependsOn": [
    			"[resourceId('Microsoft.ApiManagement/service', variables('apiManagementServiceName'))]"
    		],
    		"properties": {
    			"accessPolicies": [{
    				"tenantId": "[reference(variables('apimServiceIdentityResourceId'), '2015-08-31-PREVIEW').tenantId]",
    				"objectId": "[reference(variables('apimServiceIdentityResourceId'), '2015-08-31-PREVIEW').principalId]",
    				"permissions": {
    					"secrets": ["get"]
    				}
    			}]
    		}
    	},
    	{
    		"apiVersion": "2017-05-10",
    		"name": "apimWithKeyVault",
    		"type": "Microsoft.Resources/deployments",
    		"dependsOn": [
    		"[resourceId('Microsoft.ApiManagement/service', variables('apiManagementServiceName'))]"
    		],
    		"properties": {
    			"mode": "incremental",
    			"templateLink": {
    				"uri": "https://raw.githubusercontent.com/solankisamir/arm-templates/master/basicapim.keyvault.json",
    				"contentVersion": "1.0.0.0"
    			},
    			"parameters": {
    				"publisherEmail": { "value": "[parameters('publisherEmail')]"},
    				"publisherName": { "value": "[parameters('publisherName')]"},
    				"sku": { "value": "[parameters('sku')]"},
    				"skuCount": { "value": "[parameters('skuCount')]"},
    				"proxyCustomHostname1": {"value" : "[parameters('proxyCustomHostname1')]"},
    				"keyVaultIdToCertificate": {"value" : "[parameters('keyVaultIdToCertificate')]"}
    			}
    		}
    	}]
    }

    Friday, June 7, 2019 5:09 AM

All replies

  • If you use Azure Key Vault to manage the custom domain SSL certificate, make sure the certificate is inserted into Key Vault as a certificate, not a secret.

    Also can you try this using ARM. This scenario using ARM is described here. 

    https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-use-managed-service-identity#use-the-managed-service-identity-to-access-other-resources

    The following example shows an Azure Resource Manager template that contains the following steps:

    1. Create an API Management instance with a managed identity.
    2. Update the access policies of an Azure Key Vault instance and allow the API Management instance to obtain secrets from it.
    3. Update the API Management instance by setting a custom domain name through a certificate from the Key Vault instance.

    {
    	"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    	"contentVersion": "1.0.0.0",
    	"parameters": {
    		"publisherEmail": {
    			"type": "string",
    			"minLength": 1,
    			"metadata": {
    				"description": "The email address of the owner of the service"
    			}
    		},
    		"publisherName": {
    			"type": "string",
    			"defaultValue": "Contoso",
    			"minLength": 1,
    			"metadata": {
    				"description": "The name of the owner of the service"
    			}
    		},
    		"sku": {
    			"type": "string",
    			"allowedValues": ["Developer",
    			"Standard",
    			"Premium"],
    			"defaultValue": "Developer",
    			"metadata": {
    				"description": "The pricing tier of this API Management service"
    			}
    		},
    		"skuCount": {
    			"type": "int",
    			"defaultValue": 1,
    			"metadata": {
    				"description": "The instance size of this API Management service."
    			}
    		},
    		"keyVaultName": {
    			"type": "string",
    			"metadata": {
    				"description": "Name of the vault"
    			}
    		},
    		"proxyCustomHostname1": {
    			"type": "string",
    			"metadata": {
    				"description": "Proxy Custom hostname."
    			}
    		},
    		"keyVaultIdToCertificate": {
    			"type": "string",
    			"metadata": {
    				"description": "Reference to the KeyVault certificate. https://contoso.vault.azure.net/secrets/contosogatewaycertificate."
    			}
    		}
    	},
    	"variables": {
    		"apiManagementServiceName": "[concat('apiservice', uniqueString(resourceGroup().id))]",
    		"apimServiceIdentityResourceId": "[concat(resourceId('Microsoft.ApiManagement/service', variables('apiManagementServiceName')),'/providers/Microsoft.ManagedIdentity/Identities/default')]"
    	},
    	"resources": [{
    		"apiVersion": "2017-03-01",
    		"name": "[variables('apiManagementServiceName')]",
    		"type": "Microsoft.ApiManagement/service",
    		"location": "[resourceGroup().location]",
    		"tags": {
    		},
    		"sku": {
    			"name": "[parameters('sku')]",
    			"capacity": "[parameters('skuCount')]"
    		},
    		"properties": {
    			"publisherEmail": "[parameters('publisherEmail')]",
    			"publisherName": "[parameters('publisherName')]"
    		},
    		"identity": {
    			"type": "systemAssigned"
    		}
    	},
    	{
    		"type": "Microsoft.KeyVault/vaults/accessPolicies",
    		"name": "[concat(parameters('keyVaultName'), '/add')]",
    		"apiVersion": "2015-06-01",
    		"dependsOn": [
    			"[resourceId('Microsoft.ApiManagement/service', variables('apiManagementServiceName'))]"
    		],
    		"properties": {
    			"accessPolicies": [{
    				"tenantId": "[reference(variables('apimServiceIdentityResourceId'), '2015-08-31-PREVIEW').tenantId]",
    				"objectId": "[reference(variables('apimServiceIdentityResourceId'), '2015-08-31-PREVIEW').principalId]",
    				"permissions": {
    					"secrets": ["get"]
    				}
    			}]
    		}
    	},
    	{
    		"apiVersion": "2017-05-10",
    		"name": "apimWithKeyVault",
    		"type": "Microsoft.Resources/deployments",
    		"dependsOn": [
    		"[resourceId('Microsoft.ApiManagement/service', variables('apiManagementServiceName'))]"
    		],
    		"properties": {
    			"mode": "incremental",
    			"templateLink": {
    				"uri": "https://raw.githubusercontent.com/solankisamir/arm-templates/master/basicapim.keyvault.json",
    				"contentVersion": "1.0.0.0"
    			},
    			"parameters": {
    				"publisherEmail": { "value": "[parameters('publisherEmail')]"},
    				"publisherName": { "value": "[parameters('publisherName')]"},
    				"sku": { "value": "[parameters('sku')]"},
    				"skuCount": { "value": "[parameters('skuCount')]"},
    				"proxyCustomHostname1": {"value" : "[parameters('proxyCustomHostname1')]"},
    				"keyVaultIdToCertificate": {"value" : "[parameters('keyVaultIdToCertificate')]"}
    			}
    		}
    	}]
    }

    Friday, June 7, 2019 5:09 AM
  • Any updates on this ?
    Monday, June 10, 2019 2:55 AM
  • Thank you for the ARM template.  After reading through it, I see the error that I made.  I was giving the managed identity for the APIM instance permissions to get certificates, but not the secrets, as was illustrated in the ARM template.

    As soon as I added the Secrets(Get) permission, the portal worked correctly.

    Thank you!

    Wednesday, June 12, 2019 4:23 PM