Script to perform a warm backup of a virtual machine

Here's an example of a script written in VBScript that you can write to perform a warm backup of a virtual machine. It was written by John Kelbley, who's a technology specialist for Microsoft. Please pay attention to the warnings and caveats given in the script.

Thanks John!

' READ THIS LINE!!!   The sample scripts are not supported under any Microsoft standard support program or service.  The sample scripts are provided AS IS without warranty of any kind.  Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose.  The entire risk arising out of the use or performance of the sample scripts and documentation remains with you.  In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages.
'
'
' MoveWarm.VBS  written by John Kelbley
'
'
' This the mother of all Demo Virtual Server Availability scripts! 
'
' When the script is executed properly from a running Virtual Server system with active Guest images,
' it hibernates, copies, installs, and starts all active Guest images on another Virtual Server!
'
' This could be used in a production environment to allow for hardware maintenance while minimizing down time of Guest images, if all assumptions are met, including:
' Identical Virtual Server hardware configuration - enough RAM, disk space with same drive letters, etc. on target to accommodate migrated guest images
' Identical Virtual Network configuration (it's just easier, otherwise, they may not start automatically, and you'll have to adjust the config)
' Other things I haven't run into in my testing (I only have a couple of Evo's to test on!)
' XP SP2 (connection firewall) can keep this from running, depending on the hosts you are using (rumor that some DCOM security thing might mess things up, too)
' I sometimes have been know to setup a local account on both hosts with the same name and password as my domain credential, and map drives back and forth to
' make sure everything will run right before kicking off the script for a demo!
'
' NAME RESOLUTION IS A BIG DEAL HERE TOO!!!
'
' How does the Script do it?  It does a few key things:
' 1) "saves state" on running VPC images
' 2) Creates a .BAT and .VBS on original host for each of the hibernated images in current directory
' 3) Starts .BAT for each hibernated image on local host (this kind of multi-threads the copy)
'  a) copies files related to image to new host (.VHD config, save state)
'  b) runs .VBS to register config file on new host, and re-start from Saved State
'
' The script does not clean up after itself, as it is a sample meant for training purposes!
'
' The .BAT and .VBS files generated may be of interest to someone, so at this time, I did not want to delete them
'
'
' When I run the demos, I usually use smaller VMs  - some from the VMSTORE areas - like Windows 3.0 or BOB on Windows 95
'
'
' Thanks to the Tech Net Script Center folks for all their guidance (great examples on the web, as well as usable reference books on VBS!)
'
'
Option Explicit
'On Error Resume Next

Const ForWriting = 2 ' constant used for openning text files

Dim ToSystem  ' Computer Name of Target host system
Dim FromSystem  ' Computer Name of source host system
Dim VMStateCodes ' Array of VM State Codes - to display kinder status that integers
Dim VMStuff  ' Virtual Server Info instance
Dim SourcevmCollection ' Collection of VMs from Source Server
Dim vm   ' instance of a VM from collection loop
Dim objVM  ' instance of a VM
Dim MoveVM  ' instance of a VM
Dim colHardDisks  ' Collection of VHDs in a VM
Dim objDrive  ' instance of a VHD from collection loop
Dim objHardDisk  ' instance of a VHD
Dim NameLength   '   for tidy display of VM names
Dim MaxNameLength  '  for tidy display of VM names
Dim VMCount  ' counter for all VMs
Dim MoveCount  ' counter for VMs to move - VMs that are currently running (nicer demo!)
Dim MoveName  ' local var with VM name
Dim VMInfo(100)  ' array of VM names to be moved
Dim TargetHost  ' The server where all the runnning VM's will be moved
Dim TargetHostServer ' The server where all the runnning VM's will be moved
Dim TargetHostAvailable ' Boolean to track if the target host is pingable
Dim MyShell   '  Used to find local host name, if required
Dim MyEnvironment  '  Used to find local host name, if required

' Set Target host variables
'TargetHost = "192.168.0.3" ' put name or IP Address of target server here, NOT preceeded by two slashes
'TargetHost = "WS03VS01"  ' put name or IP Address of target server here, NOT preceeded by two slashes
TargetHost = "CHANGEME"  ' put name or IP Address of target server here, NOT preceeded by two slashes

'
' Should not need to change anything after here to get script to run!
'
TargetHostServer = "\\" & TargetHost
TargetHostAvailable = TRUE  ' setting default before ping test

Call Housekeeping()

VMCount = 0
Set SourcevmCollection = VMStuff.VirtualMachines
'
' Get maximum Name Length for Formatting - just makes some things look nice
'
For each vm in SourcevmCollection
 NameLength = Len(vm.Name)
 If MaxNameLength < NameLength Then
  MaxNameLength = NameLength
 End If
 '
 ' If VM is "Running" them target for WARM migration to other host system
 ' Here is where the systems are identifed at migration targets
 '
 if VMStateCodes(vm.State) = "Running" Then
  VMInfo(VMCount) = vm.Name
  VMCount = VMCount + 1
 End If
Next

For each vm in SourcevmCollection
   Wscript.Echo VbCrLf & "--------------------------------------------------------------------"
 Wscript.Echo "     " & vm.Name & Space(MaxNameLength - Len(vm.Name)) & " - State is:  " & VMStateCodes(vm.State)
Next

Wscript.Echo VbCrLf & "--------------------------------------------------------------------"
Wscript.Echo "--------------------------------------------------------------------"
if VMCount = 0 then
 Wscript.Echo VbCrLf & "No VMs are runnning, nothing to warm move"
End If

'****************************************************
' Engine is here!!!!!
'****************************************************
if TargetHostAvailable = TRUE Then
 For MoveCount = 1 to VMCount
  MoveName = VMInfo(MoveCount-1)
  Wscript.Echo "Processing " & MoveName & Space(MaxNameLength - Len(MoveName)) & " VM " & MoveCount & " of " & VMCount & "..."
  Set objVM = VMStuff.FindVirtualMachine(MoveName)

  ' Save State of all running VMs
  Call SaveVMState(MoveName)

  ' Generage .BAT and .VBS for each of the running VMs
  Call MakeScriptFiles(MoveName)

 Wscript.Echo VbCrLf & "--------------------------------------------------------------------"
 Next
End If

 

Wscript.Echo VbCrLf & "--------------------------------------------------------------------"
Wscript.Echo "--------------------------------------------------------------------"
'End Script

'******************************************************************************
'******************************************************************************
'******************************************************************************
'******************************************************************************
'*                                                                            *
'*                                                                            *
'*            Subroutines to make the stuff up top look nicer!                *
'*                                                                            *
'*                                                                            *
'*                                                                            *
'******************************************************************************
'******************************************************************************
'******************************************************************************
'******************************************************************************

'******************************************************************************
'*                                                                            *
'*     Do some if the ugly stuff that I didn't want to put at the front       *
'*     of the script                                                          *
'*                                                                            *
'******************************************************************************
Sub Housekeeping()

 Dim objPing ' use for ping test - poached from TechNet examples
 Dim objStatus  ' use for ping test - poached from TechNet examples

 Wscript.Echo VbCrLf & "--------------------------------------------------------------------"
 Wscript.Echo VbCrLf & "-                                                                  -"
 Wscript.Echo VbCrLf & "-                                                                  -"
 Wscript.Echo VbCrLf & "-                        WarmMove.VBS                              -"
 Wscript.Echo VbCrLf & "-                                                                  -"
 Wscript.Echo VbCrLf & "-                             by                                   -"
 Wscript.Echo VbCrLf & "-                                                                  -"
 Wscript.Echo VbCrLf & "-             John Kelbley                                 -"
 Wscript.Echo VbCrLf & "-                                                                  -"
 Wscript.Echo VbCrLf & "-                                                                  -"
 Wscript.Echo VbCrLf & "-    Finds running VM's running under Virtual Server 2005          -"
 Wscript.Echo VbCrLf & "-    on local system, pauses them, moves them to specified target  -"
 Wscript.Echo VbCrLf & "-    and unpauses them!                                            -"
 Wscript.Echo VbCrLf & "-                                                                  -"
 Wscript.Echo VbCrLf & "-    Please read comments in script carefully before running!      -"
 Wscript.Echo VbCrLf & "-                                                                  -"
 Wscript.Echo VbCrLf & "-    This is a sample, and is not meant for use in production.     -"
 Wscript.Echo VbCrLf & "-                                                                  -"
 Wscript.Echo VbCrLf & "-    Script expects NO paramameter - the name of the target        -"
 Wscript.Echo VbCrLf & "-    Virtual Server host is specified internally                   -"
 Wscript.Echo VbCrLf & "-    (I WANT YOU TO LOOK AT THE SCRIPT!!!!)                        -"
 Wscript.Echo VbCrLf & "-                                                                  -"
 Wscript.Echo VbCrLf & "-                                                                  -"
 Wscript.Echo VbCrLf & "-      Example:   cscript MoveWarm.VBS                             -"
 Wscript.Echo VbCrLf & "-                                                                  -"
 Wscript.Echo VbCrLf & "-                                                                  -"
 Wscript.Echo VbCrLf & "-                                                                  -"
 Wscript.Echo VbCrLf & "--------------------------------------------------------------------"

 ' Populate VM State Array
 VMStateCodes = Array("Invalid", "Turned Off","Saved","Turning On", "Restoring", "Running", "Paused", "Saving",_
    "Turning Off", "Merging Drives", "Deleting Machine")

 ' Create VS application instance
 Set VMStuff = WScript.CreateObject("VirtualServer.Application")

 Wscript.Echo VbCrLf & "Source Host System (this box!): " & FromSystem

 Wscript.Echo VbCrLf & "--------------------------------------------------------------------"

 Wscript.Echo "Default VM configuration path: " & VMStuff.DefaultVMConfigurationPath
 Wscript.Echo "Default VN configuration path: " & VMStuff.DefaultVNConfigurationPath

 ' Check to see if remote target system is accessible...
 '
 Wscript.Echo VbCrLf & "Pinging  " & TargetHost & " to see if warm migration can occour..."
 Set objPing = GetObject("winmgmts:{impersonationLevel=impersonate}").ExecQuery("select * from Win32_PingStatus where address = '" & TargetHost & "'")
 For Each objStatus in objPing
  If IsNull(objStatus.StatusCode) or objStatus.StatusCode<>0 Then
   TargetHostAvailable = FALSE
   WScript.Echo VbCrLf & "machine " & TargetHost & " is not reachable"
   WScript.Echo "CANNOT EXECUTE Warm migration" & VbCrLf 
  End If
 Next
 '***********************************************************************************
 ' should add a check to see if remote system has Virual Server installed, too!!!!!
 '***********************************************************************************
End Sub

 

'******************************************************************************
'*                                                                            *
'*     Saves the State of Running VMs - creating a "hibernated" VM            *
'*                                                                            *
'******************************************************************************
Sub SaveVMState(subVMName)
 Wscript.Echo Space(MaxNameLength + 12) & "Saving State"
 While objVM.State > 2 ' if the VM is not turned off of saved...
  if VMStateCodes(objVM.State)  = "Running" Then
   objVM.Save()
 End If
  wscript.sleep(1000)        
  Wscript.Echo Space(MaxNameLength + 12) & VMStateCodes(objVM.State)
 Wend
End Sub

'******************************************************************************
'*                                                                            *
'*     Generate .BAT and .VBS files to handle the migration of VM files       *
'*                                                                            *
'******************************************************************************
Sub MakeScriptFiles(subVMName)

 Dim LocalBatchFile 
 Dim LocalVBSFile 
 Dim LocalFSO
 Dim LocalFile
 Dim FileFSO
 Dim FileToCheck
 Dim FoundFolder
 Dim FromFileConfig
 Dim ToFileConfig
 Dim ConfigFolder
 Dim FromFileState
 Dim ToFileState
 Dim FromFileVHD
 Dim ToFileVHD
 Dim FromFileVUD
 Dim ToFileVUD
 Dim CommandString
 Dim objShell

 Set MoveVM = VMStuff.FindVirtualMachine(subVMName)
 Wscript.Echo VbCrLf & "Beginning Move Processing..."

 ' ***********************************************************************************************************************
 ' ***********************************************************************************************************************
 ' code to create .BAT to copy files and call .VBS to register config file and start image
 ' ***********************************************************************************************************************
 ' ***********************************************************************************************************************
 LocalVBSFile = subVMName & "_" & "local.vbs" 
 LocalBatchFile = subVMName & "_" & "local.bat" 

 Set LocalFSO = CreateObject ("scripting.FileSystemObject")
 Set LocalFile = LocalFSO.CreateTextFile(LocalBatchFile)

 ' start writing batch file
 LocalFile.WriteLine "@Echo off"
 LocalFile.WriteLine "Echo ---------------------------------------------------------"

 ' Construct destination UNC for Config File to copy
 ' Need the path for the parent directory of the file to copy to make Xcopy work right...this seemed easier than parsing the string!
 FromFileConfig = MoveVM.File
 Set FileFSO = CreateObject("Scripting.FileSystemObject")
 Set FileToCheck = FileFSO.GetFile(FromFileConfig)
 FoundFolder = FileFSO.GetParentFolderName(FileToCheck)
 ConfigFolder = FoundFolder ' Saving this for the VBS later...
 ToFileConfig = TargetHostServer & "\" & Left(FoundFolder, 1) & "$" & Mid(FoundFolder, 3, 1024)
 CommandString = "xcopy " & CHR(34) & FromFileConfig & CHR(34) & " " & CHR(34) & ToFileConfig & "\" & CHR(34) & " /Y"
 LocalFile.WriteLine "echo Copying " &  FromFileConfig & " to remote, please wait..."
 LocalFile.WriteLine CommandString
 LocalFile.WriteLine "Echo ---------------------------------------------------------"

 ' Construct destination UNC for Save State File n
 FromFileState = MoveVM.SavedStateFilePath
 Set FileFSO = CreateObject("Scripting.FileSystemObject")
 Set FileToCheck = FileFSO.GetFile(FromFileState)
 FoundFolder = FileFSO.GetParentFolderName(FileToCheck)
 ToFileState = TargetHostServer & "\" & Left(FoundFolder , 1) & "$" & Mid(FoundFolder, 3, 1024)
 CommandString = "xcopy " & CHR(34) & FromFileState & CHR(34) & " " & CHR(34) & ToFileState & "\" & CHR(34) & " /Y"
 LocalFile.WriteLine "echo Copying " &  FromFileState & " to remote, please wait..."
 LocalFile.WriteLine CommandString
 LocalFile.WriteLine "Echo ---------------------------------------------------------"

 ' Take Care of VHD (drive image) and VUD files (undo files) - can be more than one for a Guest...
 Set colHardDisks = MoveVM.HardDiskConnections
 For Each objDrive in colHardDisks
         FromFileVHD = objDrive.HardDisk.File
  Set FileFSO = CreateObject("Scripting.FileSystemObject")
  Set FileToCheck = FileFSO.GetFile(FromFileVHD)
  FoundFolder = FileFSO.GetParentFolderName(FileToCheck)
  ToFileVHD = TargetHostServer & "\" & Left(FoundFolder, 1) & "$" & Mid(FoundFolder, 3, 1024)
  CommandString = "xcopy " & CHR(34) & FromFileVHD & CHR(34) & " " & CHR(34) & ToFileVHD & "\" & CHR(34) & " /Y"
  LocalFile.WriteLine "echo Copying " &  FromFileVHD & " to remote, please wait...(big file!)"
  LocalFile.WriteLine CommandString
  LocalFile.WriteLine "Echo ---------------------------------------------------------"
        If MoveVM.Undoable Then
     FromFileVUD = objDrive.UndoHardDisk.File
   Set FileFSO = CreateObject("Scripting.FileSystemObject")
   Set FileToCheck = FileFSO.GetFile(FromFileVUD)
   FoundFolder = FileFSO.GetParentFolderName(FileToCheck)
   ToFileVUD = TargetHostServer & "\" & Left(FoundFolder, 1) & "$" & Mid(FoundFolder, 3, 1024)
   CommandString = "xcopy " & CHR(34) & FromFileVUD & CHR(34) & " " & CHR(34) & ToFileVUD & "\" & CHR(34) & " /Y"
   LocalFile.WriteLine "echo Copying " &  FromFileVUD & " to remote, please wait..."
   LocalFile.WriteLine CommandString
   LocalFile.WriteLine "Echo ---------------------------------------------------------"
  End If
 Next

 ' Call .VBS to register configuration file and start the VPC image
 LocalFile.WriteLine "Echo Calling script to register config file on remote and start VM..."
 LocalFile.WriteLine "CScript " & CHR(34) & LocalVBSFile & CHR(34) & " //NoLogo"

 LocalFile.WriteLine "Echo  End of Batch file"
 LocalFile.WriteLine "Echo ---------------------------------------------------------"
 LocalFile.Close  'close batch file

 ' ***********************************************************************************************************************
 ' ***********************************************************************************************************************
 ' code to create .VBS to register config file with Target Virtual Server
 ' ***********************************************************************************************************************
 ' ***********************************************************************************************************************
 Set LocalFSO = CreateObject ("scripting.FileSystemObject")
 Set LocalFile = LocalFSO.CreateTextFile(LocalVBSFile)

 ' start writing .VBS file
 ' LocalFile.WriteLine "On Error Resume Next"
 LocalFile.WriteLine "Set objVS = CreateObject(" & CHR(34) & "VirtualServer.Application" & CHR(34) & ", "  & CHR(34) & TargetHostServer & CHR(34) & ")"
 LocalFile.WriteLine "errReturn = objVS.RegisterVirtualMachine(" & CHR(34) & subVMName  & CHR(34) & "," & CHR(34) & ConfigFolder & CHR(34) & ")"
 LocalFile.WriteLine "Set objVM = objVS.FindVirtualMachine(" & CHR(34) & subVMName  & CHR(34) & ")"
 LocalFile.WriteLine "objVM.Startup()"

 LocalFile.Close  'close .VBS file
 
 Set objShell = Wscript.CreateObject("Wscript.Shell")
 CommandString = "%comspec% /c " & CHR(34) & LocalBatchFile & CHR(34)
 Wscript.Echo VbCrLf & "Command string:  " & CommandString
 objShell.Run(CommandString)

 Wscript.Echo VbCrLf & "End Move Processing"
End Sub