SSL: automatic renewal

Azure — Application Gateway, App Service, Front Door

An Azure Automation runbook fetches the fresh certificate once a day and adds a new version to Key Vault, and Azure services pick it up on their own.

Renews by itself

Use this if your site's certificate is stored in Azure Key Vault and used by Application Gateway v2, App Service or Front Door (Standard/Premium). These services switch to a new certificate version in Key Vault by themselves if they are linked "without a version" (Latest): Application Gateway within 4 hours, App Service within a day, Front Door within up to 72 hours.

How it works: a runbook in Azure Automation fetches the certificate from the link once a day, compares it with the current one in Key Vault and, if it is new, builds a PFX with your key (the key is stored in the same Key Vault as a secret) and imports a new version. We get no access to your Azure.

What you need

  • The certificate link from your account: https://api.zrazu.com/ssl-pull/zsp_YOUR_TOKEN.
  • A private key without a password, the key.pem file. For Front Door, RSA only: Front Door does not accept ECDSA keys.
  • A Key Vault with the Azure role-based access control permission model and Owner rights on it.

Step 1. Certificate and key in Key Vault

Open Cloud Shell (the >_ button at the top of the portal, Bash mode), upload key.pem (Manage files → Upload) and run the commands below, putting in the name of your Key Vault:

VAULT=KEY_VAULT_NAME
curl -fsS -o fullchain.pem "https://api.zrazu.com/ssl-pull/zsp_YOUR_TOKEN/fullchain.pem"
az keyvault secret set --vault-name $VAULT --name zrazu-ssl-key --file key.pem
openssl pkcs12 -export -in fullchain.pem -inkey key.pem -out zrazu.pfx -passout pass:
az keyvault certificate import --vault-name $VAULT --name zrazu-ssl --file zrazu.pfx
rm key.pem zrazu.pfx

If the certificate is already in Key Vault under another name, skip the last import and change $CertName in the runbook (step 3).

Link the certificate to the service without a version if you have not yet: Application Gateway — Listeners → certificate from Key Vault; App Service — Certificates → Bring your own certificates → Import from Key Vault; Front Door — Secrets → Add certificate → Latest version.

Step 2. Automation account

  1. Automation Accounts → Create → same resource group and region as the Key Vault → Review + Create.
  2. In the account, Account Settings → Identity → System assigned: On → Save.
  3. In the Key Vault, Access control (IAM) → Add role assignment: role Key Vault Certificates Officer → Managed identity → your Automation account → Review + assign. Repeat for the Key Vault Secrets User role.

Step 3. Runbook

  1. In the Automation account, Process Automation → Runbooks → Create a runbook: name zrazu-ssl-update, Runbook type PowerShell, Runtime version 7.2 → Create.
  2. Paste the text below, put the Key Vault name in the $Vault line → Save → Publish.
# ZRAZU: updates the certificate in Azure Key Vault using the personal link. Runs once a day.
$ErrorActionPreference = "Stop"
$Pull = "https://api.zrazu.com/ssl-pull/zsp_YOUR_TOKEN"
$Vault = "KEY_VAULT_NAME"
$CertName = "zrazu-ssl"
$KeySecret = "zrazu-ssl-key"
$X509 = [Security.Cryptography.X509Certificates.X509Certificate2]

Connect-AzAccount -Identity | Out-Null
$full = (Invoke-WebRequest -Uri "$Pull/fullchain.pem" -UseBasicParsing).Content
if ($full -is [byte[]]) { $full = [Text.Encoding]::ASCII.GetString($full) }
$pems = @([regex]::Matches($full, "-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----") | ForEach-Object { $_.Value })
if ($pems.Count -eq 0) { throw "ZRAZU: not a certificate" }
$leaf = $X509::CreateFromPem($pems[0])
$current = Get-AzKeyVaultCertificate -VaultName $Vault -Name $CertName
if ($current -and $current.Thumbprint -eq $leaf.Thumbprint) { "ZRAZU: unchanged"; return }
if ($leaf.NotAfter -le (Get-Date)) { throw "ZRAZU: certificate expired" }
$key = Get-AzKeyVaultSecret -VaultName $Vault -Name $KeySecret -AsPlainText
$withKey = $X509::CreateFromPem($pems[0], $key)
$all = New-Object Security.Cryptography.X509Certificates.X509Certificate2Collection
[void]$all.Add($withKey)
foreach ($pem in ($pems | Select-Object -Skip 1)) { [void]$all.Add($X509::CreateFromPem($pem)) }
$pfx = $all.Export([Security.Cryptography.X509Certificates.X509ContentType]::Pkcs12)
Import-AzKeyVaultCertificate -VaultName $Vault -Name $CertName -CertificateString ([Convert]::ToBase64String($pfx)) | Out-Null
"ZRAZU: updated, valid until $($leaf.NotAfter.ToString('yyyy-MM-dd'))"

If the key does not match the certificate, CreateFromPem stops with an error and nothing is imported.

Step 4. Schedule

In the runbook, Link to schedule → Link a schedule to your runbook → Add a schedule: name zrazu-daily, Recurrence Recurring, every 1 day, any time at night → Create → OK.

Check

  1. In the runbook, Start → OK. Open the job (Jobs), Output tab: "updated" or "unchanged".
  2. In the Key Vault, Certificates → zrazu-ssl shows a new version with a new date.
  3. Site: https://zrazu.com/tools/ssl/example.com/ — allowing for how fast your service picks up the new version (see the top of this page).

If it did not work

  • Forbidden / does not have secrets get permission — one of the two roles from step 2 was not assigned, or the Key Vault uses the access policy model: then in Access policies give the identity Certificates: Get, Import and Secrets: Get.
  • "The provided key does not match" — the certificate was reissued with a new key: az keyvault secret set --vault-name $VAULT --name zrazu-ssl-key --file new-key.pem.
  • 404 — the link is disabled or replaced: create a new one in the certificate card and replace $Pull.
  • Front Door does not take the certificate — the key is ECDSA, or the certificate is linked with a specific version instead of Latest.

This page in Markdown