VM Image PowerShell ‘How To’ Blog Post

Editors Note: This post was written with Kay Singh, Program Manager, Microsoft Azure Engineering

As promised in my first blog post, I am back to walk you through how to work with VM Images in PowerShell.  I have asked my colleague, Kay, PowerShell PM responsible for the compute cmdlets, to join me in explaining some of the details.  Hopefully, you have had a chance to play around with VM Images a bit and have been anticipating this blog post for a few days now.  Okay, okay… I may be getting cared away with my excitement here.

As I touched upon earlier, VM Images are the successor to OS Images and the two entities have many similarities.  As the VM Image feature develops, you will be able to think of it as encapsulating the OS Image.  While we discuss VM Images and OS Images as two separate entities, PowerShell works with a single ‘über’ image entity.  Consequently, many of the PowerShell cmdlets that you are familiar with when using OS Images, will apply to VM Images.

To get started, let’s briefly tap into your OS Image PowerShell knowledge.  As you may recall, to create, list, and delete OS Images, the following PowerShell snippets can be used:

Save-AzureVMImage –ServiceName "myServiceName" –Name "myVMtoCapture" –ImageName "myAwesomeOSImage" –ImageLabel "This is my OS Image"

Get-AzureVMImage

Remove-AzureVMImage –ImageName "myAwesomeOSImage"

You can deploy a VM from your newly created OS Image, with the following PowerShell snippets:

New-AzureQuickVM –Windows –Location "West US" –ServiceName "MySvc1" –Name "myVM1" –InstanceSize "Medium" –ImageName "myAwesomeOSImage" –AdminUsername "admin"–Password "adminPassword123" -WaitForBoot

Or,

New-AzureVMConfig -Name "myVM1" -InstanceSize "Medium" -ImageName "myAwesomeOSImage" |

Add-AzureProvisioningConfig -Windows -AdminUsername "admin" -Password "adminPassword123" |

New-AzureVM –ServiceName "MySvc1" -WaitForBoot

Hopefully, everything so far is a refresher and we can start diving into VM Images.

Creating a VM to Capture

Before we get started on the VM Image specific pieces, we need a virtual machine to work with.  Let’s use what we described above to deploy a virtual machine running SQL Server 2014 with two data disks.  The script for creating a virtual machine with the April release of SQL Server 2014 RTM DataWarehousing is as follows:

$img = Get-AzureVMImage -ImageName "fb83b3509582419d99629ce476bcb5c8__SQL-Server-2014RTM-12.0.2000.8-DataWarehousing-ENU-WS2012R2-AprilGA"

$user = "admin"
$pass = "adminPassword123"

$myVM = New-AzureVMConfig -Name "myVMToCapture2" -InstanceSize "Large" -ImageName $img.ImageName `
        | Add-AzureProvisioningConfig -Windows -AdminUsername $user -Password $pass `
        | Add-AzureDataDisk -CreateNew -DiskSizeInGB 10 -DiskLabel "DataDisk1" -LUN 0 `
        | Add-AzureDataDisk -CreateNew -DiskSizeInGB 10 -DiskLabel "DataDisk2" -LUN 1

New-AzureVM -ServiceName "MySvc2" -VMs $myVM -Location "West US" -WaitForBoot

Capturing a VM as a Generalized or Specialized VM Image

Before capturing the virtual machine, you have to decide on whether you want a generalized or specialized capture.  As mentioned in the previous blog post, generalized VM Images are meant to be used as a “model” to quickly stamp out similar virtual machines.  Similar functionality is available for OS images.  Specialized VM Images are new and are meant to be used as a “snapshot” to deploy a VM to a good known point in time before performing a task which may go wrong and render the virtual machine useless.

You’ll need to do a few extra steps to prepare the virtual machine for capturing.  To capture a generalized Windows Server VM Image, you can follow the steps described in the Microsoft Azure article How to Capture an Image of a Virtual Machine Running Windows Server.  To capture a Linux VM Image, you can follow a similar article – How to Capture an Image of a Virtual Machine Running Linux.

Now, on to actually capturing the virtual machine as a VM Image. The capturing process is similar to that of OS Images. You will need to use the existing Save-AzureVMImage cmdlet, but include new parameters, which determine whether to capture an OS Image or VM Image.

Let’s capture a generalized VM Image first, calling it myFirstVMImage.

Save-AzureVMImage –ServiceName "mySvc2" –Name "MyVMToCapture2" –ImageName "myFirstVMImage" –OSState "Generalized" -Verbose

If instead you would like to capture a specialized VM Image, you could use the following snippet:

Save-AzureVMImage –ServiceName "mySvc2" –Name "MyVMToCapture2" –ImageName "myFirstVMImageSP" –OSState "Specialized" -Verbose

Note, the use of the OSState parameter specifies your intent to capture a VM Image.  Without the use of this parameter, this cmdlet will capture an OS Image.  Since an OS Image is always assumed to be ‘generalized’, the OSState parameter is not needed.

One tip, we highly recommend that you never create two images (VM Image and OS Image) with the same name.  We do allow this in the Microsoft Azure Platform, but you may run into issues, since the platform cannot determine your intent.

You may be wondering how duplicates can arise.  Duplicate names can happen if you create a VM Image from one client (say PowerShell) and then an OS Image from another (say the Portal).  If you were to use PowerShell exclusively for creating OS Images and VM Images, the Save-AzureVMImage cmdlet validates naming and will throw an error if you create a VM Image and an OS Image with the same name.

List VM Images

As previously recapped, using the Get-AzureVMImage cmdlet will list out both user and published OS images.  But wait!  If we run the cmdlet now, we will actually see the VM Image we just captured.  Try the following PowerShell snippet, if you do not believe us!

Get-AzureVMImage | Select -Property Label, ImageName | Format-Table -Wrap

The existing Get-AzureVMImage cmdlet returns not only the OS images, but also user and published VM Images.  To list only VM Images, you can filter on a property that only applies to VM Images, like the DataDiskConfiguration.

Get-AzureVMImage | where {(gm –InputObject $_ -Name DataDiskConfigurations) -ne $null} | Select -Property Label, ImageName

Deleting VM Images

Deleting a VM Image is the same as deleting an OS Image.

Remove-AzureVMImage –ImageName $img –DeleteVHD;

Remember us mentioning that the platform is unable to determine intent when there are two images with the same name?  The above cmdlet will error in this situation to avoid deleting the wrong image.  If you want to delete the OS Image, you will need to use either the Portal or Invoke-Rest URI method from PowerShell to call the REST API directly.  To delete the VM Image, you can use the Invoke-Rest URI method from PowerShell as well.  Once one of the images is deleted, you can use the Remove-AzureVMImage cmdlet again.  Just in case you do get into this situation, here’s the PowerShell snippet to delete a VM Image using Invoke-RestMethod cmdlet:

# Delete VM Image API Documentation - http://msdn.microsoft.com/en-us/library/azure/dn499769.aspx 
#-------------------------------------------------------------------------------------#
# Method | Request URI 
#-------------------------------------------------------------------------------------#
# DELETE | https://management.core.windows.net/<subscription-id>/services/vmimages/<image-name>?comp=media 
#-------------------------------------------------------------------------------------# 

$cert = (Get-AzureSubscription).Certificate.Thumbprint 
$subid =  (Get-AzureSubscription).SubscriptionId 
$vmimgname = ‘myFirstVMImage’
$uri = "https://management.core.windows.net/$subid/services/vmimages/$vmimgname" + "?comp=media" 

$header = @{"x-ms-version" = "2014-02-01";}

Invoke-RestMethod -Uri $uri -Method Delete -ContentType "application/xml" -Headers $header -CertificateThumbprint $cert

Deploying a VM from a Generalized or Specialized VM Image

Deploying from a generalized VM Image is similar to deploying from an OS image, since both images are generalized.  The main difference is that you cannot add additional data disks when deploying from a VM Image.  The VM Image should already include the data disks you would like to initially add to your virtual machine.  Once the virtual machine is deployed, you are able to attach or detach disks as with any VM.

Similar to Remove-AzureVMImage, the platform tries to resolve the image when there are two images with the same name.  If it cannot uniquely determine whether the referenced image is an OS Image or VM Image, it will error.

The below PowerShell snippet deploys a Large virtual machine instance using the VM Image we created earlier, called myFirstVMImage

$img = Get-AzureVMImage -ImageName "myFirstVMImage"

$user = "admin"
$pass = "adminPassword123"

$myVM = New-AzureVMConfig -Name "VMImageVM" -InstanceSize "Large" -ImageName $img.ImageName `
       | Add-AzureProvisioningConfig -Windows -AdminUsername $user -Password $pass

New-AzureVM -ServiceName "VMImageCloudService" -VMs $myVM -Location "West US" -WaitForBoot

Now, let’s take a look at how to deploy from a specialized VM Image, since it is a bit different.  In this case, the provisioning configuration is not needed, since the image is not generalized.  Using a specialized VM Image is similar to using a disk.  The username and password will be the same as the original (captured) virtual machine. Here’s the script snippet:

$img = Get-AzureVMImage -ImageName "myFirstVMImageSP"

$myVM = New-AzureVMConfig -Name "VMImageVMSP" -InstanceSize "Large" -ImageName $img.ImageName `
| Add-AzureProvisioningConfig -Windows

New-AzureVM -ServiceName "VMImageCloudService" -VMs $myVM -Location "West US" -WaitForBoot

Too Long? Didn’t Read? Let’s recap the main points:

  • VM Images can be ‘Generalized’ or ‘Specialized’ while OS Images are only ‘Generalized’
  • The use of the optional –OSState parameter conveys your intent to save a VM Image instead of an OS Image with the Save-AzureVMImage cmdlet
  • Save-AzureVMImage cmdlet does not allow capturing of a VM Image or OS Image with the same name, but be careful if you are using multiple clients.
  • Get-AzureVMImage now returns OS Images as well as VM Images.
  • Remove-AzureVMImage cmdlet blocks deletion when the name refers to both an OS Image and VM Image, since the platform cannot determine user intent

We hope you have found our PowerShell walkthrough useful.  Feel free to reach out to us if you have questions or comments!

Talk to you again soon….

Christine and Kay

Update on 6/16/2014: PowerShell snippet to filter OS Images using Get-AzureVMImage has been modified.