Provisioning Virtual Machines (VMs) in Azure

Posted by: Brian Martel , on 1/12/2023, in Category Microsoft Azure
Views: 17350
Abstract: This tutorial discusses the provisioning of virtual machines (VMs) in Microsoft Azure using PowerShell, Azure CLI, and Azure Resource Manager (ARM) templates. It demonstrates the process of creating VMs with custom configurations and explains how to manage VM resources through scripting and ARM templates.

Provisioning VMs in Azure is an important task for cloud administrators and developers. In this tutorial, we will explore three methods for creating and managing VMs: using PowerShell cmdlets, Azure Command-Line Interface (CLI), and Azure Resource Manager (ARM) templates.

We will cover the process of finding image URNs, creating VMs with custom configurations, and managing resources using scripting and ARM templates.

Note: In this tutorial, I have attempted to present an easy-to-understand guide that dives deep into the process of provisioning virtual machines (VMs) in Azure. While this guide does not contain everything related to provisioning VMs in Azure, it does provide a good starting point. We will revisit and cover additional topics in the forthcoming tutorials.


[Img Source:]

1. Availability and Accessibility

Infrastructure Redundancy Options

Azure offers various options for infrastructure redundancy to ensure high availability and disaster recovery for your VMs. These options include Availability Sets, Availability Zones, and Paired Regions.

Azure VM Availability Options

Availability Sets

An Availability Set is a logical grouping of VMs that allows Azure to distribute VM instances across multiple fault domains (FDs) and update domains (UDs). This arrangement provides redundancy and minimizes downtime during maintenance or unexpected failures.

  • Fault domains: A fault domain is a group of VMs that share the same physical hardware, network switches, and power sources. Here by distributing VMs across different FDs, Azure ensures that if one FD fails, the other FDs can continue operating.
  • Update domains: An update domain is a group of VMs that can be updated and rebooted simultaneously. Sseparating VMs into different UDs ensures that at least one UD is always available during planned maintenance.
Availability Zones

Availability Zones are physically separate locations within a region, each consisting of one or more datacenters with independent power, cooling, and networking. Distributing VMs across Availability Zones ensures high availability and resiliency in case of a complete datacenter failure.

Paired Regions

Paired Regions consist of two regions within the same geography that are directly connected through Azure’s private network. Deploying VMs in paired regions ensures that data and applications are resilient to large-scale disasters, such as natural disasters or power outages.

Virtual Machine Scale Sets (VMSS)

VMSS allow you to create and manage multiple VM instances that can automatically scale based on demand or a predefined schedule. This feature enables applications to handle increased load without manual intervention, ensuring optimal performance and cost-efficiency.

2. Storage Options

Disk Types

Azure broadly offers three types of disks for VM storage:

Standard HDD

Standard HDDs are low-cost magnetic drives suitable for workloads with moderate performance requirements, such as file servers and small databases. They offer the lowest cost per gigabyte, but have higher latency and lower IOPS than SSDs.

Standard SSD

Standard SSDs are cost-effective solid-state drives with better performance and reliability compared to HDDs. They are suitable for most enterprise workloads, such as web servers, application servers, and development environments.

Premium SSD

Premium SSDs provide high-performance, low-latency storage for demanding workloads requiring high input/output operations per second (IOPS) and low-latency, such as large databases and high-performance computing (HPC) applications.

Disk Encryption

Azure Disk Encryption (ADE) uses industry-standard encryption technologies, such as BitLocker for Windows and dm-crypt for Linux, to protect data. ADE leverages Azure Key Vault to store encryption keys and secrets, ensuring that your data remains secure and compliant with industry standards.

3. Limits

Subscription Limits

At the time of writing, there is a default limit of 25,000 VMs per subscription per region and 20 virtual Central Processing Units (vCPUs) per VM. These limits apply to both the total number of VMs and the number of vCPUs used across all VMs within a single subscription.

Requesting Limit Increases

If you need to exceed the default limits, you can request an increase by contacting Azure Support. The increase may be granted depending on your requirements and Azure’s capacity. Keep in mind that approval is not guaranteed, so it’s essential to plan your VM deployment accordingly.

4. Geographic Regions

Azure has numerous datacenters worldwide, grouped into regions. You can select a region based on latency, redundancy, and compliance requirements. It’s crucial to choose a region with the necessary services and features to support your desired VM deployment.

5. Naming Conventions

A VM’s name must be unique within its resource group and comply with Azure naming conventions. The name should be descriptive and meaningful, following your organization’s naming standards. It’s essential to use a consistent naming scheme to simplify management and avoid confusion.

6. OS Image Selection

Azure provides a marketplace with various operating system images, including Windows Server, Ubuntu, CentOS, and more. Users can also upload custom images or create images from existing VMs. To retrieve the list of available images per region, you can use the Az PowerShell module or the Azure CLI.

Using Az PowerShell Module

To get a list of available images using the Az PowerShell module, run the following command: 

Get-AzVMImagePublisher -Location <Region> | Get-AzVMImageOffer | Get-AzVMImageSku

Replace <Region> with the desired Azure region.

Using Azure CLI

To get a list of available images using the Azure CLI, run the following command:

az vm image list --all --location <Region> --output table

Replace <Region> with the desired Azure region.

7. Post-Deployment Setup


Extensions are small applications that can be installed on VMs to configure, manage, or monitor the VM.

Examples of extensions include the Azure Diagnostics extension for monitoring and the Custom Script extension for executing scripts on VMs. Extensions can be installed using the Azure Portal, PowerShell, or the Azure CLI.

Virtual Machine Agents

Azure Virtual Machine agents are lightweight processes that run on VMs and facilitate communication between the VM and the Azure platform. These Agents enable various features, such as VM extensions, automatic OS updates, and remote management.

Agents are installed by default on Azure Marketplace images but can also be installed manually on custom images.

8. VM Specifications

Azure offers a wide range of VM sizes, optimized for different workloads and performance requirements. VM sizes are grouped into families, such as General Purpose, Compute Optimized, Memory Optimized, Storage Optimized, and GPU.

It’s crucial to choose the appropriate VM size based on your workload’s resource requirements, such as CPU, memory, storage, and network bandwidth. You can resize your VMs later if your needs change, but selecting the right size from the start can help optimize performance and cost.

9. Pricing Model

Azure offers several pricing models for VMs, including Pay-As-You-Go, Reserved Instances, and Spot Instances.


Pay-As-You-Go is the default pricing model, where you pay for VMs based on their usage. VMs are billed per second, with different rates depending on the VM size, region, and operating system. This pricing model offers flexibility and is suitable for workloads with variable or unpredictable resource requirements.

Reserved Instances

Reserved Instances (RIs) allow you to reserve VM capacity in a specific region for one or three years, in exchange for a significant discount compared to Pay-As-You-Go rates. RIs are suitable for workloads with stable and predictable resource requirements.

You can optimize costs and ensure capacity availability by committing to a longer-term reservation.

Spot Instances

Spot Instances allow you to bid on unused Azure capacity at a significant discount compared to Pay-As-You-Go rates. However, Spot Instances can be evicted at any time if Azure needs the capacity or if the current price exceeds your maximum bid. This pricing model is suitable for workloads that can tolerate interruptions and have flexible start and end times.

10. Creating a VM

To create a VM in Azure, you can use the Azure Portal, Azure PowerShell, Azure CLI, or ARM templates.

Azure Portal

The Azure Portal provides a graphical interface for creating and managing VMs. Follow these steps to create a VM using the Azure Portal:

  1. Sign in to the Azure Portal.
  2. Click the “Create a resource” button in the left-hand menu.
  3. Search for “Virtual Machine” and select it from the list of results.
  4. Click the “Create” button to start the VM creation process.
  5. Fill in the required fields, such as subscription, resource group, name, region, operating system image, and size.
  6. Configure optional settings, such as availability options, networking, and management.
  7. Review the configuration and click the “Create” button to deploy the VM.

Azure PowerShell

To create a VM using Azure PowerShell, you can use the New-AzVM cmdlet. Here’s an example command to create a VM with the specified parameters:

New-AzVM `
    -ResourceGroupName "DotNetCurryResourceGroup" `
    -Name "DotNetCurryVM" `
    -Location "East US" `
    -VirtualNetworkName "DNCVnet" `
    -SubnetName "DNCSubnet" `
    -SecurityGroupName "DNCNetworkSecurityGroup" `
    -PublicIpAddressName "DNCPublicIpAddress" `
    -OpenPorts 8080,3389 `
    -Image "MicrosoftWindowsServer:WindowsServer:2022-Datacenter:latest" `
    -Size "Standard_B2s" `
    -Credential (Get-Credential) `
    -Zone 1

Azure CLI

To create a VM using the Azure CLI, you can use the az vm create command. Here’s an example command to create a VM with the specified parameters:

az vm create \
    --resource-group DotNetCurryResourceGroup \
    --name DotNetCurryVM \
    --location eastus2 \
    --vnet-name DNCVnet \
    --subnet DNCSubnet \
    --nsg DNCNetworkSecurityGroup \
    --public-ip-address DNCPublicIpAddress \
    --image MicrosoftWindowsServer:WindowsServer:2022-Datacenter:latest \
    --size Standard_B2s \
    --admin-username customusername \
    --admin-password custompassword \
    --zones 1

ARM Templates

ARM templates are JSON files that define the resources and configurations for your Azure deployment. You can create a VM using an ARM template by following these steps:

1. Create a JSON file with the necessary resources and configurations for the VM, such as the virtual network, subnet, network security group, public IP address, and storage account.

2. Deploy the ARM template using the Azure Portal, Azure PowerShell, Azure CLI, or REST APIs.

Here’s an example of an ARM template to create a VM:

  "$schema": "",
  "contentVersion": "",
  "parameters": {
    "vmName": {
      "type": "string",
      "metadata": {
        "description": "Name of the virtual machine"
    "location": {
      "type": "string",
      "metadata": {
        "description": "Azure region for the VM"
    "adminUsername": {
      "type": "string",
      "metadata": {
        "description": "Username for the VM"
    "adminPassword": {
      "type": "securestring",
      "metadata": {
        "description": "Password for the VM"
    "vmSize": {
      "type": "string",
      "metadata": {
        "description": "Size of the VM"
  "resources": [
      "type": "Microsoft.Compute/virtualMachines",
      "apiVersion": "2019-07-01",
      "name": "[parameters('vmName')]",
      "location": "[parameters('location')]",
      "properties": {
        "hardwareProfile": {
          "vmSize": "[parameters('vmSize')]"
        "storageProfile": {
          "imageReference": {
            "publisher": "MicrosoftWindowsServer",
            "offer": "WindowsServer",
            "sku": "2022-Datacenter",
            "version": "latest"
        "osProfile": {
          "computerName": "[parameters('vmName')]",
          "adminUsername": "[parameters('adminUsername')]",
          "adminPassword": "[parameters('adminPassword')]"
        "networkProfile": {
          "networkInterfaces": [
              "id": "[resourceId('Microsoft.Network/networkInterfaces', concat(parameters('vmName'), 'NIC'))]"
        "diagnosticsProfile": {
          "bootDiagnostics": {
            "enabled": true,
            "storageUri": "[concat('https://', parameters('vmName'), '')]"

To deploy the updated ARM template using Azure PowerShell, save the template to a JSON file (e.g., azuredeploy.json) and use the New-AzResourceGroupDeployment cmdlet:

New-AzResourceGroupDeployment `
    -ResourceGroupName "MyResourceGroup" `
    -TemplateFile "path/to/template.json" `
    -vmName "MyVM" `
    -location "East US" `
    -adminUsername "myusername" `
    -adminPassword (ConvertTo-SecureString "mypassword" -AsPlainText -Force)

To deploy the updated ARM template using the Azure CLI, save the template to a JSON file (e.g., azuredeploy.json) and use the az deployment group create command:

az deployment group create \
    --resource-group MyResourceGroup \
    --template-file path/to/template.json \
    --parameters vmName=MyVM location=eastus adminUsername=myusername adminPassword=mypassword

11. Image URNs

An Image URN (Uniform Resource Name) is a unique identifier for an Azure Marketplace image. The URN format is: <publisher>:<offer>:<sku>:<version>. You can use an Image URN to specify the operating system image when creating a VM using Azure PowerShell, Azure CLI, or ARM templates.

Image URNs are essential for creating VMs in Azure, as they uniquely identify the operating system image you want to use for the VM. These URNs are available in the Azure Marketplace or can be user-generated for custom images.

To create a VM using a specific image, you can use an Image URN when deploying through Azure PowerShell, Azure CLI, or ARM templates.

Here are some of the key points about Image URNs:

  1. An Image URN is composed of four segments: Publisher, Offer, SKU, and Version. Example: MicrosoftWindowsServer:WindowsServer:2019-Datacenter:latest.
  2. You can use Image URNs for both Microsoft-provided images and custom images.
  3. To find the Image URN for a specific image, use the Get-AzVMImageSku command in Az PowerShell or az vm image list command in Azure CLI.
  4. Image URNs can also be used to find the available versions of an image in a specific region.
  5. You can use the –all flag with the az vm image list command to list all available images in the Azure Marketplace.
  6. The Version segment of an Image URN can be set to latest to use the most recent version of the image.
  7. You can create a VM with an Image URN using the New-AzVM command in Az PowerShell, az vm create command in Azure CLI, or by specifying the imageReference property in an ARM template.
  8. To create a VM using a custom image, upload the image to a storage account and use the image’s resource ID as the Image URN.
  9. You can create a custom image from an existing VM using the New-AzImage command in Az PowerShell or az image create command in Azure CLI.
  10. To find the Image URN for a custom image, use the Get-AzImage command in Az PowerShell or az image list command in Azure CLI.
  11. You can use the –output table flag with the az vm image list command to display the results in a tabular format for easier reading.
  12. It’s essential to choose an Image URN that is compatible with your desired VM size and location.
  13. Some images have additional licensing fees, which will be billed separately from the VM usage costs.
  14. Regularly check for updates to the images you use, as new versions may include important security patches or performance improvements.

Once you have the Image URNs for the desired operating system images, you can create VMs in Azure using different tools like Azure PowerShell, Azure CLI, or ARM templates. The syntax for creating a VM using Image URNs can vary depending on the tool you use, but the basic process remains the same.

Azure PowerShell

To create a VM with an Image URN using Azure PowerShell, you can use the New-AzVM command with the following syntax:

New-AzVM -ResourceGroupName <ResourceGroupName> -Name <VMName> -Location <Location> -ImageName <ImageURN> -Credential <Credential>

Replace <ResourceGroupName>, <VMName>, <Location>, <ImageURN>, and <Credential> with your specific values.

Azure CLI

To create a VM with an Image URN using Azure CLI, you can use the az vm create command with the following syntax:

az vm create --resource-group <ResourceGroupName> --name <VMName> --location <Location> --image <ImageURN> --admin-username <Username> --admin-password <Password>

Replace <ResourceGroupName>, <VMName>, <Location>, <ImageURN>, <Username>, and <Password> with your specific values.

ARM Templates

To create a VM with an Image URN using ARM templates, you’ll need to specify the imageReference property in the template’s JSON file. The imageReference property should include the Publisher, Offer, SKU, and Version information from the Image URN. Here’s an example of the imageReference property in an ARM template:

"imageReference": {
  "publisher": "<Publisher>",
  "offer": "<Offer>",
  "sku": "<SKU>",
  "version": "<Version>"

Replace <Publisher>, <Offer>, <SKU>, and <Version> with the corresponding values from your Image URN.

To find the Windows Server 2022 Datacenter SKU using PowerShell (or the CLI, if you prefer), use the following command, substituting the location value with your own:

Get-AzVMImageSku -Location 'eastus' -PublisherName 'MicrosoftWindowsServer' -Offer 'WindowsServer'

For the Ubuntu version, use the following command:

Get-AzVMImageSku -Location "eastus" -PublisherName "Canonical" -Offer "UbuntuServer"

The URNs for the latest versions of these SKUs are: • Windows VM: MicrosoftWindowsServer:WindowsServer:2022-Datacenter:latest • Ubuntu VM: Canonical:UbuntuServer:20.04-LTS:latest

Now that we have our URNs, let’s create some VMs. For this example, we’ll use a cost-effective VM, Standard_B1s, which is available in the desired subscription and region.

Follow these steps to create VMs with the provided URNs using an authenticated PowerShell session (ensure the correct subscription context is set using Get-AzContext and Set-AzContext):

Step 1: Open Visual Studio Code (VS Code), navigate to a suitable location, create a new folder if necessary, and create a new file with the .ps1 file extension.

Step 2: Set up the variables we’ll use, customizing the names and location as needed:

$rgName      = "ResourceGroup-AZ"
$location    = "eastus"
$vmSize      = "Standard_B1s"
$winVmName   = "WinVM-EastUS-AZ"
$ubuVmName   = "UbuntuVM-EastUS-AZ"
$winURN      = "MicrosoftWindowsServer:WindowsServer:2022-Datacenter:latest"
$ubuURN      = "Canonical:UbuntuServer:20.04-LTS:latest"
$creds       = (Get-Credential -Message "Admin credentials for the VMs:")
$text        = "Hello, Azure World!"
$userData    = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($text))
$tag         = @{"website" = "dotnetcurry"}

The $creds variable uses Get-Credential to prompt for a username and password for both VMs. You can customize the $text variable with any string. $userData converts the value of $text into a Base64 string, as required by the user data property.

Step 3: Add code to create the resource group, Windows VM, and Ubuntu VM using the variables. Without adding any exception handling or anything else that you would usually add (for the sake of simplicity and readability), your script should look something like this:

# Variables
$rgName      = "ResourceGroup-AZ"
$location    = "eastus"
$vmSize      = "Standard_B1s"
$winVmName   = "WindowsVM-EastUS-AZ"
$ubuVmName   = "UbuntuVM-EastUS-AZ"
$winURN      = "MicrosoftWindowsServer:WindowsServer:2022-Datacenter:latest"
$ubuURN      = "Canonical:UbuntuServer:20.04-LTS:latest"
$creds       = (Get-Credential -Message "Admin credentials for the VMs:")
$text        = "Hello, Azure World!"
$userData    = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($text))
$tag         = @{"website" = "dotnetcurry"}

# Create resource group
New-AzResourceGroup -Name $rgName -Location $location -Tag $tag -Force

# Create Windows VM
New-AzVM -Name $winVmName -ResourceGroupName $rgName -Location $location -ImageName $winURN -Credential $creds -Size $vmSize -UserData $userData

# Create Ubuntu VM
New-AzVM -Name $ubuVmName -ResourceGroupName $rgName -Location $location -ImageName $ubuURN -Credential $creds -Size $vmSize -UserData $userData

Proceed to follow the steps outlined earlier to create and manage the VMs using this script.

Keep in mind that understanding basic commands such as the ones we’ve used so far is useful for the everyday admin. However, if you require complex orchestration, ARM templates are the more pragmatic way to deploy repeatable infrastructure as code, which we will explore in an upcoming article.

Now that you have created the script to provision the Windows and Ubuntu VMs using Image URNs, follow these steps to execute the script and manage the VMs:

Step 4: Open a new terminal window in Visual Studio Code (VS Code) and switch to a PowerShell terminal if it’s not your default.

Step 5: Ensure you are authenticated and have the correct context set to your desired subscription using the Get-AzContext command. If necessary, use Set-AzContext with the appropriate subscription and tenant information to change it.

Step 6: When you’re ready to run the script, make sure you’re in the correct directory and execute the script.

Step 7: When prompted due to the $creds variable using Get-Credential, input the desired admin username and password for the VMs.

Step 8: Once the script has finished, check the Azure portal. You should see your new resource group with the “dotnetcurry” tag and the new VMs, along with their related resources.

Step 9: Click on one of the newly created VMs, and under Settings, go to the Configuration blade. Scroll down, and you can see our text in the User data field.

Step 10: Feel free to make changes to the $tag hashtable (e.g., capitalize the ‘C’ in ‘chapter’) and rerun your script. The script should complete much faster as ARM can see the resources already exist, and the changes don’t require redeployment. Refresh your browser page and go back to the resource group to see the change in the tag.

Step 11: Verify the image that was used by using the following command:

Get-AzVM -ResourceGroupName "ResourceGroup-AZ" -Name "UbuntuVM-EastUS-AZ").StorageProfile.ImageReference

You can do the same using the Windows VM name and see that they have indeed used the image we specified with the URN.

Step 12: To clean up the resources you created, delete the resource group and all the resources deployed with the following command:

Remove-AzResourceGroup -Name "ResourceGroup-AZ"

You will be prompted before the deletion carries out, as we didn’t use -Force. This will take a few moments.


Provisioning VMs in Azure involves several considerations, such as availability, disks, limits, location, naming, operating system image, post-provision configuration, size, pricing model, and more. Understanding these aspects will help you create and manage your VMs effectively, optimizing performance and cost.

By leveraging various tools like the Azure Portal, Azure PowerShell, Azure CLI, and ARM templates, you can create and configure VMs tailored to your specific requirements. Furthermore, understanding Azure’s pricing models and availability options will enable you to make informed decisions for your workloads and deployments.

I hope this comprehensive guide will get you started with creating VMs in Azure. In an upcoming tutorial, we will do a deep dive on ARM templates.

This article has been editorially reviewed by Suprotim Agarwal.

Absolutely Awesome Book on C# and .NET

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.

We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).

Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.

Click here to Explore the Table of Contents or Download Sample Chapters!

What Others Are Reading!
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+


Brian Martel, an experienced Azure and DevOps developer, has spent the last decade mastering technologies such as Kubernetes, Docker, Ansible, and Terraform. Armed with a Bachelor's degree in Computer Science and certifications like Cloud DevOps Engineer Expert (AWS and Azure) and Certified Kubernetes Administrator (CKA), Brian has a proven track record of guiding organizations through successful transitions to cloud-based infrastructures and implementing efficient DevOps pipelines.

He generously shares his wealth of knowledge as a mentor and an active participant in the developer community, contributing articles, speaking at user groups, and engaging with others on social media. All the while, Brian remains dedicated to staying current with the latest trends in his field.

Page copy protected against web site content infringement 	by Copyscape

Feedback - Leave us some adulation, criticism and everything in between!