If you run a website, it's expected to run with SSL. In the old days buying those certificates could become costly, but since a couple of years Let's Encrypt is giving them away for free in an effort to make the web safer.
Certificates issued by Let's Encrypt are only valid for 90 days (read about their reasoning), and can only be requested with an API. I combined the following resources and tweaked it for reusability and Azure Functions v2:
- Let's Encrypt Site Extension
- Install instructions for the Site Extension
- Running the extension in an Azure Function
- Using Managed Service Identity (MSI).
That's how I ended up with a fully automated way to request and assign all my SSL certificates in Azure. It's a big list of steps to get this running, so let's start with an overview:
- Create a Function App v2, configure it for MSI and enable Let's Encrypt
- Create an Azure AD application and assign it access rights to manage your Azure Web App
- Create an Azure Key Vault and store the secrets
- Create the actual Azure Function that will request the SSL certificates
Create a Function App v2, configure it for MSI and enable Let's Encrypt
- Go into the Azure Portal and create an Azure Function App. It works with a consumption plan, but I reused an App Service Plan. v2 is the new default.
- In the Azure Function App, go into Platform Settings.
- Find Managed Service Identity and enable it.
- Open Site Extensions, click Add and find and install Azure Let's Encrypt (No Web Jobs)
- Restart the Function App
- Download the Publishing Profile, open it. Store the value of userPwd for later use.
Create an Azure AD application and assign it access rights to manage your Azure Web App
- Go into the Azure Portal, open Active Directory and App registrations.
- Click + New application registration, give it a name and a url. Select Web App / API
- Copy the Application ID and generate a key, store both for later use.
- Go to Access Control for each App Service, App Service Plan and AZure Function that requires an SSL certificate. If they are in the same Resource Group, you can assign access there instead.
- Add Contributor access to the Azure AD application you just registered.
Create an Azure Key Vault and store the secrets
- Go into the Azure Portal and create an Azure Key Vault.
- Under Access Policies, find the Managed Service Identity (it's named the same as your Azure Function App). Assign it Get permissions under Secrets.
- Under Secrets, create 3 secrets: clientSecret, userPwd, pfxPwd. As values put in the generated key from the previous step, the deployment password from the first step and you can freely choose a password for your certificate.
- Copy and store the url for the Key Vault.
Create the actual Azure Function that will request the SSL certificates
- Go into the Azure Portal and open the Azure Function App.
- Create a new Function, choose Timer Trigger. I named it Let's Encrypt and put 0 0 0 1 * *. This will renew my certificates on the first day of every month.
- Create an additional file in the Azure Function, named function.proj. Past in the code from this gist.
- In the run.csx file itself, paste the code from this gist. Make sure to replace the values in the lines marked with TODO.
The actual creation of the certificate is done with the following lines of code (make sure to update the host name with your own domain):
configBody.AcmeConfig.Host = "blog.yannickreekmans.be"; await CreateCertificate(configBody, functionAppName, client, log);
You can use the same Azure Function for multiple domains and App Services. Make sure to update the values accordingly. Eg. if the additional domain is for a different App Service, run it like this:
configBody.AzureEnvironment.WebAppName = "otherwebapp"; configBody.AcmeConfig.Host = "yannickreekmans.be"; await CreateCertificate(configBody, functionAppName, client, log);
Now it just needs a little tweaking to create certificates for Azure Function Apps, but that's for a different post!