Tutorial: Create and use a custom image for virtual machine scale sets with the Azure CLI

When you create a scale set, you specify an image to be used when the VM instances are deployed. To reduce the number of tasks after VM instances are deployed, you can use a custom VM image. This custom VM image includes any required application installs or configurations. Any VM instances created in the scale set use the custom VM image and are ready to serve your application traffic. In this tutorial you learn how to:

  • Create and customize a VM
  • Deprovision and generalize the VM
  • Create a custom VM image
  • Deploy a scale set that uses the custom VM image

If you don’t have an Azure subscription, create a free account before you begin.

Use Azure Cloud Shell

Azure hosts Azure Cloud Shell, an interactive shell environment that you can use through your browser. You can use either Bash or PowerShell with Cloud Shell to work with Azure services. You can use the Cloud Shell preinstalled commands to run the code in this article without having to install anything on your local environment.

To start Azure Cloud Shell:

Option Example/Link
Select Try It in the upper-right corner of a code block. Selecting Try It doesn't automatically copy the code to Cloud Shell. Example of Try It for Azure Cloud Shell
Go to https://shell.azure.com, or select the Launch Cloud Shell button to open Cloud Shell in your browser. Launch Cloud Shell in a new window
Select the Cloud Shell button on the top-right menu bar in the Azure portal. Cloud Shell button in the Azure portal

To run the code in this article in Azure Cloud Shell:

  1. Start Cloud Shell.

  2. Select the Copy button on a code block to copy the code.

  3. Paste the code into the Cloud Shell session by selecting Ctrl+Shift+V on Windows and Linux or by selecting Cmd+Shift+V on macOS.

  4. Select Enter to run the code.

If you choose to install and use the CLI locally, this tutorial requires that you are running the Azure CLI version 2.0.29 or later. Run az --version to find the version. If you need to install or upgrade, see Install Azure CLI.

Create and configure a source VM


This tutorial walks through the process of creating and using a generalized VM image. It is not supported to create a scale set from a specialized VM image.

First, create a resource group with az group create, then create a VM with az vm create. This VM is then used as the source for a custom VM image. The following example creates a VM named myVM in the resource group named myResourceGroup:

az group create --name myResourceGroup --location eastus

az vm create \
  --resource-group myResourceGroup \
  --name myVM \
  --image ubuntults \
  --admin-username azureuser \

The public IP address of your VM is shown in the output of the az vm create command. SSH to the public IP address of your VM as follows:

ssh azureuser@<publicIpAddress>

To customize your VM, let's install a basic web server. When the VM instance in the scale set would be deployed, it would then have all the required packages to run a web application. Use apt-get to install NGINX as follows:

sudo apt-get install -y nginx

The final step to prepare your VM for use as a custom image is to deprovision your VM. This step removes machine-specific information from the VM and makes it possible to deploy many VMs from a single image. When the VM is deprovisioned, the host name is reset to localhost.localdomain. SSH host keys, nameserver configurations, root password, and cached DHCP leases are also deleted.

To deprovision the VM, use the Azure VM agent (waagent). The Azure VM agent is installed on every VM and is used to communicate with the Azure platform. The -force parameter tells the agent to accept prompts to reset the machine-specific information.

sudo waagent -deprovision+user -force

Close your SSH connection to the VM:


Create a custom VM image from the source VM

The source VM is now customized with the Nginx web server installed. Let's create the custom VM image to use with a scale set.

To create an image, the VM needs to be deallocated. Deallocate the VM with az vm deallocate. Then, set the state of the VM as generalized with az vm generalize so that the Azure platform knows the VM is ready for use a custom image. You can only create an image from a generalized VM:

az vm deallocate --resource-group myResourceGroup --name myVM
az vm generalize --resource-group myResourceGroup --name myVM

It may take a few minutes to deallocate and generalize the VM.

Now, create an image of the VM with az image create. The following example creates an image named myImage from your VM:

[NOTE] If the Resource Group and Virtual Machine location are different, you can add the --location parameter to the below commands to specificy the location of source VM used to create the image.

az image create \
  --resource-group myResourceGroup \
  --name myImage \
  --source myVM

Create a scale set from the custom VM image

Create a scale set with az vmss create. Instead of a platform image, such as UbuntuLTS or CentOS, specify the name of your custom VM image. The following example creates a scale set named myScaleSet that uses the custom image named myImage from the previous step:

az vmss create \
  --resource-group myResourceGroup \
  --name myScaleSet \
  --image myImage \
  --admin-username azureuser \

It takes a few minutes to create and configure all the scale set resources and VMs.

Test your scale set

To allow traffic to reach your scale set and that verify that the web server works correctly, create a load balancer rule with az network lb rule create. The following example creates a rule named myLoadBalancerRuleWeb that allows traffic on TCP port 80:

az network lb rule create \
  --resource-group myResourceGroup \
  --name myLoadBalancerRuleWeb \
  --lb-name myScaleSetLB \
  --backend-pool-name myScaleSetLBBEPool \
  --backend-port 80 \
  --frontend-ip-name loadBalancerFrontEnd \
  --frontend-port 80 \
  --protocol tcp

To see your scale set in action, get the public IP address of your load balancer with az network public-ip show. The following example gets the IP address for myScaleSetLBPublicIP created as part of the scale set:

az network public-ip show \
  --resource-group myResourceGroup \
  --name myScaleSetLBPublicIP \
  --query [ipAddress] \
  --output tsv

Type the public IP address into your web browser. The default NGINX web page is displayed, as shown in the following example:

Nginx running from custom VM image

Clean up resources

To remove your scale set and additional resources, delete the resource group and all its resources with az group delete. The --no-wait parameter returns control to the prompt without waiting for the operation to complete. The --yes parameter confirms that you wish to delete the resources without an additional prompt to do so.

az group delete --name myResourceGroup --no-wait --yes

Next steps

In this tutorial, you learned how to create and use a custom VM image for your scale sets with the Azure CLI:

  • Create and customize a VM
  • Deprovision and generalize the VM
  • Create a custom VM image
  • Deploy a scale set that uses the custom VM image

Advance to the next tutorial to learn how to deploy applications to your scale set.