This sample provides an example of how to use Custom Script Extenion to update a Windows application running in Azure Virtual Machine Scale Sets (VMSS). It assumes the VMSS has been configured with Rolling Upgrades to prevent downtime. It also assumes Uniform VMSS mode but should be easily changed to support Flexible as well (more on that below).
It should not be treated as production-ready code, consider this an example only. Where applicable, links are included to the official docs for further reference.
Before you begin, there are working examples for Linux and Windows which I strongly recommend going through. This repo build on those examples (using Azure CLI and Windows), adds a custom image to minimize app update time and also adds CI/CD through GitHub Actions.
A quick and important note: CustomScriptExtension is different for Windows and Linux. Consider this when looking at the examples.
- Windows:
- publisher "Microsoft.Compute"
- type "CustomScriptExtension"
- version "1.10"
- Linux
- publisher "Microsoft.Azure.Extensions"
- type "CustomScript"
- version "2.0"
- Azure Scale Set (VMSS) app update with Custom Script Extension (Windows)
- Table of Contents
- Contents
- Instructions
- Required Azure infrastructure for this example
- Creating the required Azure infrastructure
- (Optional) - local run of the ASP.NET app
- Behind the scenes
- Screenshots
- Contributing
The focus of this example is on the az vmss extension set
instruction and how to use artifacts uploaded to an Azure Storage account without a public endpoint to update an app running on VMSS.
In this repo:
- ASP.NET Core mvc application used as an example
- GitHub Actions workflow to build and deploy the app to Azure VMSS
Things that could be (a lot) better:
- IaC - no infra-as-code provided in this repo
- GitHub Actions workflow is a single job, could split into at least 2 - build and deploy
- Missing ASP.NET web.config transforms for production - this might be very important for your scenario but it's out of scope here.
Azure Virtual Machine Scale Sets (VMSS) have 2 very different operation modes - Flecible and Uniform. They're very well documented and Flexible is now the recommended one. However, given that Flexible is new and Uniform supports Rolling Upgrades, this repo assumes you have an Azure VMSS running in Uniform mode with Rolling Upgrades.
If you want to use this repo with Flexible mode VMSS, remember that the upgrade policy is null when creating a Flexible VMSS. Which means you'll have to upgrade the instances after the az vmss extension set
block in the workflow.
An approach to do that would be to use the az vmss reimage
command as documented here. Please beware, to avoid downtime, you'll have to reimage in batches and wait for those to come online before updating the next batch. Out of scope for this repo.
Stil, if you want to do it, include that command in the workflow as a new step after the az vmss extension set
one.
- An Azure subscription :)
- A Virtual Machine image in a gallery. This VM image should have IIS and ASP.NET Core installed.
- An Azure VMSS created with Uniform mode and Rolling Upgrade policy.
- An Azure Load Balancer with a public IP or an Application Gateway that exposes a public endpoint and has a backend pool configured for the VMSS instances.
- (Optional) An Azure Bastion if you want to RDP into the individual instances (can be useful for debugging)
Follow the steps in Create a Windows Virtual Machine
-
This will also install IIS and open port 80
-
Make sure this is running properly
NOTE: This step is not really necessary, you can use one of the provided base images to create a Scale Set. However, by having the required services and software pre-installed, using a custom image will significantly reduce the startup, scale-out and updating time of the scale set.
-
- Replace the value of
--managed-image
in this example with the ID of your VM from Step 1.
- Replace the value of
Follow the steps in the Deploy apps to a scale set docs while making the following changes:
-
When (creating the scale set)[https://learn.microsoft.com/en-us/azure/virtual-machine-scale-sets/tutorial-install-apps-cli#create-a-scale-set]:
- use
--orchestration-mode Uniform
- use
--image "/subscriptions/<Subscription ID>/resourceGroups/myGalleryRG/providers/Microsoft.Compute/galleries/myGallery/images/myImageDefinition"
(or the custom image you created before)
- use
-
NOTE: You can skip the custom script extenion as this will be added by the GitHub Actions workflow.
You can do this via CLI during the creation of the scale or in the portal afterwards. In this example, for testing purposes, I've set the sliders to 50% so half of the images are updated in each batch.
You can get your Azure Credentials by following this: https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blobs-static-site-github-actions?tabs=userlevel#generate-deployment-credentials
Once you have those credentials:
- Fork this repo
- Add the required repos secrets
Just download the latest version of asp.net core (at this time, it's version 7), then, in a command prompt:
cd app
dotnet restore
dotnet run
The GitHub Actions workflow should be relatively easy to follow and I've added some comments there. Also, if you're using Azure DevOps, you can use the az cli
commands like they're used here or check if there's specific Azure DevOps tasks for the job, but I won't cover that here.
The example app will show a simple Welcome. Yes, CSS and JS is not working due to some IIS/ASP.NET configuration on the VM, but I'm not a Windows/IIS/ASP.NET expert and it's not essential for this example.
If you change something in the app (let's say the welcome message), and push it to the main branch of the repo (or merge a pull request, ideally!), the GitHub Actions workflow gets triggered.
When the GitHub Actions workflow is triggered, the az vmss set extension step will start updating the VMSS. You should see the first batch of instances being updated.
When the command completes execution, all instances should now be updated, healthy and with the latest model.
The public endpoint of the VMSS should now show the updated app.
Just as a reference, here's an Azure Load Testing service running for 15 minutes while the update is taking place. As you can see, because we're using rolling upgrade policy, there's no downtime.
This could definitely be better, so you're welcome to open a pull request.