How to convert a VM from managed disks to unmanaged disks

Recently, managed disk has been published as a new feature. Azure Managed Disks simplifies disk management for Azure IaaS VMs by managing the storage accounts associated with the VM disks. You only have to specify the type (Premium or Standard) and the size of disk you need, and Azure creates and manages the disk for you.

There is also a tutorial for you to help you convert a virtual machine from unmanaged disks to managed disks (https://docs.microsoft.com/en-us/azure/virtual-machines/windows/convert-unmanaged-to-managed-disks). With the Azure PowerShell, you can easily finish the job.

However, someone of you may want to return back to use unmanaged disk. To accomplish this you have to recreate a new VM with based on the VHD, because currently there is not other direct way to do it. This article will show you how to recreate it by Azure Java SDK.

The whole process is broadly divided into the following steps:

  1.  Create a VM with managed disks (with a data disk)
  2.  Export the data from managed disks to unmanaged disks
  3. Create a new VM based on the VHD

Configure the environment 

I will use Marven to manage the project, please add the following dependences into the POM file:

<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure</artifactId>
<version>1.1.0</version>
</dependency>

<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-storage</artifactId>
<version>5.3.0</version>
</dependency>


Azure Authentication

Use the following funtion to get the Azure context:

private static Azure getContext() {
 String client = "Client ID";
 String tenant = "Tenant ID";
 String key = "Client Secret";
 ApplicationTokenCredentials token = new ApplicationTokenCredentials(client, tenant, key,
 AzureEnvironment.AZURE);
 return Azure.configure().withLogLevel(LogLevel.BASIC).authenticate(token).withSubscription("Subscription ID");
}

You can get the Client ID and Client Secret from the service principal you created. You can create it either through PowerShell or the portal.


Create a VM with managed disks

private static VirtualMachine createVM(Azure azure) {
 String rsgname = "JackTest";
 Region region = Region.ASIA_EAST;

 // Create the resource group
 ResourceGroup rsg = azure.resourceGroups().define(rsgname).withRegion(region).create();

 // Create the public ip for the vm
 PublicIPAddress publicIPAddress = azure.publicIPAddresses().define("publicip4testvm")
 .withRegion(region).withExistingResourceGroup(rsgname)
 .create();

 // Create the VNet 
 Network virtualNetwork = azure.networks().define("vnet4testvm")
 .withRegion(region).withExistingResourceGroup(rsgname)
 .withAddressSpace("192.168.0.0/16")
 .withSubnet("DefaultSubnet", "192.168.1.0/24")
 .defineSubnet("SecondSubnet").withAddressPrefix("192.168.2.0/24").attach()
 .create();

 // Create the NIC
 NetworkInterface networkInterface = azure.networkInterfaces().define("nic4testvm")
 .withRegion(region).withExistingResourceGroup(rsgname)
 .withExistingPrimaryNetwork(virtualNetwork)
 .withSubnet("DefaultSubnet")
 .withPrimaryPrivateIPAddressDynamic()
 .withExistingPrimaryPublicIPAddress(publicIPAddress)
 .create();

 // Create the data disk
 Disk dataDisk = azure.disks().define("Data_Disk")
 .withRegion(region).withExistingResourceGroup(rsg)
 .withData().withSizeInGB(32).withSku(DiskSkuTypes.PREMIUM_LRS)
 .create();

 // Create the VM
 VirtualMachine vm = azure.virtualMachines().define("JackTestVM")
 .withRegion(region).withExistingResourceGroup(rsgname)
 .withExistingPrimaryNetworkInterface(networkInterface)
 .withPopularWindowsImage(KnownWindowsVirtualMachineImage.WINDOWS_SERVER_2012_R2_DATACENTER)
 .withAdminUsername("jack").withAdminPassword("TestPassword01!")
 .withExistingDataDisk(dataDisk, 0, CachingTypes.READ_WRITE)
 .withOSDiskStorageAccountType(StorageAccountTypes.PREMIUM_LRS).withOSDiskName("OS_Disk")
 .withSize(VirtualMachineSizeTypes.STANDARD_DS1_V2)
 .create();

 return vm;
}

Export the data from the managed disk

Here is the code sample for your reference :

// Create a new storage account for the VHDs
StorageAccount storage = azure.storageAccounts().define("storage4testvm")
 .withRegion(vm.region()).withExistingResourceGroup(vm.resourceGroupName())
 .withSku(SkuName.PREMIUM_LRS)
 .create();

// Generate the connection string
String connectionString = String.format(
 "DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s;EndpointSuffix=core.windows.net",
 storage.name(), storage.getKeys().get(0).value());

Disk os_disk = azure.disks().getById(vm.osDiskId());
Disk data_disk = azure.disks().getById(vm.dataDisks().get(0).id());

// Deallocate the VM
vm.deallocate();

// Get the export URLs
String os_disk_url = os_disk.grantAccess(3600);
String data_disk_url = data_disk.grantAccess(3600);

// Begin copy
CloudStorageAccount storageAccount = CloudStorageAccount.parse(connectionString);
CloudBlobClient blobClient = storageAccount.createCloudBlobClient();
CloudBlobContainer blobContainer = blobClient.getContainerReference("vhds");
blobContainer.createIfNotExists();

CloudPageBlob os_disk_blob = blobContainer.getPageBlobReference("os_disk.vhd");
os_disk_blob.startCopy(new URI(os_disk_url));

CloudPageBlob data_disk_blob = blobContainer.getPageBlobReference("data_disk.vhd");
data_disk_blob.startCopy(new URI(data_disk_url));

CopyState os_disk_state = null;
CopyState data_disk_state = null;
do{
 os_disk_blob.downloadAttributes();
 data_disk_blob.downloadAttributes();
 os_disk_state = os_disk_blob.getCopyState();
 data_disk_state = data_disk_blob.getCopyState();

 System.out.println(String.format("OS_Disk : %.2f%% \t Data_Disk : %.2f%%"
 , 1.00 * 100 * os_disk_state.getBytesCopied()/os_disk_state.getTotalBytes()
 , 1.00 * 100 * data_disk_state.getBytesCopied()/data_disk_state.getTotalBytes()
 ));
 if(os_disk_state.getStatus().equals(CopyStatus.SUCCESS) && data_disk_state.getStatus().equals(CopyStatus.SUCCESS))
 break;
 else
 Thread.sleep(5000);
}while(true);

// Finish, cancel the export URLs
os_disk.revokeAccess();
data_disk.revokeAccess();

Create a new VM

// Get the old NIC configuration
NetworkInterface nic = azure.networkInterfaces().getById(vm.networkInterfaceIds().get(0));

// Delete the old VM
azure.virtualMachines().deleteById(vm.id());

// Create a new one
azure.virtualMachines().define(vm.name())
 .withRegion(vm.region()).withExistingResourceGroup(vm.resourceGroupName())
 .withExistingPrimaryNetworkInterface(nic)
 .withSpecializedOSUnmanagedDisk(os_disk_blob.getStorageUri().getPrimaryUri().toString(),OperatingSystemTypes.WINDOWS)
 .defineUnmanagedDataDisk("Data_Disk").withExistingVhd("storage4testvm", "vhds", "data_disk.vhd").withLun(0).attach()
 .withOSDiskName("OS_Disk").withSize(VirtualMachineSizeTypes.STANDARD_DS1_V2)
 .create();

Summary

Now, the VM based on managed disks should be successfully turned to a VM based on unmanaged disks  and all the configurations and data are kept.

You can get the whole code sample from :  https://1drv.ms/u/s!AqZDQKBllgcXguw5WT2kBhtoxB3mWA

Leave a Reply

Your email address will not be published. Required fields are marked *