Share via

Practice lab question on how can you automate and manage custom scalable Azure VM based infrastructure solutions (Expert Level)

Nelson Narayan 0 Reputation points
2026-05-05T09:28:26.2266667+00:00

In this challenge, you will create a custom image, use the image to create a scale set with availability zones, configure autoscale, and deploy an update to the scale set using a custom script extension. What steps, cmdlets using Azure Portal, CLI and Azure Resource Manager templates to solve

Azure Virtual Machine Scale Sets
Azure Virtual Machine Scale Sets

Azure compute resources that are used to create and manage groups of heterogeneous load-balanced virtual machines.

0 comments No comments

2 answers

Sort by: Most helpful
  1. Himanshu Shekhar 5,740 Reputation points Microsoft External Staff Moderator
    2026-05-05T10:59:51.2266667+00:00

    Hey Nelson, here’s a walkthrough you can use to build your lab solution (Portal, CLI/PowerShell, and ARM template examples) for each of the four challenge steps:

    Create a Custom VM Image a. Azure Portal • Deploy a VM from Marketplace or existing image. • Generalize it: connect via RDP/SSH, run “sysprep /generalize /shutdown” (Windows) or “sudo waagent -deprovision+user -force; sudo shutdown -h now” (Linux). • In the Portal, go to the VM’s Overview > Capture > give it a name, resource group, and select “Automatically delete this virtual machine after creating the image.” b. Azure CLI

    # Deallocate & generalize (Windows example)
    az vm deallocate --resource-group RG --name MyVM
    az vm generalize  --resource-group RG --name MyVM
    az image create     --resource-group RG --name MyCustomImage \
                         --source MyVM
    

    c. PowerShell

    Stop-AzVM -ResourceGroupName RG -Name MyVM -Force
    Set-AzVM -ResourceGroupName RG -Name MyVM -Generalized
    New-AzImage -ResourceGroupName RG -ImageName MyCustomImage -SourceVirtualMachineId $vm.Id
    

    d. ARM Template snippet

    {
      "type": "Microsoft.Compute/images",
      "apiVersion": "2021-04-01",
      "name": "[parameters('imageName')]",
      "location": "[resourceGroup().location]",
      "properties": {
        "storageProfile": {
          "osDisk": {
            "osType": "Windows",
            "osState": "Generalized",
            "blobUri": null,
            "managedDisk": {
              "id": "[parameters('vmId')]"
            }
          }
        }
      }
    }
    
    1. Create a Scale Set with Availability Zones a. Azure Portal • Navigate to “Create a resource” > “Compute” > “Virtual machine scale set.” • Under “Basics,” choose your RG, name, and custom image. • Under “Availability options,” pick “Availability zones” and select Zones 1, 2, 3. • Finish networking, health probes, and hit Create. b. Azure CLI
         az vmss create \
      

    --resource-group RG
    --name MyScaleSet
    --image MyCustomImage
    --zones 1 2 3
    --instance-count 2
    --upgrade-policy-mode Automatic

       
       c. PowerShell
       
       powershell
       $sku = New-AzVmssSku -Name Standard_DS1_v2 -Capacity 2 -Tier Standard
    $vz  = New-AzVmssVirtualMachineScaleSetPublicIPAddressConfiguration -Name pipConfig -DomainNameLabel mylabel
    $ipconfig = New-AzVmssIpConfig -Name ipconfig1 -SubnetId $subnet.Id -PublicIpAddressConfiguration $vz
    $vmss = New-AzVmssConfig -Location eastus -Sku $sku -Overprovision
    $vmss.VirtualMachineProfile.NetworkProfile.NetworkInterfaceConfigurations.Add(
      (New-AzVmssNetworkInterfaceConfiguration -Name nic1 -Primary `
        -IpConfiguration $ipconfig -EnableIPForwarding))
    $vmss.VirtualMachineProfile.StorageProfile.ImageReference.Id = $image.Id
    $vmss.Zones = @("1","2","3")
    New-AzVmss -ResourceGroupName RG -Name MyScaleSet -VirtualMachineScaleSet $vmss
    

    d. ARM Template snippet

       {
      "type": "Microsoft.Compute/virtualMachineScaleSets",
      "apiVersion": "2021-04-01",
      "name": "[parameters('vmssName')]",
      "location": "[resourceGroup().location]",
      "zones": [ "1", "2", "3" ],
      "sku": { "name": "Standard_DS1_v2", "capacity": 2 },
      "properties": {
        "upgradePolicy": { "mode": "Automatic" },
        "virtualMachineProfile": {
          "storageProfile": {
            "imageReference": { "id": "[resourceId('Microsoft.Compute/images', parameters('imageName'))]" }
          },
          "networkProfile": {
            "networkInterfaceConfigurations": [
              {
                "name": "nicConfig",
                "properties": {
                  "primary": true,
                  "ipConfigurations": [
                    {
                      "name": "ipconfig1",
                      "properties": {
                        "subnet": { "id": "[parameters('subnetId')]" },
                        "publicIPAddressConfiguration": {
                          "name": "pipConfig",
                          "properties": { "domainNameLabel": "[parameters('dnsLabel')]" }
                        }
                      }
                    }
                  ]
                }
              }
            ]
          }
        }
      }
    }
    
    1. Configure Autoscale a. Azure Portal • In your VMSS blade, go to “Scaling” > “Add a rule.” • Define a metric (e.g., CPU Percentage > 75%), scale out by +1, cool-down period 5 min. • Add a scale‐in rule (e.g., CPU < 25%, decrease by 1). b. Azure CLI
         az monitor autoscale create \
      

    --resource-group RG
    --resource MyScaleSet
    --resource-type Microsoft.Compute/virtualMachineScaleSets
    --name autoscaleConfig
    --min-count 1 --max-count 5 --count 2

    az monitor autoscale rule create
    --resource-group RG --autoscale-name autoscaleConfig
    --condition "Percentage CPU > 75 avg 5m"
    --scale out 1

    az monitor autoscale rule create
    --resource-group RG --autoscale-name autoscaleConfig
    --condition "Percentage CPU < 25 avg 5m"
    --scale in 1

       
       c. PowerShell
       
       powershell
       $autoscale = Add-AzAutoscaleSetting -ResourceGroupName RG -TargetResourceId $vmss.Id `
      -Name "MyVMSSAutoscale" -MinCapacity 1 -MaxCapacity 5 -DefaultCapacity 2
    Add-AzAutoscaleRule -AutoscaleSetting $autoscale `
      -MetricName "Percentage CPU" -Operator GreaterThan -Threshold 75 `
      -TimeGrain 00:01:00 -TimeWindow 00:05:00 -Statistic Average `
      -ScaleActionScaleType ChangeCount -ScaleActionValue 1 -ScaleActionCooldown 00:05:00
    Add-AzAutoscaleRule -AutoscaleSetting $autoscale `
      -MetricName "Percentage CPU" -Operator LessThan -Threshold 25 `
      -TimeGrain 00:01:00 -TimeWindow 00:05:00 -Statistic Average `
      -ScaleActionScaleType ChangeCount -ScaleActionValue -1 -ScaleActionCooldown 00:05:00
    

    d. ARM Template snippet

       {
      "type": "Microsoft.Insights/autoscaleSettings",
      "apiVersion": "2015-04-01",
      "name": "autoscaleConfig",
      "location": "[resourceGroup().location]",
      "properties": {
        "targetResourceUri": "[resourceId('Microsoft.Compute/virtualMachineScaleSets', parameters('vmssName'))]",
        "enabled": true,
        "profiles": [
          {
            "name": "AutoScaleProfile",
            "capacity": { "minimum": "1", "maximum": "5", "default": "2" },
            "rules": [
              {
                "metricTrigger": {
                  "metricName": "Percentage CPU",
                  "metricNamespace": "",
                  "operator": "GreaterThan",
                  "threshold": 75,
                  "timeGrain": "PT1M",
                  "statistic": "Average",
                  "timeWindow": "PT5M",
                  "timeAggregation": "Average"
                },
                "scaleAction": {
                  "direction": "Increase",
                  "type": "ChangeCount",
                  "value": "1",
                  "cooldown": "PT5M"
                }
              },
              {
                "metricTrigger": {
                  "metricName": "Percentage CPU",
                  "metricNamespace": "",
                  "operator": "LessThan",
                  "threshold": 25,
                  "timeGrain": "PT1M",
                  "statistic": "Average",
                  "timeWindow": "PT5M",
                  "timeAggregation": "Average"
                },
                "scaleAction": {
                  "direction": "Decrease",
                  "type": "ChangeCount",
                  "value": "1",
                  "cooldown": "PT5M"
                }
              }
            ]
          }
        ]
      }
    }
    
    1. Deploy an Update with Custom Script Extension a. Azure Portal • In your VMSS blade > “Extensions” > “Add” > “Custom Script Extension.” • Provide the script URL (e.g., a blob SAS URL) and any arguments. • Save to rollout the update across all instances. b. Azure CLI
         az vmss extension set \
         --vmss-name MyScaleSet \
         --name CustomScript \
         --publisher Microsoft.Azure.Extensions \
         --version 2.1 \
         --settings '{"fileUris":["https://mystorage.blob.core.windows.net/scripts/update.sh"],"commandToExecute":"bash update.sh"}'
      
         
      

    Set-AzVmssExtension -ResourceGroupName RG -VMScaleSetName MyScaleSet -Name CustomScript -Publisher "Microsoft.Azure.Extensions" -Type "CustomScript" -TypeHandlerVersion "2.1" -SettingString '{"fileUris":["https://.../update.sh"],"commandToExecute":"bash update.sh"}'

       
       d. ARM Template snippet
       
       ```json
       {
      "type": "Microsoft.Compute/virtualMachineScaleSets/extensions",
      "apiVersion": "2021-04-01",
      "name": "[concat(parameters('vmssName'), '/CustomScript')]",
      "properties": {
        "publisher": "Microsoft.Azure.Extensions",
        "type": "CustomScript",
        "typeHandlerVersion": "2.1",
        "settings": {
          "fileUris": ["https://mystorage.blob.core.windows.net/scripts/update.sh"],
          "commandToExecute": "bash update.sh"
        }
      }
    }
    

    References

    • VMSS overview & zones: https://docs.microsoft.com/azure/virtual-machine-scale-sets/overview

    • VMSS FAQ & troubleshooting: https://docs.microsoft.com/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-faq

    • Autoscale best practices: https://docs.microsoft.com/azure/monitoring-and-diagnostics/insights-autoscale-best-practices

    • Custom Script Extension for Windows/Linux: https://docs.microsoft.com/azure/virtual-machines/extensions/custom-script-windows

    • ARM template reference: https://docs.microsoft.com/azure/templates/microsoft.compute/2021-04-01/virtualmachinescalesets

    0 comments No comments

  2. Q&A Assist
    2026-05-05T09:28:48.7666667+00:00
    AI generated content. This question contains an answer created with AI from Q&A Assist. Learn more

    Below is an expert‑level, end‑to‑end outline using Azure Portal, Azure CLI, and ARM templates, based only on the provided context.


    1. Create and prepare the source VM (custom image base)

    Azure CLI

    1. Create a resource group for the source VM:
         az group create \
           --name myResourceGroup \
           --location eastus
      
    2. Create the source VM (Linux example):
         az vm create \
           --resource-group myResourceGroup \
           --name myVM \
           --image UbuntuLTS \
           --admin-username azureuser \
           --generate-ssh-keys
      
      The VM ID appears in the output; store it for later use.
    3. Sign in to the VM and install/configure the application and OS settings required for your solution.
    4. Decide whether this will be a generalized or specialized image:
      • For generalized images, deprovision the VM (Linux example) as described in Step 1: Deprovision the VM (referenced in context).
      • For specialized images, skip deprovisioning and keep machine‑specific data and user profiles.

    Azure Portal (conceptual steps)

    1. Create a VM in a resource group and region of choice.
    2. Install and configure application components.
    3. Optionally generalize the VM (Sysprep for Windows or waagent -deprovision for Linux) if a generalized image is desired.

    ARM template

    Use a standard Microsoft.Compute/virtualMachines resource to define the source VM. The template is not fully shown in context, but the VM is the basis for the image version created later.


    Azure CLI

    1. Create a resource group for the gallery:
         export REGION="eastus"
         export RANDOM_ID=$RANDOM
         export MY_GALLERY_RG_NAME="myGalleryRG$RANDOM_ID"
         export MY_GALLERY_NAME="myGallery$RANDOM_ID"
         
         az group create --name $MY_GALLERY_RG_NAME --location $REGION
      
    2. Create the image gallery:
         az sig create \
           --resource-group $MY_GALLERY_RG_NAME \
           --gallery-name $MY_GALLERY_NAME
      
    3. Create an image definition (specialized Linux, Trusted Launch example):
         az sig image-definition create \
           --resource-group $MY_GALLERY_RG_NAME \
           --gallery-name $MY_GALLERY_NAME \
           --gallery-image-definition myImageDefinition \
           --publisher Contoso \
           --offer MyOffer \
           --sku MySku \
           --os-type Linux \
           --os-state specialized \
           --hyper-v-generation V2 \
           --features SecurityType=TrustedLaunch
      
      The image definition ID is returned; store it for later.

    Azure Portal

    1. Create a Shared Image Gallery (Azure Compute Gallery) in the desired resource group and region.
    2. Create an Image definition under the gallery:
      • Choose OS type (Linux/Windows).
      • Choose OS state: generalized or specialized.
      • Configure security type (e.g., Trusted Launch) to match the source VM.

    ARM template

    Define:

    • Microsoft.Compute/galleries
    • Microsoft.Compute/galleries/images (image definition) with properties matching OS type, OS state, and security type (Trusted Launch if required).

    3. Create the image version from the source VM

    Azure CLI

    Assuming myVM is the source VM in myResourceGroup:

    export MY_VM_ID=$(az vm show \
      --resource-group myResourceGroup \
      --name myVM \
      --query id -o tsv)
    
    az sig image-version create \
      --resource-group $MY_GALLERY_RG_NAME \
      --gallery-name $MY_GALLERY_NAME \
      --gallery-image-definition myImageDefinition \
      --gallery-image-version 1.0.0 \
      --target-regions "southcentralus=1" "eastus=1" \
      --virtual-machine $MY_VM_ID
    

    Notes from context:

    • Version format: Major.Minor.Patch (e.g., 1.0.0).
    • Replication regions must include the region of the source VM.
    • Wait for the image version to finish building and replicating before using it again.

    Azure Portal

    1. In the gallery → image definition, create a new image version.
    2. Select the source VM, set version (e.g., 1.0.0), and choose replication regions.

    ARM template

    Define Microsoft.Compute/galleries/images/versions referencing the source VM ID and specifying target regions and storage account type if needed.


    4. Create a VM Scale Set from the custom image with availability zones

    Azure CLI

    Use az vmss create with the image definition or specific image version ID.

    • Latest image version:
        export IMAGE_DEF_ID="/subscriptions/<subId>/resourceGroups/$MY_GALLERY_RG_NAME/providers/Microsoft.Compute/galleries/$MY_GALLERY_NAME/images/myImageDefinition"
        
        az vmss create \
          --resource-group myScaleSetRG \
          --name myScaleSet \
          --image $IMAGE_DEF_ID \
          --specialized \
          --orchestration-mode Uniform \
          --instance-count 3 \
          --zones 1 2 3 \
          --platform-fault-domain-count 1 \
          --admin-username azureuser \
          --vm-sku Standard_DS1_v2
      
    • Specific image version:
        export IMAGE_VER_ID="/subscriptions/<subId>/resourceGroups/$MY_GALLERY_RG_NAME/providers/Microsoft.Compute/galleries/$MY_GALLERY_NAME/images/myImageDefinition/versions/1.0.0"
        
        az vmss create \
          --resource-group myScaleSetRG \
          --name myScaleSet \
          --image $IMAGE_VER_ID \
          --specialized \
          --orchestration-mode Uniform \
          --instance-count 3 \
          --zones 1 2 3 \
          --platform-fault-domain-count 1 \
          --admin-username azureuser \
          --vm-sku Standard_DS1_v2
      

    Azure Portal

    1. Create a Virtual machine scale set.
    2. For Image, choose Shared image gallery and select the custom image definition or specific version.
    3. Enable Availability zones and select zones (e.g., 1, 2, 3).
    4. Configure instance size, instance count, and networking.

    ARM template

    Define Microsoft.Compute/virtualMachineScaleSets:

    • virtualMachineProfile.storageProfile.imageReference.id set to the image definition ID (latest) or image version ID (pinned).
    • zones array on the scale set resource to distribute instances across availability zones.

    5. Configure autoscale for the scale set

    Azure Portal

    1. Open the scale set → Scaling.
    2. Enable Custom autoscale.
    3. Configure rules (e.g., scale out when CPU > 70% for 10 minutes; scale in when CPU < 30% for 10 minutes).

    Azure CLI

    While autoscale commands are not explicitly shown in the context, autoscale is conceptually configured against the VMSS resource using Azure Monitor autoscale settings.

    ARM template

    Add an Microsoft.Insights/autoscaleSettings resource targeting the VMSS resource ID, defining profiles and rules for scale out/in.


    6. Deploy an update to the scale set using Custom Script Extension

    Concept from context

    The tutorial on installing applications in VM scale sets uses the Azure Custom Script Extension to:

    • Automatically install applications on each VM instance after deployment.
    • Update a running application on a scale set.

    Azure CLI

    1. Create a script (for example, update-app.sh) in a storage account or accessible URI.
    2. Apply or update the Custom Script Extension on the scale set (pattern based on the tutorial):
         az vmss extension set \
           --resource-group myScaleSetRG \
           --vmss-name myScaleSet \
           --name CustomScript \
           --publisher Microsoft.Azure.Extensions \
           --settings '{"fileUris":["https://<storage>/scripts/update-app.sh"],"commandToExecute":"bash update-app.sh"}'
      
      This runs the script on all instances, updating the application in place.

    Azure Portal

    1. Go to the scale set → Extensions + applications.
    2. Add or update the Custom Script extension.
    3. Provide the script URI and command to execute.

    ARM template

    Add or update virtualMachineProfile.extensionProfile.extensions in the VMSS resource with a CustomScript extension definition, including fileUris and commandToExecute in settings.


    Azure CLI

    1. Get the gallery object ID:
         export MY_GALLERY_ID=$(az sig show \
           --resource-group $MY_GALLERY_RG_NAME \
           --gallery-name $MY_GALLERY_NAME \
           --query "id" --output tsv)
         
         export CALLER_ID=$(az ad signed-in-user show --query id -o tsv)
      
    2. Assign Reader role at gallery scope:
         az role assignment create \
           --role "Reader" \
           --assignee $CALLER_ID \
           --scope $MY_GALLERY_ID
      

    Users with read access to the image version can deploy VMs or VMSS from it, even across subscriptions.


    8. Clean up resources

    Azure CLI

    Delete the resource groups when finished:

    az group delete --name myScaleSetRG --yes --no-wait
    az group delete --name $MY_GALLERY_RG_NAME --yes --no-wait
    az group delete --name myResourceGroup --yes --no-wait
    

    This removes the scale set, gallery, image versions, and source VM.


    References:

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.