5 tips for IIS on containers: #1 SSL certificate lifecycle management
Published Nov 01 2022 03:00 AM 12K Views

Today, my colleague Amy Colyer and I are starting a new blog series based on our talk at Microsoft Ignite 2022. In the session we covered 5 tips for containerizing IIS applications with Windows containers. On that 45-minute session, we covered the management challenges on IIS that customers face today and how Windows containers and Azure Kubernetes Services can help with IT Ops practices. In this blog series, we will explore these tips in more detail. We will go over the management challenges, how Windows containers and other Azure services can help, and how to implement these in detail.


SSL Certificate lifecycle management

SSL certificate management is hard. We’ve all seen it happen when a website starts to show security warnings because a certificate expired, right? IIS is not equipped with an alerting mechanism to let you know that a certificate is about to expire.


However, not only managing when a certificate is about to expire, but handling certificates for websites on VMs is management overhead. If you have multiple websites, that becomes a nightmare. On top of that, if you have to scale your IIS instances, you need to match the certificate for the new instances. Using a gold image on that case might not be in your favor – does the image have all the updated websites and website content? Does the gold image have the updated certificate for all websites?


At this point, managing IIS certificates at scale becomes a manual and tedious process. The question is: How do we make this process easier? The answer to that is in removing the certificate entirely from the IIS instance.


Web application routing add-on on Azure Kubernetes Service

On Kubernetes, an ingress exposes HTTP and HTTPS traffic to the backend service and pods via an ingress rule, which is in turn fulfilled by an ingress controller via an external load balancer. To our SSL certificate management scenario above, this helps a lot as the ingress controller will then handle the HTTPS traffic, and consequently the SSL termination on behalf of the Windows container in the backend. What this means is that you don’t need to host the website SSL certificate on the IIS instance. The website certificate will live on the ingress controller pod so the HTTPS ingress rule can be fulfilled. However, to feed the certificate to the ingress controller pod, you do need to host the certificate somewhere. That’s where Azure Key Vault comes in. You can securely host your SSL certificate on AKS and have the AKS cluster reach for it for your website configuration. More than that, the AKS cluster can monitor changes on the certificate and renew on the ingress controller in case the certificate changed.


The benefits of this approach are:

  • SSL certificates don’t live in the IIS instances. You can scale up or down and all HTTPS traffic directed to the website will work seamlessly.
  • The AKS cluster can reach out to AKV frequently to ensure the certificate hasn’t changed. If the certificate changed – because you provided a new one with a new expiration date, for example – the ingress controller will update the certificate to be used.
  • AKV will notify you in case the certificate is about to expire. In fact, AKV can renew the certificate for you in some cases.
  • This process is a one-time configuration for the ingress controller and ingress rule. All you have to do regarding the SSL certificate after that is to renew it on AKV when the certificate is about to expire. The ingress controller will do the rest for you.
  • While the process described in this post has HTTPS traffic only from client browser to ingress controller, you can implement a service mesh for intra-cluster TLS.


This whole process might seem a bit complex to configure at first – and in fact, if you decide to do all of this manually, it kind of is. Web application routing is a new AKS add-on that has been created to make the process above much simpler. Note: at the time this blog is going out, web application routing add-on is in public preview.


How to configure web application routing on AKS for Windows containers

The Web Application Routing add-on can be installed on a new cluster or an existing one. The link above is a great documentation on how to deploy it, so here I’ll walk you through the process of getting it to work on a new cluster hosting an IIS application on Windows container. In addition, the scripts provided here are also available on GitHub.


In fact, you have two options to run this: You can copy/paste the below into a PowerShell session, or you can simply run the PS1 script. Also, if want to make this easier, you can download this script to an Azure Cloud Shell session and run from there. If you do that, this script is ready to go. If you decide to run from your machine, there are two things you need to change:

  • Uncomment the line #5 to properly log in to Azure from PowerShell.
  • Install OpenSSL on your machine to generate the SSL Certificate to be used.

Here is the whole script.




#This script will create a new AKS cluster and deploy 

#Log into the Azure subscription
$Az_Sub = Read-Host -Prompt 'Please provide the Azure subscription ID to be used'
#az login --use-device-code
az account set --subscription $Az_Sub

# Install the aks-preview extension
az extension add --name aks-preview | Out-Null
# Update the extension to make sure you have the latest version installed
az extension update --name aks-preview | Out-Null

# Create a self-signed SSL certificate
$Hostname = Read-Host -Prompt 'Please provide the Hostname to be used for the certificate'
openssl req -new -x509 -nodes -out aks-ingress-tls.crt -keyout aks-ingress-tls.key -subj "/CN=$Hostname" -addext "subjectAltName=DNS:$Hostname" | Out-Null
# Export the SSL certificate, skipping the password prompt
openssl pkcs12 -export -in aks-ingress-tls.crt -inkey aks-ingress-tls.key -out aks-ingress-tls.pfx | Out-Null

#Create new Resource Group
$RGName = Read-Host -Prompt 'Please provide the Resource Group name'
$RGLocation = Read-Host -Prompt 'Please provide the location of the RG and its resources'
Write-Host 'Creating RG'
az group create -n $RGName -l $RGLocation | Out-Null

#Create the Azure Key Vault
$AKVName = Read-Host -Prompt 'Please provide the name of the Azure Key Vault'
Write-Host 'Creating AKV'
az keyvault create -g $RGName -l $RGLocation -n $AKVName | Out-Null

#Import certificate to Azure Key Vault
$AKVCertName = Read-Host -Prompt 'Please provide a name for the certificate to be stored on Azure Key Vault'
Write-Host 'Creating certificate on AKV'
az keyvault certificate import --vault-name $AKVName -n $AKVCertName -f aks-ingress-tls.pfx | Out-Null

#Create the AKS cluster
Write-Host 'Next, we will create an AKS cluster'
$AKSClusterName = Read-Host -Prompt 'Please provide the name for the Azure Kubernetes Service cluster'
$WinUsername = Read-Host -Prompt 'Please create a username for the administrator credentials on your Windows Server nodes'
$WinPassword = Read-Host -Prompt 'Please create a password for the administrator credentials on your Windows Server nodes' -AsSecureString
Write-Host "Creating AKS cluster"
az aks create -g $RGName -n $AKSClusterName -l $RGLocation --node-count 2 --enable-addons azure-keyvault-secrets-provider,web_application_routing,monitoring --generate-ssh-keys --enable-secret-rotation --network-plugin azure --vm-set-type VirtualMachineScaleSets --windows-admin-username $WinUsername --windows-admin-password $WinPassword` | Out-Null
$AKSNodepoolName = Read-Host -Prompt 'Please provide the name for the nodepool (max 6 characters)'
Write-Host "Creating Windows Node Pool"
az aks nodepool add -g $RGName --cluster-name $AKSClusterName --os-type Windows --name $AKSNodepoolName --node-count 1 | Out-Null

#Retrieve user managed identity object ID for the add-on
Write-Host 'Gathering AKS Managed Identity to be used for AKV access'
$ManagedIdentityName = "webapprouting-$AKSClusterName" | Out-Null
$MCRGName = az aks show -g $RGName -n $AKSClusterName --query nodeResourceGroup -o tsv | Out-Null
$UserManagedIdentity_ResourceID = "/subscriptions/$Az_Sub/resourceGroups/$MCRGName/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$ManagedIdentityName" | Out-Null
$ManagedIdentity_ObjectID = az resource show --id $UserManagedIdentity_ResourceID --query "properties.principalId" -o tsv | tr -d '[:space:]' | Out-Null

#Grant the add-on permissions to retrieve certificates from Azure Key Vault
Write-Host 'Granting the add-on permission to retrieve the certificate'
az keyvault set-policy --name $AKVName --object-id $ManagedIdentity_ObjectID --secret-permissions get --certificate-permissions get | Out-Null

#Connect to your AKS cluster
Write-Host "Now, let's connect to your AKS cluster. We'll retrieve the credentials from Azure"
az aks get-credentials -g $RGName -n $AKSClusterName

#Create a namespace for the application on your AKS cluster
$NamespaceName = Read-Host -Prompt 'Please provide the name for the namespace on which the app will be deployed'
kubectl create namespace $NamespaceName | Out-Null

#Deppoy the application and Ingress
Write-Host "The next steps should only be performed after you edited the YAML files from the repo."
Write-Host "You will need to update the ingress.yaml file with <HostName>, <KeyVaultCertificateUri>, and NameSpace."
Write-Host "Based on the deployment so far, this is the information you need:"
Write-Host "The hostname is:$Hostname"
$KeyVaultCertificateURI = az keyvault certificate show --vault-name $AKVName -n $AKVCertName --query "id" --output tsv  | Out-Null
Write-Host "The Key Vault Certificate URI is: $KeyVaultCertificateURI"
Write-Host "The namespace is: $NamespaceName"
$Edit = Read-Host -Prompt "Did you download the files and edit it? Type YES to continue"
if ($Edit.Equals('YES')) {
    kubectl apply -f deployment.yaml -n $NamespaceName
    kubectl apply -f ingress.yaml -n $NamespaceName
#Final output
Write-Host 'Your deployment finalized with success. Here is the information on the Ingress:'
$IngressInfo = kubectl get ingress -n $NamespaceName
Write-Host "Ingress output: $IngressInfo"




Note that during the execution, you need to download the deployment.yaml and ingress.yaml files. You also need to edit the ingress.yaml file with the proper information – which is given to you during the execution.


This will create a fully functioning Windows pod running IIS, an NGINX Ingress Controller based on the Web App Routing add-on for AKS, and of course, the AKS cluster, node pool, and AKV with the certificate. Keep in mind that this script does not configure DNS. To access the application using the hostname you provided, you’ll need to add an A record on your DNS zone for the IP address of the ingress. (Note: For lab purposes, you can also edit the HOSTS file on your local machine to be able to access a fictitious URL. You can get the IP address of the Ingress from the script above, or by running:




kubectl get ingress -n <namespace>




Next, you can open a browser session for the address you provided as https://<hostname>. This should open the IIS start page – You will see an error message as the certificate is not trusted, but you can safely continue to the website. While the certificate is not trusted (as it is a self-signed certificate) it does prove the point of configuring the cluster with SSL outside the Windows pod.


I hope you enjoyed this and that you can re-use the example above on your environment. Let us know what you think on the comments section below.

Other posts in this series:

 IIS App Pools and Websites

Hardcoded configuration
Solving for Horizontal Scale

Container and Node OS upgrade

IIS Remote Management

Version history
Last update:
‎Dec 29 2022 11:54 AM
Updated by: