Tutorial: Create Windows VM images with Azure PowerShell

Applies to: ✔️ Windows VMs ✔️ Flexible scale sets

Images can be used to bootstrap deployments and ensure consistency across multiple VMs. In this tutorial, you create your own specialized image of an Azure virtual machine using PowerShell and store it in an Azure Compute Gallery (formerly known as Shared Image Gallery). You learn how to:

  • Create an Azure Compute Gallery
  • Create an image definition
  • Create an image version
  • Create a VM from an image
  • Share a gallery

Before you begin

The steps below detail how to take an existing VM and turn it into a re-usable custom image that you can use to create new VMs.

To complete the example in this tutorial, you must have an existing virtual machine. If needed, you can see the PowerShell quickstart to create a VM to use for this tutorial. When working through the tutorial, replace the resource names where needed.


an Azure Compute Gallery simplifies custom image sharing across your organization. Custom images are like marketplace images, but you create them yourself. Custom images can be used to bootstrap configurations such as preloading applications, application configurations, and other OS configurations.

The Azure Compute Gallery lets you share your custom VM images with others. Choose which images you want to share, which regions you want to make them available in, and who you want to share them with.

The Azure Compute Gallery feature has multiple resource types:

Resource Description
Image source This is a resource that can be used to create an image version in a gallery. An image source can be an existing Azure VM that is either generalized or specialized, a managed image, a snapshot, or an image version in another gallery.
Gallery Like the Azure Marketplace, a gallery is a repository for managing and sharing images and VM applications, but you control who has access.
Image definition Image definitions are created within a gallery and carry information about the image and requirements for using it internally. This includes whether the image is Windows or Linux, release notes, and minimum and maximum memory requirements. It is a definition of a type of image.
Image version An image version is what you use to create a VM when using a gallery. You can have multiple versions of an image as needed for your environment. Like a managed image, when you use an image version to create a VM, the image version is used to create new disks for the VM. Image versions can be used multiple times.

Launch Azure PowerShell

Open the Azure Powershell console and run the following scripts with Administrator priviledge.

Get the VM

You can see a list of VMs that are available in a resource group using Get-AzVM. Once you know the VM name and what resource group, you can use Get-AzVM again to get the VM object and store it in a variable to use later. This example gets a VM named sourceVM from the myResourceGroup resource group and assigns it to the variable $sourceVM.

$sourceVM = Get-AzVM `
   -Name sourceVM `
   -ResourceGroupName myResourceGroup

Create a resource group

Create a resource group with the New-AzResourceGroup command.

An Azure resource group is a logical container into which Azure resources are deployed and managed. In the following example, a resource group named myGalleryRG is created in the ChinaEast region:

$resourceGroup = New-AzResourceGroup `
   -Name 'myGalleryRG' `
   -Location 'ChinaEast'

A gallery is the primary resource used for enabling image sharing. Allowed characters for gallery name are uppercase or lowercase letters, digits, dots, and periods. The gallery name cannot contain dashes. Gallery names must be unique within your subscription.

Create a gallery using New-AzGallery. The following example creates a gallery named myGallery in the myGalleryRG resource group.

$gallery = New-AzGallery `
   -GalleryName 'myGallery' `
   -ResourceGroupName $resourceGroup.ResourceGroupName `
   -Location $resourceGroup.Location `
   -Description 'Azure Compute Gallery for my organization'	

Create an image definition

Image definitions create a logical grouping for images. They are used to manage information about the image versions that are created within them. Image definition names can be made up of uppercase or lowercase letters, digits, dots, dashes and periods. For more information about the values you can specify for an image definition, see Image definitions.

Create the image definition using New-AzGalleryImageDefinition. In this example, the gallery image is named myGalleryImage and is created for a specialized image.

$galleryImage = New-AzGalleryImageDefinition `
   -GalleryName $gallery.Name `
   -ResourceGroupName $resourceGroup.ResourceGroupName `
   -Location $gallery.Location `
   -Name 'myImageDefinition' `
   -OsState specialized `
   -OsType Windows `
   -Publisher 'myPublisher' `
   -Offer 'myOffer' `
   -Sku 'mySKU'

Create an image version

Create an image version from a VM using New-AzGalleryImageVersion.

Allowed characters for image version are numbers and periods. Numbers must be within the range of a 32-bit integer. Format: MajorVersion.MinorVersion.Patch.

In this example, the image version is 1.0.0 and it's replicated to both China East and China North datacenters. When choosing target regions for replication, you need to include the source region as a target for replication.

To create an image version from the VM, use $vm.Id.ToString() for the -Source.

   $region1 = @{Name='China East';ReplicaCount=1}
   $region2 = @{Name='China North';ReplicaCount=2}
   $targetRegions = @($region1,$region2)

New-AzGalleryImageVersion `
   -GalleryImageDefinitionName $galleryImage.Name`
   -GalleryImageVersionName '1.0.0' `
   -GalleryName $gallery.Name `
   -ResourceGroupName $resourceGroup.ResourceGroupName `
   -Location $resourceGroup.Location `
   -TargetRegion $targetRegions  `
   -Source $sourceVM.Id.ToString() `
   -PublishingProfileEndOfLifeDate '2030-12-01'

It can take a while to replicate the image to all of the target regions.

Create a VM

Once you have a specialized image, you can create one or more new VMs. Using the New-AzVM cmdlet. To use the image, use Set-AzVMSourceImage and set the -Id to the image definition ID ($galleryImage.Id in this case) to always use the latest image version.

Replace resource names as needed in this example.

# Create some variables for the new VM.
$resourceGroup = "myResourceGroup"
$location = "China North"
$vmName = "mySpecializedVM"

# Create a resource group
New-AzResourceGroup -Name $resourceGroup -Location $location

# Create the network resources.
$subnetConfig = New-AzVirtualNetworkSubnetConfig -Name mySubnet -AddressPrefix
$vnet = New-AzVirtualNetwork -ResourceGroupName $resourceGroup -Location $location `
  -Name MYvNET -AddressPrefix -Subnet $subnetConfig
$pip = New-AzPublicIpAddress -ResourceGroupName $resourceGroup -Location $location `
  -Name "mypublicdns$(Get-Random)" -AllocationMethod Static -IdleTimeoutInMinutes 4
$nsgRuleRDP = New-AzNetworkSecurityRuleConfig -Name myNetworkSecurityGroupRuleRDP  -Protocol Tcp `
  -Direction Inbound -Priority 1000 -SourceAddressPrefix * -SourcePortRange * -DestinationAddressPrefix * `
  -DestinationPortRange 3389 -Access Deny
$nsg = New-AzNetworkSecurityGroup -ResourceGroupName $resourceGroup -Location $location `
  -Name myNetworkSecurityGroup -SecurityRules $nsgRuleRDP
$nic = New-AzNetworkInterface -Name $vmName -ResourceGroupName $resourceGroup -Location $location `
  -SubnetId $vnet.Subnets[0].Id -PublicIpAddressId $pip.Id -NetworkSecurityGroupId $nsg.Id

# Create a virtual machine configuration using $imageVersion.Id to specify the image version.
$vmConfig = New-AzVMConfig -VMName $vmName -VMSize Standard_D1_v2 | `
Set-AzVMSourceImage -Id $galleryImage.Id | `
Add-AzVMNetworkInterface -Id $nic.Id

# Create a virtual machine
New-AzVM -ResourceGroupName $resourceGroup -Location $location -VM $vmConfig

We recommend that you share access at the gallery level. Use an email address and the Get-AzADUser cmdlet to get the object ID for the user, then use New-AzRoleAssignment to give them access to the gallery. Replace the example email, alinne_montes@contoso.com in this example, with your own information.

# Get the object ID for the user
$user = Get-AzADUser -StartsWith alinne_montes@contoso.com
# Grant access to the user for our gallery
New-AzRoleAssignment `
   -ObjectId $user.Id `
   -RoleDefinitionName Reader `
   -ResourceName $gallery.Name `
   -ResourceType Microsoft.Compute/galleries `
   -ResourceGroupName $resourceGroup.ResourceGroupName

Clean up resources

When no longer needed, you can use the Remove-AzResourceGroup cmdlet to remove the resource group, and all related resources:

# Delete the gallery 
Remove-AzResourceGroup -Name myGalleryRG

# Delete the VM
Remove-AzResourceGroup -Name myResoureceGroup

Azure Image Builder

Azure also offers a service, built on Packer, Azure VM Image Builder. Simply describe your customizations in a template, and it will handle the image creation.

Next steps

In this tutorial, you created a specialized VM image. You learned how to:

  • Create an Azure Compute Gallery
  • Create an image definition
  • Create an image version
  • Create a VM from an image
  • Share a gallery

Advance to the next tutorial to learn about Virtual Machine Scale Sets.