Recently I had a customer ask me about deploying virtual machines using custom images via Azure Resource Manager (ARM). In the Classic (v1) Portal you have the capability to capture a VM directly in the portal. In the Azure (v2) Portal that capability does not exist, at least not yet. Also, you cannot capture an image in the Classic mode and use it to deploy a VM in the ARM mode. So the questions is, how can this be done today. My answer, PowerShell! Let me show you just how easy it is to deploy custom images using ARM and PowerShell.
Take note that the commands below are using Azure PowerShell 0.9.8. If you wish to use the 1.0 Preview you can refer to a previous post I did here on Keeping Azure PowerShell Cmdlets Updated. In addition, you will need to edit the cmdlets using the pattern {verb}-AzureRM{noun}. So let’s move forward.
Prepare VM
Note: This process will destroy your VM!! I see many comments from others complaining that their VM is useless after this procedure so I am warning you ahead of time. This was tested using Server 2012 R2.
1. The first thing you will want to do is to deploy a VM in the Azure Portal and customize your VM with the applications desired in your image. You can also deploy data disks as needed.
2. Next we need to logon the VM, open a PowerShell console as administrator, and run the follow command to sysprep the image. This command is executing the sysprep.exe application. Sysprep removes all unique system information from the Windows installation. In addition, we use the generalize option to prepare the installation to be imaged, the Out Of Box Experience option to enable users to customize their installation, and the shutdown option which is self-explanatory.
1 |
& "$Env:SystemRoot\system32\sysprep\sysprep.exe" /generalize /oobe /shutdown |
3. Once the sysprep process is complete the VM will shut down as noted by the below screen shots.
Capture VM
The next step is to capture the image. This can be down via PowerShell or by using the ARM Explorer. We will be using PowerShell but for more info on ARM Explorer click here. In a future post we will cover using ARM Explorer.
1. Open your Azure PowerShell console as administrator.
2. Connect to your subscription.
1 |
Add-AzureAccount |
3. Select which subscription to deploy to.
1 |
Select-AzureSubscription "Visual Studio Premium with MSDN" |
4. Switch to ARM mode.
1 |
Switch-AzureMode -Name AzureResourceManager |
5. Here I am just setting some variables based on the VM that I deployed, customized, and sysprepped. You will want to change these to fit your environment.
1 2 3 4 |
$rgName = 'gwpccisco-rg2' $vmName = 'TestVM2' $destContainerName = 'myimages' $location = 'West US' |
6. Now we want to deallocate the VM.
1 |
Stop-AzureVM -ResourceGroupName $rgName -Name $vmName -Force |
7. Next, we need to set the state of the VM to generalized.
1 |
Set-AzureVM -ResourceGroupName $rgName -Name $vmName -Generalized |
8. Finally, we capture the image to our storage account. In this command we are downloading a copy of the ARM template file (JSON) to our local computer. Make sure that you create a folder for the directory structure if its not already in place or the command will fail.
Note: At a minimum two files will be generated, one VHD file for the OS and another file which is the JSON template. You could have additional ones for each data disk as well. All files will get stored in a container called ‘System’ in the storage account with a blob path as the $destContainerName that you specified. This is a bit odd; however, I kind of like it in that my vhd images are stored out of the way and not intermingled with others. Note to self, do not delete System container!
1 |
Save-AzureVMImage -ResourceGroupName $rgName -VMName $vmName -DestinationContainerName $destContainerName -VHDNamePrefix 'template' -Path C:\temp\VMCapture\SampleTemplate.json |
In my example you can see the path to the container and blob.
https://gwpcciscorg23687.blob.core.windows.net/system/Microsoft.Compute/Images/myimages/template-osDisk.cc51b4fb-1755-4d43-ae6b-e93b277980f2.vhd
And the files that were generated.
Note: The JSON file that is generated can be used to build a ARM template using your custom images. The info is contained in the Resources/StorageProfile node of the JSON file.
Create VM Using Custom Image
The custom VM images need to be stored in the same storage account that the VM will be stored in during deployment. So what I have done is to copy the image files, using PowerShell, from the source storage account to the destination storage account. Keep in mind that once the new VMs are deployed you can delete the templates if you wish. This would be more important if using a Premium storage account to keep the cost down of storing those images.
Now it is simply just running your deployment scripts and editing a few parameters to point at the sourceImageUri location for your OS and Data disks.
In my example I am deploying a Resource Group, new storage account, and virtual network. In that storage account I am creating a container called ‘myimages’. I am copying all blobs with the word ‘disk’ in them to this new container. I am deploying 2 VMs in an Availability Set (AS) with a Network Security Group (NSG) associated to each NIC defining allowed inbound traffic. Each NIC has an Instance-level public IP (ILPIP) associated for direct access from the internet. Finally, we clean-up by removing the container holding the copied images.
Her is a high-level outline of the tasks in my deployment.
If you would like to use this code for your testing you can find it in a GitHub account here. Keep in mind that you will need to modify the script to fit your environment. Pay close attention to the $sourceImageUri and $sourceDataDiskImageUri variables as these will need to match up with your storage account.
I hope you have found this post to be useful to you. Maybe in the near future we will see an option in the Azure (v2) portal to accomplish the same tasks.