Continuation from Part 1
In Azure Virtual Machine Internals Part 1 we created a vanilla Windows VM and spent some time poking under the covers and following the leads. In this part we will modify the VM that was created earlier.
Add Disk
C:Program Files (x86)Microsoft SDKsAzureCLIbin>node azure vm disk attach-new -g BlogRG -n BlogWindowsVM -z 60 -d newdatadisk -c ReadWrite -o blogrgdisks562 -r newdatadiskc info: Executing command vm disk attach-new + Looking up the VM "BlogWindowsVM" + Looking up the storage account blogrgdisks562 info: New data disk location: https://blogrgdisks562.blob.core.windows.net/newdatadiskc/newdatadisk.vhd + Updating VM "BlogWindowsVM" info: vm disk attach-new command OK C:Program Files (x86)Microsoft SDKsAzureCLIbin>node azure vm show -g BlogRG -n BlogWindowsVM -d full --json
Snippet below:
{ "lun": 1, "name": "newdatadisk", "vhd": { "uri": "https://blogrgdisks562.blob.core.windows.net/newdatadiskc/newdatadisk.vhd" }, "caching": "ReadWrite", "createOption": "Empty", "diskSizeGB": 60 }
Note that the caching is by default set to readwrite – this means that in addition to read transactions, write transactions are cached and lazily flushed to durable storage. So if the VM fails for whatever reason before the data is flushed, data will be lost. The topic of data disks including caching is discussed. Set this option to a value that is appropriate for your application.
At this stage you would have to RDP into the VM and initialize, partition and format the disk. This can be tedious for large number of VMs or disks. There is an example where you can use automation.
Capture
RDP to the VM and sysprep, generalize, shutdown.
The VM shows as stopped in the Portal, as expected.
The warning about continuing to incur the compute charges is important. Azure charges customers for the usages VM incurs. Sticking to the core resources, VMs can independently incur Compute, Storage and Networking charges.
Compute charge is incurred if a VM is provisioned. The charge is incurred even if VM is in ‘stopped’ state (as shown by the warning above) because Azure has still provisioned that VM slot for the customer. The only way to avoid incurring the compute charge is to stop-dellocate or to deprovision the VM.
Stop-deallocating a VM deallocates the VM slot so it could be allocated to another customer. However, the definition of the VM along with its state in disks is maintained so that the VM can be re-provisioned at customer’s choice. While a Stop-deallocated VM will not accrue Compute charges, it will continue to accrue the Storage charges as the disks are not deleted.
At this stage we have a generalized VM which means that the OS disk does not have the OS customizations were made when the VM was initially created including settings like locale, timezone, admin credentials, etc. Let’s see what happens if we try to ‘start’ the stopped VM from portal. The ‘start’ operation does a simple power-start and does not take the VM thru its specialize sequence as part of provisioning.
Clicking ‘Start’ in Portal and after a few seconds the VM shows a ‘Running’. Try to RDP into it – RDP times out. Boot Diagnostics to rescue.
As expected, this VM has detected that its disk is generalized and is in the ‘specialization’ sequence waiting for customer input. This VM cannot be started successfully.
When we sysprep a VM and have generalized its disk, we have to follow thru with following management operations to realize a generalized image that can be used to create one or more new VMs.
Stop VM – powers down the VM
C:Program Files (x86)Microsoft SDKsAzureCLIbin>node azure vm stop -g BlogRG -n BlogWindowsVM info: Executing command vm stop + Looking up the VM "BlogWindowsVM" warn: VM shutdown will not release the compute resources so you will be billed for the compute resources that this Virtual Machine uses. info: To release the compute resources use "azure vm deallocate". + Stopping the virtual machine "BlogWindowsVM" info: vm stop command OK Generalize – meta data change on the VM to mark it generalized C:Program Files (x86)Microsoft SDKsAzureCLIbin>node azure vm generalize -g BlogRG -n BlogWindowsVM info: Executing command vm generalize + Looking up the VM "BlogWindowsVM" + Generalizing the virtual machine "BlogWindowsVM" info: vm generalize command OK Capture – C:Program Files (x86)Microsoft SDKsAzureCLIbin>node azure vm capture -g BlogRG -n BlogWindowsVM -p CaptureBlogWVM -R capturecontainer -t C:tempCaptureVMTemplate.json info: Executing command vm capture + Looking up the VM "BlogWindowsVM" + Capturing the virtual machine "BlogWindowsVM" info: Saved template to file "C:tempCaptureVMTemplate.json" info: vm capture command OK
The template generated can be used to create new VMs based on the generalized image.
Looking at the storage profile of CaptureVMTemplate.json
"storageProfile": { "osDisk": { "osType": "Windows", "name": "CaptureBlogWVM-osDisk.694733ec-46a0-4e0b-a73b-ee0863a0f12c.vhd", "createOption": "FromImage", "image": { "uri": https://blogrgdisks562.blob.core.windows.net/system/Microsoft.Compute/Images/capturecontainer/CaptureBlogWVM-osDisk.694733ec-46a0-4e0b-a73b-ee0863a0f12c.vhd
Above is the URL of the generalized image that the capture operation has generated. You will notice that the image is a page blob VHD just like any other disk VHD. From the Visual Studio Cloud Explorer we can see the three captured VHDs for the OS data and two data disks. In storage terms they are blob snapshots of the disk page blobs. You will notice that the blob snapshots are in the same storage account as the original disks – blogrgdisks562
Looking at the detail of the generalized OS disk blob (using the command, node azure storage blob list -vv ) we will notice a few things:
{ "name": "Microsoft.Compute/Images/capturecontainer/CaptureBlogWVM-osDisk.694733ec-46a0-4e0b-a73b-ee0863a0f12c.vhd", "lastModified": "Sun, 21 Aug 2016 19:14:25 GMT", "etag": "0x8D3C9F75DB60C73", "contentLength": "136367309312", "contentSettings": { "contentType": "application/octet-stream", "contentEncoding": "", "contentLanguage": "", "contentMD5": "en7n+5uiKTbMlrhW59lEGg==", "cacheControl": "", "contentDisposition": "" }, "sequenceNumber": "8", "blobType": "PageBlob", "lease": { "status": "unlocked",
This is a generalized image and as such unlocked with no outstanding lease
"state": "available" }, "copy": { "id": "b069a713-599d-44ad-85d0-e7e255f9a92c", "progress": "136367309312/136367309312", "bytesCopied": 136367309312, "totalBytes": 136367309312, "source": https://blogrgdisks562.blob.core.windows.net/vhds/BlogWindowsVM2016713231120.vhd?sv=2014-02-14&sr=b&sk=system-1&sig=HTMp66d7wK37Cc12cb2LzDsS6w1YjGyHsIHxPjR2%2F%2F8%3D&st=2016-08-21T18%3A59%3A23Z&se=2016-08-21T20%3A14%3A23Z&sp=rw,
This is the source blob which is still the OS disk on the stopped VM, BlogWindowsVM. By following the copy trail, you can trace how the blobs are getting copied. BTW, the source blob still has an infinite lease (not shown here) against it since it is attached as a disk to a VM.
"status": "success", "completionTime": "Sun, 21 Aug 2016 19:14:25 GMT" }, "metadata": { "microsoftazurecompute_capturedvmkey": "/Subscriptions/f028f547-f912-42b0-8892-89ea6eda4c5e/ResourceGroups/BLOGRG/VMs/BLOGWINDOWSVM", "microsoftazurecompute_imagetype": "OSDisk", "microsoftazurecompute_osstate": "Generalized", "microsoftazurecompute_ostype": "Windows" } }
Continuation of the capture template json-
}, "vhd": { "uri": https://blogrgdisks562.blob.core.windows.net/vmcontainerb05604df-5f0f-4ef2-ab18-76ab7b644cfd/osDisk.b05604df-5f0f-4ef2-ab18-76ab7b644cfd.vhd
The Vhd URI as mentioned above does not exist yet. It will be created when this template is deployed.
If you intend to create multiple VMs using this template then the VHD uri will have to be changed to be unique from the second VM onwards.
}, "caching": "ReadWrite" }, "dataDisks": [ { "lun": 0, "name": "CaptureBlogWVM-dataDisk-0.694733ec-46a0-4e0b-a73b-ee0863a0f12c.vhd", "createOption": "FromImage", "image": { "uri": "https://blogrgdisks562.blob.core.windows.net/system/Microsoft.Compute/Images/capturecontainer/CaptureBlogWVM-dataDisk-0.694733ec-46a0-4e0b-a73b-ee0863a0f12c.vhd" }, "vhd": { "uri": "https://blogrgdisks562.blob.core.windows.net/vmcontainerb05604df-5f0f-4ef2-ab18-76ab7b644cfd/dataDisk-0.b05604df-5f0f-4ef2-ab18-76ab7b644cfd.vhd" }, "caching": "ReadWrite" }, { "lun": 1, "name": "CaptureBlogWVM-dataDisk-1.694733ec-46a0-4e0b-a73b-ee0863a0f12c.vhd", "createOption": "FromImage", "image": { "uri": "https://blogrgdisks562.blob.core.windows.net/system/Microsoft.Compute/Images/capturecontainer/CaptureBlogWVM-dataDisk-1.694733ec-46a0-4e0b-a73b-ee0863a0f12c.vhd" }, "vhd": { "uri": "https://blogrgdisks562.blob.core.windows.net/vmcontainerb05604df-5f0f-4ef2-ab18-76ab7b644cfd/dataDisk-1.b05604df-5f0f-4ef2-ab18-76ab7b644cfd.vhd" }, "caching": "ReadWrite" } ] },
At this point of time, the original VM is stopped but still accumulating usage charges for Compute and Storage. If we intend to leave the VM stopped for a length of time, we can save on the Compute charges by stop deallocating the VM – it will deallocate the VM on the Hyper-V host but retains the VM meta-data and the disk blobs in Storage.
C:Program Files (x86)Microsoft SDKsAzureCLIbin>node azure vm deallocate -g BlogRG -n BlogWindowsVM info: Executing command vm deallocate + Looking up the VM "BlogWindowsVM" + Deallocating the virtual machine "BlogWindowsVM" info: vm deallocate command OK get-instance-view command will show the VM status as deallocated (snippets of result below) C:Program Files (x86)Microsoft SDKsAzureCLIbin>node azure vm get-instance-view -g blogrg -n blogwindowsvm –json "osDisk": { "osType": "Windows", "name": "BlogWindowsVM", "vhd": { "uri": https://blogrgdisks562.blob.core.windows.net/vhds/BlogWindowsVM2016713231120.vhd
Even when the VM is stop-deallocated, the OS and data disks are retained
}, "caching": "ReadWrite", "createOption": "FromImage" }, "dataDisks": [ { "lun": 0, "name": "BlogWindowsVM-20160814-191501427", "vhd": { "uri": "https://blogrgdisks562.blob.core.windows.net/vhds/BlogWindowsVM-20160814-191501427.vhd" }, "caching": "ReadWrite", "createOption": "Empty", "diskSizeGB": 50 }, { "lun": 1, "name": "newdatadisk", "vhd": { "uri": "https://blogrgdisks562.blob.core.windows.net/newdatadiskc/newdatadisk.vhd" }, "caching": "ReadWrite", "createOption": "Empty", "diskSizeGB": 60 } ] --- --- "statuses": [ { "code": "ProvisioningState/succeeded", "level": "Info", "displayStatus": "Provisioning succeeded", "time": "2016-08-22T02:58:42.797Z" }, { "code": "OSState/generalized", "level": "Info", "displayStatus": "VM generalized"
OS is generalized, so this VM can be recreated by specializing it
}, { "code": "PowerState/deallocated", "level": "Info", "displayStatus": "VM deallocated"
Compute VM resources are deallocated
Even when a VM is deallocated, its disk storage blobs are still locked like when the VM was running. This is to prevent them from being accidentally deleted.
Looking at the blob properties of the disk blob you will notice that Azure continues to maintain an infinite lease on the blob.
C:Program Files (x86)Microsoft SDKsAzureCLIbin>node azure storage blob list -vv name: 'BlogWindowsVM-20160814-191501427.vhd', lastModified: 'Sun, 21 Aug 2016 19:10:50 GMT', etag: '0x8D3C9F6DD886DB5', contentLength: '53687091712', contentSettings: { contentType: 'application/octet-stream', contentEncoding: '', contentLanguage: '', contentMD5: '', cacheControl: '', contentDisposition: '' }, sequenceNumber: '1', blobType: 'PageBlob', lease: { status: 'locked', state: 'leased', duration: 'infinite' }
Cloning VMs
At this point of time we can clone VMs from the generalized image that we captured. We can either use the capture template json or create a new image using the FromImage option with the generalized image as the parameter value.
I grabbed a template from Azure Quickstart templates and modified it just enough and deployed it to create a new VM of name CopyBlogVM.
C:Program Files (x86)Microsoft SDKsAzureCLIbin>node azure group deployment create -g BlogRG -n CopyVMDeployment -f "C:tempcopyvmtemplatetemplate.json" -e "C:tempcopyvmtemplateparameters.json" info: Executing command group deployment create + Initializing template configurations and parameters + Creating a deployment info: Created template deployment "CopyVMDeployment" + Waiting for deployment to complete + The relevant snippet from the VM template is the storage profile: "storageProfile": { "osDisk": { "name": "[concat(parameters('virtualMachineName'),'-osDisk')]", "osType": "[parameters('osType')]", "caching": "ReadWrite", "createOption": "fromImage", image": { "uri": "[parameters('osDiskVhdUri')]" }, "vhd": { "uri": "[concat(concat(reference(resourceId('blogrg', 'Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2015-06-15').primaryEndpoints['blob'], 'vhds/'), parameters('virtualMachineName'), '20161228010921.vhd')]" } }, "dataDisks": [] },
The OsDiskvhdUri is set in the parameters file to the generalized image file
"osDiskVhdUri": { "value": https://blogrgdisks562.blob.core.windows.net/system/Microsoft.Compute/Images/capturecontainer/CaptureBlogWVM-osDisk.694733ec-46a0-4e0b-a73b-ee0863a0f12c.vhd },
The CopyBlogVM creates successfully with an OS disk that starts out as a copy of the generalized OS disk referred to by OsDiskvhdUri. Using the generalized OD disk as a template, any number of new VMs can be stamped out. A common scenario would be to capture a generalized VM with new updates/patches and then create new VMs based on the updated image.
In Conclusion
In the two posts we have covered some of the details on how an Azure VM works under the covers. There are other capabilities that we have not covered including backup, encryption, licensing, planned maintenance and networking details. Time permitting, we will visit these topics in future posts.