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.

 

clip_image001

So here you can see I have a VM that is listed in my Hyper-V and that the VM is off.clip_image003

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.

clip_image005

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:

Hyper-v-1

clip_image006

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.

 

Test VM

clip_image007

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.

C:\Configonly\Test VM

clip_image008

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.

clip_image009

You then should see a few popups that let you know that the configuration file is exporting. Click ok here to continue.

clip_image010

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).

clip_image012

Click on create a Cluster. And follow the wizard.

clip_image014Click next.

clip_image016

Add the server names that are to be clustered together.

clip_image018

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.

clip_image020

Next thing to do is to give the cluster a name, and then click next.

clip_image022

This is the confirmation screen, just click next…

clip_image024 Wait for the cluster to be configured.

clip_image026

And finally we have a cluster. Click on finished.

clip_image028

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.

clip_image030

Once you click on the link you will get a warning message informing you of the following:

clip_image031

Select “I have read the above notice” and then click “ok”

clip_image033

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.

clip_image034

Place a check mark next to the storage you want to add to the “Cluster Shared Storage” area and then select “OK”.

clip_image035

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.

clip_image037

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.

clip_image039

clip_image041

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.

clip_image042

 Next you will click on the delete option on the right hand side of the Hyper-v Manager window.

clip_image043clip_image044

This will remover the VM from the Hyper-V Manager. This does not delete the VHD file that was associated with the VM.

clip_image046

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:

clip_image048

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:

clip_image049

Click ok here…

clip_image050

Click ok here…

clip_image051

Click ok here…

The virtual machine has been imported.

clip_image052clip_image010[1]

And wow…finally done! Click ok

Now let’s take a look back in Hyper-V manager…

clip_image054

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

clip_image056

Then right click on Services and Application on the left hand side.

clip_image058

Select Configure a Service or Application…

This will open up a new wizard called High Availability Wizard

clip_image060

Click Next…

clip_image062

Then select the Virtual Machine option and then click next.

clip_image064

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.

clip_image066

That brings up the confirmation window…click next.

clip_image068

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

clip_image070

Great job it is present in the Failover Cluster Manager. Now to bring the VM online.

clip_image072

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