How to Export Non Clustered VM’S and Import Them to CSV Shares on 2008 R2 Cluster
So I have been asked a few times a process of how to take a Windows Server 2008 R2 Enterprise Hyper-V Server and reconfigure it into a Windows 2008 R2 Failover Cluster with CSV’s running the Hyper-V Application. So I decided to write this process here in this blog. As you can see from the picture below we have one child partition that is running on Windows Server 2008 R2.
Note: In this example I will be clustering a Windows 2008 R2 Enterprise Core server. I have connected to the Windows Server 2008 R2 Core Server through another server.
So here you can see I have a VM that is listed in my Hyper-V and that the VM is off.
So the first thing to do is to shut down the VM’s and export them. To ensure that if anything happens you have a good backup of the VM’s.
Save the VM out on the network, or any other disk that will not be clustered. Once you have ensured that all the VM’s are backed up and in a safe location we will need to export the configuration file that we will use to import the VM back into Hyper-V.
To do this we will have to run a script. The script that will be used is listed below:
Option Explicit
Dim HyperVServer Dim VMName Dim ExportPath Dim WMIService Dim Msvm_VirtualSystemManagementService Dim query Dim vm Dim InParam Dim exportSettingData Dim OutParam Dim Job
'Prompt for the Hyper-V Server to use HyperVServer = InputBox("Specify the Hyper-V Server to export the virtual machine from:")
'Get name for the virtual machine to export VMName = InputBox("Specify the name of the virtual machine to export:")
'Get location for the exported virtual machine ExportPath = InputBox("Specify the location to export the virtual machine configuration to:")
'Get an instance of the WMI Service in the virtualization namespace. Set WMIService = GetObject("winmgmts:\\" & HyperVServer & "\root\virtualization")
'Get the Msvm_VirtualSystemManagementService object Set Msvm_VirtualSystemManagementService = WMIService.ExecQuery("Select * from Msvm_VirtualSystemManagementService").ItemIndex(0)
'Get the virtual machine object query = "select * from Msvm_ComputerSystem where ElementName = '" & VMName & "'" Set vm = WMIService.ExecQuery(query).ItemIndex(0)
'Setup the input parameter list Set InParam = Msvm_VirtualSystemManagementService.Methods_("ExportVirtualSystemEx").InParameters.SpawnInstance_() InParam.ComputerSystem = vm.Path_.Path
Set exportSettingData = (vm.Associators_("Msvm_SystemExportSettingData", "Msvm_VirtualSystemExportSettingData")).ItemIndex(0)
exportSettingData.CopyVmStorage = False exportSettingData.CopyVmRuntimeInformation = true exportSettingData.CreateVmExportSubdirectory = true exportSettingData.CopySnapshotConfiguration = 0
InParam.ExportSettingData = exportSettingData.GetText_(1) InParam.ExportDirectory = ExportPath
'Execute the method and store the results in OutParam Set OutParam = Msvm_VirtualSystemManagementService.ExecMethod_("ExportVirtualSystemEx", InParam)
'Check to see if the job completed synchronously if (OutParam.ReturnValue = 0) then Wscript.Echo "The virtual machine has been exported." elseif (OutParam.ReturnValue <> 4096) then Wscript.Echo "The virtual machine has not been exported." else
'Get the job object set Job = WMIService.Get(OutParam.Job)
'Wait for the job to complete (3 == starting, 4 == running) while (Job.JobState = 3) or (Job.JobState = 4) Wscript.Echo "Exporting virtual machine. " & Job.PercentComplete & "% complete" WScript.Sleep(1000)
'Refresh the job object set Job = WMIService.Get(OutParam.Job) Wend
'Provide details if the job fails (7 == complete) if (Job.JobState <> 7) then Wscript.Echo "The virtual machine has not been exported." Wscript.Echo "ErrorCode:" & Job.ErrorCode Wscript.Echo "ErrorDescription:" & Job.ErrorDescription else Wscript.Echo "The virtual machine has been exported." end If end if
|
NOTE: copy the code listed above and save it to the computer. In my example I saved it as exportconfig.vbs.
So to run this script we will need to execute it from the CMD window on the Windows Server 2008 R2 Core server. I executed the script by typing exportconfig.vbs and pressing enter. There will be a few popup boxes that you will need to answer. Let’s take a look at them now:
|
Here you will want to specify the Hyper-V Parent server. In this case it’s Hyper-V-1. Press “OK” after you enter your Hyper-V Parent server name.
|
Next you will have to give the name of the VM that you want to export the configuration from. In my case it is Test VM. Press “ok” after you have identified the VM that you want to export.
|
The next popup will ask you for a location to save the VM configuration file to. In my case I created a folder on C, called configonly, and saved the configuration file there. Press “ok” after you enter a path to save the export configuration file to.
You then should see a few popups that let you know that the configuration file is exporting. Click ok here to continue.
Finally the configuration file has been exported.
Note: You will have to run this script for every VM that you want to export from the Hyper-V server.
____________________________________________________________________________________
Now that the VM’s are exported you can Cluster the two (or more) nodes together. Again I will be using another server to run the Failover Cluster Manager to perform the cluster of the two nodes (I am only cluster 2 nodes in my example).
Click on create a Cluster. And follow the wizard.
Add the server names that are to be clustered together.
Here you can see that I have added both of my server’s names here. Once you have all the server names added to the selected servers column click next.
Next thing to do is to give the cluster a name, and then click next.
This is the confirmation screen, just click next…
Wait for the cluster to be configured.
And finally we have a cluster. Click on finished.
The next thing we need to do is to setup the Cluster Shared Volumes. To do this you will select “Enable Cluster Shared Volumes…” link from the Failover Cluster Manager. To see this option you must have the cluster name highlighted.
Once you click on the link you will get a warning message informing you of the following:
Select “I have read the above notice” and then click “ok”
You will now see a new area listed under the cluster. This is the Cluster Shared Volumes. Once you have added the clustered storage you can then add that storage to “Cluster Shared Volumes”. To do this you select “Cluster Shared Storage” and then click the “add storage” link under the actions pane on the right hand side. A new window will pop up and list the disk that are currently in the cluster storage area.
Place a check mark next to the storage you want to add to the “Cluster Shared Storage” area and then select “OK”.
Click ok to add the selected storage to the cluster shared volumes.
You should then see the storage added to the “Cluster Shared Volumes” location.
Note : at this point any drive letter that was assigned to the disk that was added to the cluster shared volumes is now removed. To access the disk you will need to browse c:\clusterstorage\volume#\. Also the order that the disk were listed back when you added them to the cluster shared storage is the way that the volumes will be displayed under the c:\clusterstorage directory.
So what is the next thing that we need to do…well we need to delete the VM from the Hyper-v Manager. To do this you will select the VM machine from the Hyper-V Manager window.
Next you will click on the delete option on the right hand side of the Hyper-v Manager window.
This will remover the VM from the Hyper-V Manager. This does not delete the VHD file that was associated with the VM.
As you can see the VM is now missing. But not to worry it soon will be back. Next thing that we need to do is to run the Import script. Copy the script is listed below:
option explicit
Dim objWMIService Dim managementService Dim switchService Dim fileSystem
const JobStartIng = 3 const JobRunnIng = 4 const JobCompleted = 7 const wmiStarted = 4096 const wmiSuccessful = 0
MaIn()
'----------------------------------------------------------------- ' MaIn '----------------------------------------------------------------- Sub MaIn() Dim computer, objArgs, importDirectory, generateNewID, tempSourceResourcePaths, sourceResourcePaths(), Index, resourcePaths, path, i, j
Set objArgs = WScript.Arguments.Named If WScript.Arguments.Count = 2 Then If objArgs.Exists("ImportDirectory") Then importDirectory = objArgs.Item("ImportDirectory") Else WScript.Echo "ImportDirectory argument is not provided, Please refer the usage section for more inFormation on the arguments" Usage End If If objArgs.Exists("ResourcePaths") Then resourcePaths = objArgs.Item("ResourcePaths") tempSourceResourcePaths = Split(resourcePaths, ";") i = 0 For j=0 to UBound(tempSourceResourcePaths) If tempSourceResourcePaths(j) <> "" Then If IsNull(tempSourceResourcePaths(j)) <> true Then ReDim Preserve sourceResourcePaths(i) sourceResourcePaths(i) = tempSourceResourcePaths(j) i = i + 1 End If End If Next WScript.Echo "Resource Paths" For Each path In sourceResourcePaths WScript.Echo path Next Else WScript.Echo "ResourcePaths argument is not provided, Please refer the usage section for more inFormation on the arguments" Usage End If Else WScript.Echo "Number of arguments does not match, Please refer the usage section for more information on the arguments" Usage End If
Set fileSystem = Wscript.CreateObject("ScriptIng.FileSystemObject") computer = "." Set objWMIService = GetObject("wInmgmts:\\" & computer & "\root\virtualization") Set managementService = objWMIService.ExecQuery("select * from Msvm_VirtualSystemManagementService").ItemIndex(0) Set switchService = objWMIService.ExecQuery("select * from Msvm_VirtualSwitchManagementService").ItemIndex(0)
If ImportVirtualSystemEx(importDirectory, (sourceResourcePaths)) Then WriteLog "Done" WScript.Quit(0) Else WriteLog "ImportVirtualSystemEx Failed." WScript.Quit(1) End If End Sub
'----------------------------------------------------------------- ' GetVirtualSystemImportSettIngData from a directory '----------------------------------------------------------------- Function GetVirtualSystemImportSettIngData(importDirectory)
Dim objInParam, objOutParams
Set objInParam = managementService.Methods_("GetVirtualSystemImportSettIngData").InParameters.SpawnInstance_() objInParam.ImportDirectory = importDirectory
Set objOutParams = managementService.ExecMethod_("GetVirtualSystemImportSettIngData", objInParam)
If objOutParams.ReturnValue = wmiStarted Then If (WMIJobCompleted(objOutParams)) Then Set GetVirtualSystemImportSettIngData = objOutParams.ImportSettIngData End If ElseIf objOutParams.ReturnValue = wmiSuccessful Then Set GetVirtualSystemImportSettIngData = objOutParams.ImportSettIngData Else WriteLog Format1("GetVirtualSystemImportSettIngData failed with ReturnValue {0}", objOutParams.ReturnValue) End If
End Function '----------------------------------------------------------------- ' ImportVirtualSystem from a directory '----------------------------------------------------------------- Function ImportVirtualSystemEx(importDirectory, sourceResourcePaths)
Dim objInParam, objOutParams Dim newDataRoot Dim importSettIngData Dim currentResourcePaths
ImportVirtualSystemEx = false Set objInParam = managementService.Methods_("ImportVirtualSystemEx").InParameters.SpawnInstance_() objInParam.ImportDirectory = importDirectory
Set importSettIngData = GetVirtualSystemImportSettIngData(importDirectory) currentResourcePaths = importSettIngData.CurrentResourcePaths Call OrderResourcePaths(currentResourcePaths, sourceResourcePaths) importSettIngData.GenerateNewId = false importSettIngData.CreateCopy = false importSettIngData.SourceResourcePaths = sourceResourcePaths importSettIngData.CurrentResourcePaths = currentResourcePaths
objInParam.ImportSettIngData = importSettIngData.GetText_(1)
Set objOutParams = managementService.ExecMethod_("ImportVirtualSystemEx", objInParam)
If objOutParams.ReturnValue = wmiStarted Then If (WMIJobCompleted(objOutParams)) Then ImportVirtualSystemEx = true End If ElseIf objOutParams.ReturnValue = wmiSuccessful Then ImportVirtualSystemEx = true Else WriteLog Format1("ImportVirtualSystemEx failed with ReturnValue {0}", objOutParams.ReturnValue) End If
End Function
'----------------------------------------------------------------- ' ImportVirtualSystem from a directory '----------------------------------------------------------------- Function OrderResourcePaths(currentResourcePaths, sourceResourcePaths) Dim resourcePathLength, newCurrentResourcePaths(), newSourceResourcePaths(), vhdName, pathNames, pos, index, initialIndex, i, j If UBound(sourceResourcePaths) >= UBound(currentResourcePaths) Then resourcePathLength = UBound(sourceResourcePaths) Else WScript.Echo "Number of entries in the SourceResourcePaths and the CurrentResourcePaths are not matching , Please refer the usage section for more information on the arguments" WScript.Quit(1) End If index = 0 For i=0 to resourcePathLength initialIndex = index pathNames = split(sourceResourcePaths(i), "\",-1,1) vhdName = pathNames(UBound(pathNames)) For j=0 to UBound(currentResourcePaths) pos = Instr(1,currentResourcePaths(j),vhdName,1) If pos <> 0 Then ReDim Preserve newCurrentResourcePaths(index) newCurrentResourcePaths(index) = currentResourcePaths(j) ReDim Preserve newSourceResourcePaths(index) newSourceResourcePaths(index) = sourceResourcePaths(i) index = index + 1 Exit For End If Next If initialIndex = index Then ReDim Preserve newCurrentResourcePaths(index) newCurrentResourcePaths(index) = sourceResourcePaths(i) ReDim Preserve newSourceResourcePaths(index) newSourceResourcePaths(index) = sourceResourcePaths(i) index = index + 1 End If Next currentResourcePaths = newCurrentResourcePaths sourceResourcePaths = newSourceResourcePaths End Function
'----------------------------------------------------------------- ' Handle wmi Job object '----------------------------------------------------------------- Function WMIJobCompleted(outParam)
Dim WMIJob, jobState
Set WMIJob = objWMIService.Get(outParam.Job)
WMIJobCompleted = true
jobState = WMIJob.JobState
while jobState = JobRunnIng or jobState = JobStartIng WriteLog Format1("In progress... {0}% completed.",WMIJob.PercentComplete) WScript.Sleep(1000) Set WMIJob = objWMIService.Get(outParam.Job) jobState = WMIJob.JobState wend
If (jobState <> JobCompleted) Then WriteLog Format1("ErrorCode:{0}", WMIJob.ErrorCode) WriteLog Format1("ErrorDescription:{0}", WMIJob.ErrorDescription) WMIJobCompleted = false End If
End Function
'----------------------------------------------------------------- ' Create the console log files. '----------------------------------------------------------------- Sub WriteLog(lIne) Dim fileStream Set fileStream = fileSystem.OpenTextFile(".\ImportVM.log", 8, true) WScript.Echo lIne fileStream.WriteLIne lIne fileStream.Close
End Sub
'------------------------------------------------------------------------------ ' The strIng FormatIng functions to avoid strIng concatenation. '------------------------------------------------------------------------------ Function Format1(myStrIng, arg0) Format1 = Replace(myStrIng, "{0}", arg0) End Function
'------------------------------------------------------------------------------ ' The Usage function to convey how to call the script. '------------------------------------------------------------------------------ Sub Usage() WScript.Echo "Usage: cscript ImportVM.vbs /ImportDirectory:importDirectoryName /ResourcePaths:CurrentResourcePaths" WScript.Echo "/ImportDirectory: Directory from where the virtual MachIne will be imported from." WScript.Echo "/ResourcePaths: The comma separated list of Current Resource Paths. The Former list of Resource Paths can be obtained by executIng " WScript.Echo "script GetImportSettIngsData. If there is any change in the paths fix it up and pass it as an argument to this script." WScript.Echo "Provide multiple paths in a comma separated way. Like /ResourcePaths: ""C:\a.vhd"";""C:\b.vhd"";""D:\abc.vhd""" WScript.Quit(1) End Sub |
So when we execute this script we need to do it from a CMD prompt so we can add some switches to it when it runs. So you will need to do this from the Windows Server 2008 R2 core machine. So the switches that we will be passing are importdirectory and resourcepaths. ImportDirectory is the directory where the configuration file was exported to. In my case it was c:\configonly\testvm. The resourcepaths switch is where the VHD has been moved to. In my case it will be located in the C:\clusterstorage\volume1\vm’s\test vm\test vm\test vm.vhd. so when we run this with the switches in the CMD prompt window on Windows Server 2008 R2 it looks something like:
Note: this information can be found in KB957256 (https://support.microsoft.com/kb/957256)
Once you hit enter you should then see a few popup windows that look like:
Click ok here…
Click ok here…
Click ok here…
|
And wow…finally done! Click ok
Now let’s take a look back in Hyper-V manager…
You can see here that our VM is listed again in Hyper-V Manager.
Now we need to cluster the VM. To do this open up Failover Cluster Manager
Then right click on Services and Application on the left hand side.
Select Configure a Service or Application…
This will open up a new wizard called High Availability Wizard
Click Next…
Then select the Virtual Machine option and then click next.
That will bring up a list of VM’s that are on the node. You want to place a check mark next to the VM that you want to cluster and then click next.
That brings up the confirmation window…click next.
Yahoo!!! We have a finish button. You can view the report for a list of warning or errors….but I am going to click finished.
Note : you will get a warning on the virtual machine due to CSV configuration…this is expected.
Once again back to Failover Cluster Manager to see the VM
Great job it is present in the Failover Cluster Manager. Now to bring the VM online.
Looking Very nice here. Now test failover and ensure that everything is working well.
I hope that the information found in this article is helpful.
Keith Hill
Support Escalation Engineer
Microsoft Enterprise Platforms Support
Comments
- Anonymous
January 01, 2003
@raffa
The process can be the same for 2012R2, but the much easier way is to simply do a Live Storage Migration. Right Mouse Click the VM and select MOVE. In the wizard, say you wish to just move the storage (which means the .vhd(x)'s) and give the path to the CSV where you want them. Then inside Failover Cluster Manager, add a new Virtual Machine role and select this VM just like the steps above. - Anonymous
January 01, 2003
HiYour information is too good, but I don´t know if you can send me the script, because I copied the entire information and send me some errors, I corrected some lines, but at the end, the script does not work.Thanks - Anonymous
March 17, 2014
I am keep getting this error and i don't understand what does it mean ?
Number of entries in the SourceResourcePaths and the CurrentResourcePaths are not matching - Anonymous
June 27, 2014
For 2012r2 is The same procedure? - Anonymous
September 22, 2014
I get the following error when importing
Disk path 'C:configonlyWin7VMTest' is not a path to storage in the cluster or to storage that can be added to the cluster. You must ensure this storage is available to every node in the cluster to make this virtual machine highly available.
I can turn on the vm but I can migrate/live migrate or move to my 2nd cluster node.
I copied the VHD file to C:clustervolume1hyper-vvmtestvmtest.vhd
After the above error I even copied the contents that were created under C:configonlyWin7VMTest to C:clustervolume1hyper-vvmtest but still get the same error.
Guide looks promising but either I am doing something wrong or the guide is missing something.
NOTE that you should emphasize that after you export the vm to COPY the actual vhd file to the cluster volume instead of a simple sentence towards the end of the article "The resourcepaths switch is where the VHD has been moved to"