Performing an Azure VM Scale Set deployment is a great way to get autoscaling and high-availability out of the box. Azure VM Scale Sets have many baked-in features like automatically scaling to meet demand, deploying across availability zones and more.

When you need to deploy an Azure Virtual Machine Scale Set, you've got a few options such as using the Azure portal, the AZ CLI or PowerShell. In this article, let's dive into what it takes to build some  PowerShell code to deploy and configure scale sets.

Most of the code for this article was created by my friend, Josh Duffney, as he studied for the AZ-103 certification. I've chosen to write a tutorial around it since I found the code so useful. Catch up on what Josh is doing at his blog.

Tutorial Overview

In this article/tutorial, you'll learn how to use PowerShell to perform many common actions around Azure VM Scale Sets from:

  • Creating an Azure VM Scale Set
  • Creating a network security group (NSG)
  • Assigning an NSG to a Scale Set's subnet
  • Deploying IIS and a sample application to a scale set
  • Adding additional VMs to the scale set
  • Allowing inbound RDP to all scale set instances
  • Tearing down the scale set

By breaking down actions in this way will allow you to get a full breadth of knowledge on what it takes to manage a full lifecycle of an Azure VM Scale Set.

Prerequisites

To follow along with this tutorial, you should have a few prerequisites in place before starting. Be sure you have each of these requirements met before attempting the steps in this tutorial.

  • An Azure subscription. If you don't have one already, you can get Azure for free.
  • Have already set up Azure Cloud Shell and are logged in. To prevent having to install any dependencies, all examples you'll be seeing will be run inside of the Azure Cloud Shell.
  • Assuming Azure global administrator permission. Laziness is in strong effect here obviously. You don't have to be a global admin but you know what I mean.
If you don't have any Azure credits, you will incur some charges by following this tutorial. However, they should be minimal. Don't forget to clean up when you're done!

Options for Creating an VM Scale Set in Azure

Your first task is to create an Azure VM scale set using the New-AzVmss cmdlet. To manage high-availability and redundancy, a scale set consists of different types of resources such as a virtual network, load balancer, public IP address and the VM instances themselves. The New-AzVMss cmdlet takes care of all of that.

You have two options when creating a VM scale set with the New-AzVmss cmdlet. You can either use the simple parameter set which quickly creates a pre-set scale set and assumes much of the configuration for you or provide each configuration option separately.

Creating an Azure VM Scale Set: The Easy Way

In this tutorial, you'll be focusing on specifying out each resource separately but if you'd like to create a  simple scale set, you can do so with only two parameters; Credential and VMScaleSetName. The Credential parameter is a PSCredential object for the local administrator account for each VM instance.

You can see an example below on how to create everything required for a scale set with PowerShell.

$vmssName = 'vmssScaleSet-simple'
$vmPassword = ConvertTo-SecureString '<somepassword>' -AsPlainText -Force
$vmCred = New-Object System.Management.Automation.PSCredential('<somelocaladminaccountname>', $vmPassword)

## Create a VMSS using the default settings
New-AzVmss -Credential $vmCred -VMScaleSetName $vmssName

Once started, you will then be presented with a progress indicator as shown below.

When complete, the easy route will net you:

  • A Resource Group
  • A virtual network
  • A load balancer
  • A public IP
  • A VMSS with two Windows instances

You can see what the vmssScaleSet-simple resource group looks like via the Azure portal below.

Creating an Azure VM Scale Set: The Better Way

You're not taking the easy route in this tutorial. You need to learn how this stuff works! The remainder of this article will be built off of a scale set where you define each resource for a scale set yourself.

The simple parameter set covered above covers creating everything including a resource group but going it on your own does not. If specifying each resource to support the scale set with the New-AzVmss cmdlet, you'll have to create the resource group yourself. No biggie.

To create a resource group with Azure PowerShell, use the New-AzResourceGroup cmdlet providing the name of the resource group and the region/location.

The below example is creating a resource group called vmssPractice in the eastus region.

PS Azure:\\> New-AzResourceGroup -Name vmssPractice -Location eastus

ResourceGroupName : vmssPractice
Location          : eastus
ProvisioningState : Succeeded
Tags              :
ResourceId        : /subscriptions/1427e7fb-a488-4ec5-be44-30ac10ca2e95/resourceGroups/vmssPractice

Once the resource has been created, you can then call the New-AzVmss cmdlet again. But this time, define each of the resource names yourself. To do so, you'll notice below you have options to name each resource that builds the scale set like the subnet, load balancer and more.

Upgrade Policies

Notice below you're using the UpgradePolicyMode parameter. Later in this tutorial, you'll be modifying the scale set and this will be important. An upgrade policy determines how VMs are brought up-to-date when a change is made to the scale set model.

A scale set model is how a scale set is represented in Azure. It is a schema of how a scale set should look. Within each scale set is a scale set instance that represents the current runtime state or what the scale set looks like right now.

The three modes for the upgrade policy are:

  • Automatic - VMs may be rebooted in a random order even taking all VMs temporarily down at the same time.
  • Rolling - The scale set deploys updates to instances in batches with an optional pause time between batches.
  • Manual - Nothing happens to existing VMs when an update is initiated.

Since this tutorial is just an example, the UpgradePolicyMode is set to Automatic to ensure the VM instances are automatically brought up to date when you do that later.

$vmPassword = ConvertTo-SecureString '<somepassword>' -AsPlainText -Force
$vmCred = New-Object System.Management.Automation.PSCredential('<somelocaladminusername>', $vmPassword)

$params = @{
    ResourceGroupName = 'vmssPractice'
    Location = 'eastus'
    VmScaleSetName = 'vmssScaleSet'
    SubnetName = 'vmssSubnet'
    PublicIpAddressName = 'vmssPublicIpAddress'
    LoadBalancerName = 'vmssLoadBalancer'
    UpgradePolicyMode = 'Automatic'
    Credential = $vmCred
}

New-AzVmss @params
Even though the upgrade policy is set to Automatic, doesn't mean that the scale set will be automatically updated when the model is. This simply defines how the VMs are rebooted when an update is made. Refer to this blog post for more information.

The scale set and related resources will take a few minutes to be created.

Setting up Networking

Now that the scale set and all related resources are created, it's time to perform some common maintenance actions. As-is, the scale set is wide open to the Internet. You need to lock it down. To do that, you first need to create a network security group (NSG).

Setting up the Network Security Group (NSG)

To create an NSG for the scale set, you'll:

  1. Get the scale set model with Get-AzVmss.
  2. Create a configuration object representing the rules you'd like to configure.
  3. Create the NSG using the newly-created rule.

First, use the Get-AzVmss cmdlet to find the newly-created scale set model as shown below.

$vmss = Get-AzVmss -ResourceGroupName vmssPractice -VMScaleSetName vmssScaleSet
The Get-AzVmss cmdlet doesn't actually query the scale set itself. Instead, it queries the scale set's model. To query the realtime view of the scale set, you can use the InstanceView parameter.

Creating the Network Security Rule

Next, you'll need to create a network security rule. A network security rule represents all of the firewall rules that will eventually be applied to the NSG. Think of a security rule as something similar to a Windows Firewall rule.

To create a network security rule, use the New-AzNetworkSecurityRuleConfig cmdlet. Since you'll eventually be deploying an IIS web service to this scale set, you need to allow HTTP traffic to the load balancer. You can see an example below of how to do this.

The example below is creating a rule called allowHTTP allowing all TCP traffic coming in on port 80 access to the load balancer. The output is being saved to the $nsgRule variable to pass to the next command.

$nsgRuleParams = @{
    Name = 'allowHTTP'
    Protocol = 'Tcp'
    Direction = 'Inbound'
    Priority = 200
    SourceAddressPrefix = '*'
    SourcePortRange = '*'
    DestinationAddressPrefix = '*'
    DestinationPortRange = 80
    Access = 'Allow'
}

$nsgRule = New-AzNetworkSecurityRuleConfig @nsgRuleParams

Creating the Network Security Group

Once the rule is created, you can now create the NSG and apply the rule using the New-AzNetworkSecurityGroup cmdlet as shown below.

$nsgParams = @{
    ResourceGroupName = 'vmssPractice'
    Location = 'eastus'
    Name = 'vmssNSG'
    SecurityRules = $nsgRule
}

$nsg = New-AzNetworkSecurityGroup @nsgParams

Assigning the NSG to the Subnet

When the scale set was created, a virtual network was created with it. Since the NSG is created, it's now time to assign that NSG to the subnet within the virtual network. To do so requires three steps:

  1. Getting the created virtual network
  2. Adding the NSG to the virtual network's subnet configuration
  3. Applying the change to the virtual network

First, find the virtual network with Get-AzVirtualNetwork.

$vnet = Get-AzVirtualNetwork -ResourceGroupName vmssPractice -Name vmssScaleSet

Next, create a subnet configuration object that "attaches" the NSG to the subnet using the Set-AzVirtualNetworkSubnetConfig cmdlet. Since a virtual network can have more than one subnet, below you're ensuring only the first one is selected.

$subnet = $vnet.Subnets[0]
$subnetConfigParams = @{
    VirtualNetwork = $vnet
    Name = 'vmssSubnet'
    AddressPrefix = $subnet.AddressPrefix
    NetworkSecurityGroup = $nsg
}

$subnetConfig = Set-AzVirtualNetworkSubnetConfig @subnetConfigParams

Finally, apply the new subnet change to the virtual network using the Set-AzVirtualNetwork cmdlet.

Set-AzVirtualNetwork -VirtualNetwork $vnet

Updating the Scale Set Model

At this point, the scale set model doesn't yet know of the NSG that's been added to the vNet. You must now update it to reflect this change. To do so, use the Update-AzVmss cmdlet. This cmdlet has a parameter called VirtualMachineScaleSet which represents a scale set model containing the updated information.

By providing the resource group name, name of the scale set and the updated scale set model, you can now make the VM instances aware of the newly added NSG as shown below.

$vmss = Get-AzVmss -ResourceGroupName vmssPractice -VMScaleSetName vmssScaleSet
Update-AzVmss -ResourceGroupName vmssPractice -Name vmssScaleSet -VirtualMachineScaleSet $vmss

Deploying a Sample Application

Creating an Azure VM scale set is only an end to a means. In a production environment, you'll use a scale set to run workloads like a web service. Let's now install IIS to our scale set and create a simple default web page.

You can find a simple, sample PowerShell script stored in a Microsoft GitHub repo here. This PowerShell script, called automate-iis.ps1 contains just two lines to install IIS and create a default webpage as shown below.

Add-WindowsFeature Web-Server
Set-Content -Path "C:\\inetpub\\wwwroot\\Default.htm" -Value "Hello World from host $($env:computername) !"

To invoke this script, set up an Azure Custom Script Extension for the VM scale set. You'll do this using the Add-AzVmssExtension cmdlet which downloads the automate-iis.ps1 PowerShell script from GitHub and runs it.

You can see an example of how to do this below.

$publicSettings = @{
    "fileUris" = (,"<https://raw.githubusercontent.com/Azure-Samples/compute-automation-configurations/master/automate-iis.ps1>");
    "commandToExecute" = "powershell -ExecutionPolicy Unrestricted -File automate-iis.ps1"
}

$vmss = Get-AzVmss -ResourceGroupName vmssPractice -VMScaleSetName vmssScaleSet

$extParams = @{
    VirtualMachineScaleSet = $vmss
    Name = 'CustomScript'
    Publisher = 'Microsoft.Compute'
    Type = 'CustomScriptExtension'
    TypeHandlerVersion = 1.8
    Setting = $publicSettings
}

Add-AzVmssExtension @extParams

To update all VM instances in the scale set, update the scale set once again.

Update-AzVmss -ResourceGroupName vmssPractice -Name vmssScaleSet -VirtualMachineScaleSet $vmss

If you'd look in the Azure Portal, you'd now see the scale set being updated as seen in the screenshot below.

When the update is complete, you should now have IIS installed and a default webpage set up on all of the VM instances.

Verifying Application Deployment

To verify the update has happened, first find the public DNS name of your scale set as shown below.

(Get-AzPublicIpAddress -Name vmssPublicIpAddress).DnsSettings.Fqdn

Now navigate to http://<FQDN> in your browser. If all went well, you should see the webpage below.

Scale the Virtual Machine Set

Let's say your awesome web application is getting tons of traffic and you need to add more instances to the scale set. Doing so only requires updating a single property on the scale set object queried earlier.

By setting the SKU capacity to the number of instances you need and sending the update to the scale set, the scale set's instance count will soon be increased.

$vmss.sku.capacity = 3

Update-AzVmss -ResourceGroupName vmssPractice -Name vmssScaleSet -VirtualMachineScaleSet $vmss

This provisions a new VM instance and adds it to the scale set. The process may take a few minutes. Be patient!

Tearing it Down

Since this was just a tutorial, let's now tear down the work you've done. Since the scale set and everything included with it is all in a single resource group, you now just need to remove the resource group. This will clean up all of the resources provisioned in this article.

Remove-AzResourceGroup -ResourceGroupName vmssPractice

Further Reading