Azure Resource Manager Templates - Using Certificates from KeyVault in AppServices

Certificates have various uses in AppServices. The most obvious one is to enable SSL for your application. Another use is to authenticate towards Azure KeyVault to retrieve confidential values.

In this post, we will be uploading a certificate to KeyVault. Then we will deploy it to an AppService with Azure Resource Manager. Finally, we will set a custom domain binding to use the certificate for SSL.

(If you would like to test this without buying a certificate, see below on how to create a self-signed certificate.)

Adding a Certificate to KeyVault

In PowerShell, run this script to set a couple of variables. Replace $pwd with the password for your .pfx file. In $file, put the path to your file or just the name if it resides in the current folder:

$pwd = 'Password12!'
$file = 'test.local.pfx'

Next, run the following script which will extract the certificate contents. After executing, the variable $certSecureString will contain the certificate:

$fullPath = Get-ChildItem $file
$certCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
$certCollection.Import($fullPath.FullName, $pwd, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
$certExport = $certCollection.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pkcs12)
$certb64 = [System.Convert]::ToBase64String($certExport)
$certSecureString = ConvertTo-SecureString -String $certb64 -AsPlainText -Force

Finally, you can upload it to your KeyVault using the following cmdlet. Remember to replace the VaultName with your vault’s name. Also, provide a name that is appropriate to your environment and usage:

Set-AzureKeyVaultSecret -VaultName demokeyvault -Name democert1 -SecretValue $certSecureString -ContentType 'application/x-pkcs12'

You have now deployed the certificate to KeyVault and can use it in resource templates.

Deploying a Certificate from KeyVault

To deploy a certificate from KeyVault, use the Microsoft.Web/certificate type. First, you must grant access to the Microsoft.Web provider using the following:

Set-AzureRmKeyVaultAccessPolicy -VaultName keyvault-bhany3pt7zg6k -ServicePrincipalName abfa0a7c-a6b6-4736-8310-5855508787cd -PermissionsToSecrets get

The service principal name is a constant across all Azure subscriptions and for all resource providers. You have now given the provider read access to the vault.

Here is an example resource template:

{
	"apiVersion": "2015-08-01",
	"location": "[resourceGroup().location]",
	"name": "kvcertificate1",
	"type": "Microsoft.Web/certificates",
	"properties": {
		"keyVaultId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.KeyVault/vaults/', 'demo-keyvault')]",
		"keyVaultSecretName": "demo-cert1",
		"serverFarmId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Web/serverfarms/', 'demo-asp1')]"
	}
}

Provide the keyVaultId like you would for deploying simple secrets. The keyVaultSecretName is the name you provided to the Set-AzureKeyVaultSecret cmdlet in step 1. Finally, serverFarmId is the App Service Plan you want to deploy to. You deploy certificates at the App Service Plan level and reference them from the App Service.

Using the Certificate for a Custom Domain Binding and SSL

You can use a custom domain name for any AppService, but the underlying App Service Plan must run at least on tier Basic. Otherwise, you get a cryptic error message: “The parameter App Service Plan has an invalid value.”

Additionally, Azure requires specific DNS records for your domain. The easiest approach is to add a CNAME record for a subdomain pointing to the Azure App Service-provided hostname (e.g., something.azurewebsites.net). Once completed, you can use the following resource template to deploy the domain and SSL:

{
	"type": "Microsoft.Web/sites/hostnameBindings",
	"name": "[concat(variables('appName'), '/', 'demo.example.org')]",
	"apiVersion": "2016-03-01",
	"location": "[resourceGroup().location]",
	"properties": {
		"sslState": "SniEnabled",
		"thumbprint": "[reference(resourceId('Microsoft.Web/certificates', 'democert1')).Thumbprint]"
	},
	"dependsOn": ["[concat('Microsoft.Web/certificates/', 'democert1')]"]
}

The property sslState can be either SniEnabled or IpBasedEnabled. The thumbprint references the certificate installed above, so it also needs a dependsOn.

Once deployed, you have your custom domain and SSL set up. See here for a solution on how to enforce SSL in Azure App Service.

If you only want to assign the custom domain, leave the properties object empty.

Bonus: Creating a Self-Signed Certificate for Test Purposes

If you don’t have a certificate available, you can create your own. This is only recommended for specific use cases and testing.

Open PowerShell in Administrator Mode. First, set a variable to a password of your choice. Set a second variable to a name of your choice. For testing, you can use both values provided here. The password will be removed before uploading to Azure anyway:

$pwd = 'Password12!'
$dnsName = 'test.local'

Next, execute the following script. It will use the New-SelfSignedCertificate cmdlet to create a certificate in your local certificate store. This is why you need administrative rights to run this script. The Export-PfxCertificate cmdlet then exports the certificate to a .pfx file. The file is protected by the password you set above:

$file = $dnsName + '.pfx'
$certStore = 'cert:LocalMachine\My'
$cert = New-SelfSignedCertificate -DnsName $dnsName -CertStoreLocation $certStore
$certPath = $certStore + '\' + $cert.Thumbprint
$certPwd = ConvertTo-SecureString -String $pwd -Force -AsPlainText
Export-PfxCertificate -Cert $certPath -FilePath $file -Password $certPwd

You can now use this file to run through the steps above.