Hyper-V backup script

This script can backup your Hyper-V to a specific folder with an optional list of VM from a file or it can get a list from Hyper-V directly and can rotate backup set based on the optional parameters.

If you are running Windows 2008 R2 then you might want to use an ExportVirtualSystemEx function instead ExportVirtualSystem function.

Usage:

cscript program.vbs exportDirectoryName [c:\fileOfVMList.txt] [d3|w1]

Further explain:

[c:\fileOfVMList.txt] (optional)
It is an optional file that contains a list of VM for this script to run backup

[d3|w1] (optional)
d3 is for Daily schedule with 3 rotation
w1 is for Weekly schedule with 1 rotation

Note: the above assume to will have a scheduled task created and this script will just parse the text and create the folder for rotation base on the number


' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
' IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
' FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
' THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
' LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
' FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
' DEALINGS IN THE SOFTWARE.
' Hyper-V export all VM
' By Chanh Ong
' Purpose: Try to create Hyper-V backup solution for Hyper-V server
' Date: 04/2010

option explicit

Const OPEN_FILE_FOR_READING = 1

dim objWMIService
dim managementService
dim fileSystem
dim vmCollection
dim vm
dim backupDir
dim vmList

Dim StdIn, StdOut, gFso, gShell, gNetwork
Set StdIn = WScript.StdIn
Set StdOut = WScript.StdOut
Set gFso = WScript.CreateObject("Scripting.FileSystemObject")
Set gShell = WScript.CreateObject ("WSCript.shell")
Set gNetwork = Wscript.CreateObject("WScript.Network")

const JobStarting = 3
const JobRunning = 4
const JobCompleted = 7
const wmiStarted = 4096
const wmiSuccessful = 0

dim strComputer, objArgs, exportDir, dow, which
dim freq
strComputer = "."
set fileSystem = Wscript.CreateObject("Scripting.FileSystemObject")
set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\virtualization")
set managementService = objWMIService.ExecQuery("select * from Msvm_VirtualSystemManagementService").ItemIndex(0)

set objArgs = WScript.Arguments
if WScript.Arguments.Count > 0 then
exportDir = objArgs.Unnamed.Item(0)
vmList = GetVMList(objArgs)
freq = GetFreq(objArgs)
else
WScript.Echo "usage: cscript program.vbs exportDirectoryName [c:\fileOfVMList.txt] [d3|w1] "
WScript.Quit(1)
end if

if (isArray(vmList) = true) then
backupDir = RotateFolder(exportDir, freq)
WriteLog "Start at " & now & " backup to folder: " & backupDir
call ExportVM(vmList, backupDir)
WriteLog "Stop at " & now
else
WScript.Echo "Nothing to do!"
end if
' end main

Function RotateFolder(exportDir, freq)
dim which, fDate, f, fName, count, nextFolder, nextNumber, thisFolder, prevFolder
dim backupDir, baseDir, rotate
nextNumber = 1

rotate = right(freq,1)
count = rotate
' fName = left(freq,1)
' fName = left(freq,1) & "-" & DatePart("h", now)
fName = left(freq,1) & "-" & DatePart("h", now) & "-" & DatePart("n", now)

backupDir = exportDir & "\" & fName & "."
baseDir = backupDir & "1"
do
thisFolder = backupDir & count
nextFolder = backupDir & count - 1
If (gFso.FolderExists(thisFolder) = true and gFso.FolderExists(nextFolder) = true and count = rotate) Then
call doRun("rmdir /s /q "&thisFolder)
WScript.Sleep 1000
end if
If (count <> rotate) Then
prevFolder = backupDir & count + 1
If (gFso.FolderExists(thisFolder) = true) then
call doRun("move " & thisFolder & " " & prevFolder)
WScript.Sleep 1000
end if
end if
count = count - 1
loop while (count <> 0)
call MakeBackupFolder(baseDir)
RotateFolder = baseDir
end Function

sub MakeBackupFolder(bkupDir)
If (gFso.FolderExists(bkupDir) = true) Then
call doRun("rmdir /s /q "&bkupDir)
WScript.Sleep 1000
end if
call doRun("mkdir "&bkupDir)
WScript.Sleep 1000
end sub

Function GetFreq(objArgs)
if (len(objArgs(2))<>0 and objArgs.Count > 1) then
GetFreq = objArgs(2)
else
GetFreq = "d2"
end if
end Function

Function GetVMList(objArgs)
if (len(objArgs(1))<>0 and objArgs.Count > 0) then
WScript.Echo "Get VM list from file: " & objArgs(1)
GetVMList = getVMFromFile(objArgs(1))
else
WScript.Echo "Get VM list from HyperV"
GetVMList = GetVMListFromHyperV()
end if
end Function

Function GetVMListFromHyperV()
dim oneVM, count
count = 0
Set vmCollection = objWMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem",,48)
dim vmList(100)
For Each oneVM in vmCollection
vmList(count) = oneVM.ElementName
count = count + 1
next
GetVMListFromHyperV = vmList
end Function

sub ExportAllVM_old(expDirectory)
dim oneVM
call outHeader(" Start export to " & expDirectory)
Set vmCollection = objWMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem",,48)
For Each oneVM in vmCollection
if oneVM.Caption <> "Hosting Computer System" then
stopOneVM(oneVM)
if ExportVirtualSystemEx(oneVM, expDirectory) then
vmStart(oneVM)
WriteLog " at " & now & " : Finish export"
else
WriteLog "ExportVirtualSystemEx Failed for " & oneVM.ElementName
end if
end if
Next
end sub

sub ExportAllVM(expDirectory)
dim vmList
vmList = GetVMListFromHyperV()
call ExportVM(vmList, expDirectory)
end sub

sub ExportVM(VMList, expDirectory)
dim oneVM, eachVM
' if (isArray(VMList) = true) then
call outHeader(" Start export to " & expDirectory)
For Each eachVM in VMList
' WScript.Echo "VM: " & eachVM
if (eachVM<>"") then
set oneVM = GetComputerSystem(trim(eachVM))
if oneVM.Caption <> "Hosting Computer System" then
stopOneVM(oneVM)
' un-comment this line if you are using Windows 2008 R2
' if ExportVirtualSystemEx(oneVM, expDirectory) then
' This is for Windows 2008 and R2
if ExportVirtualSystem(oneVM, expDirectory) then
vmStart(oneVM)
WriteLog " at " & now & " : Finish export for VM: " & oneVM.ElementName
else
WriteLog "ExportVirtualSystemEx Failed for " & oneVM.ElementName
end if
end if
end if
Next
' end if
end sub

Function ExportVirtualSystem(computerSystem, exportDirectory)
dim objInParam, objOutParams
ExportVirtualSystem = false
if Not fileSystem.FolderExists(exportDirectory) then
fileSystem.CreateFolder(exportDirectory)
end if
set objInParam = managementService.Methods_("ExportVirtualSystem").InParameters.SpawnInstance_()
objInParam.ComputerSystem = computerSystem.Path_.Path
objInParam.CopyVmState = true
objInParam.ExportDirectory = exportDirectory
set objOutParams = managementService.ExecMethod_("ExportVirtualSystem", objInParam)
if objOutParams.ReturnValue = wmiStarted then
if (WMIJobCompleted(objOutParams)) then
ExportVirtualSystem = true
end if
elseif (objOutParams.ReturnValue = wmiSuccessful) then
ExportVirtualSystem = true
else
WriteLog Format1("DefineVirtualSystem failed with ReturnValue {0}", wmiStatus)
end if

End Function

'-----------------------------------------------------------------
' Export a virtual system (R2)
'-----------------------------------------------------------------
Function ExportVirtualSystemEx(oneVM, expDirectory)
dim objInParam, objOutParams
dim query
dim computer
dim exportSettingData
dim computerSystem
ExportVirtualSystemEx = false
if Not fileSystem.FolderExists(expDirectory) then
fileSystem.CreateFolder(expDirectory)
end if
set computerSystem = GetComputerSystem(oneVM.ElementName)
' set managementService = objWMIService.ExecQuery("select * from Msvm_VirtualSystemManagementService").ItemIndex(0)
set objInParam = managementService.Methods_("ExportVirtualSystemEx").InParameters.SpawnInstance_()
objInParam.ComputerSystem = computerSystem.Path_.Path
query = Format1("ASSOCIATORS OF {{0}} WHERE resultClass = Msvm_VirtualSystemExportSettingData", computerSystem.Path_.Path)
set exportSettingData = objWMIService.ExecQuery(query).ItemIndex(0)
exportSettingData.CopyVmStorage = true
exportSettingData.CopyVmRuntimeInformation = true
exportSettingData.CreateVmExportSubdirectory = true
exportSettingData.CopySnapshotConfiguration = 0
objInParam.ExportSettingData = exportSettingData.GetText_(1)
objInParam.ExportDirectory = expDirectory
set objOutParams = managementService.ExecMethod_("ExportVirtualSystemEx", objInParam)
if objOutParams.ReturnValue = wmiStarted then
if (WMIJobCompleted(objOutParams)) then
ExportVirtualSystemEx = true
end if
elseif (objOutParams.ReturnValue = wmiSuccessful) then
ExportVirtualSystemEx = true
else
WriteLog Format1("ExportVirtualSystemEx failed with ReturnValue {0}", objOutParams.ReturnValue)
end if
End Function

'-----------------------------------------------------------------
' Handle wmi Job object
'-----------------------------------------------------------------
Function WMIJobCompleted(outParam)
dim WMIJob, jobState, nextProgress
set WMIJob = objWMIService.Get(outParam.Job)
WMIJobCompleted = true
jobState = WMIJob.JobState
nextProgress = 10
WriteLog " at " & now & " : Start export"
while jobState = JobRunning or jobState = JobStarting
if (nextProgress = WMIJob.PercentComplete) then
nextProgress = WMIJob.PercentComplete + 10
WriteLog Format1(" In progress... {0}% completed.",WMIJob.PercentComplete)
end if
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

Sub WriteLog(line)
dim fileStream, filename
' filename = ".\ExportHyperVLog.log"
filename = backupDir&"\ExportHyperVLog.log"
' WScript.Echo "write log to: "&filename
set fileStream = fileSystem.OpenTextFile(filename, 8, true)
WScript.Echo line
fileStream.WriteLine line
fileStream.Close
End Sub

Function Format2(myString, arg0, arg1)
Format2 = Format1(myString, arg0)
Format2 = Replace(Format2, "{1}", arg1)
End Function

Function Format1(myString, arg0)
Format1 = Replace(myString, "{0}", arg0)
End Function

Function VMCode(VMStateCode)
Select Case VMStateCode
Case 2 VMCode = "Running"
Case 3 VMCode = "PowerOff"
Case 4 VMCode = "ShuttingDown"
Case 10 VMCode = "Reset"
Case 32768 VMCode = "Paused"
Case 32770 VMCode = "Starting"
Case 32771 VMCode = "SnapshotInProgress"
Case 32772 VMCode = "Migrating"
Case 32773 VMCode = "Saving"
Case 32774 VMCode = "Stopping"
Case 32776 VMCode = "Pausing"
Case 32777 VMCode = "Resuming"
Case 32769 VMCode = "Saved"
Case Else VMCode = "Unclassified"
End Select
end Function

Function VMInfo(oneVM)
dim VMStateCode, VMState
VMStateCode = oneVM.EnabledState
VMState = VMCode(VMStateCode)
VMInfo = oneVM.ElementName & Space(20 - Len(oneVM.ElementName)) & oneVM.Description & Space(35 - Len(oneVM.Description)) & VMState & " (" &VMStateCode & ")"
end Function

Function GetComputerSystem(vmElementName)
On Error Resume Next
dim query
query = Format1("select * from Msvm_ComputerSystem where ElementName = '{0}'", vmElementName)
set GetComputerSystem = objWMIService.ExecQuery(query).ItemIndex(0)
if (Err.Number <> 0) then
WriteLog Format1("Err.Number: {0}", Err.Number)
WriteLog Format1("Err.Description:{0}",Err.Description)
WScript.Quit(1)
end if
End Function

Function theVMState(vmName)
dim computerSystem
set computerSystem = GetComputerSystem(VMName)
theVMState = VMCode(computerSystem.EnabledState)
end Function

sub vmChange(oneVM, changeType)
dim VMList, computerSystem, vmName
vmName = oneVM.ElementName
WriteLog VMInfo(oneVM)
set computerSystem = GetComputerSystem(VMName)
' 2 = start, 3 = stop and 32769 = save state
computerSystem.RequestStateChange(changeType)
end sub

Function vmStart(oneVM)
call vmChange(oneVM, 2)
end Function

Function vmStop(oneVM)
call vmChange(oneVM, 3)
end Function

Function vmStartx(oneVM)
dim VMList, computerSystem, vmName
vmName = oneVM.ElementName
WriteLog VMInfo(oneVM)
set computerSystem = GetComputerSystem(VMName)
' 2 = start, 3 = stop and 32769 = save state
computerSystem.RequestStateChange(2)
end Function

Function vmShutdown(oneVM)
dim vmShut, VMGuid, computerSystem, vmName
WriteLog VMInfo(oneVM)
vmName = oneVM.ElementName
set computerSystem = GetComputerSystem(VMName)
VMGuid = computerSystem.Name
Set vmShut = objWMIService.ExecQuery("SELECT * FROM Msvm_ShutdownComponent WHERE SystemName='" & VMGuid & "'")
if VMCode(computerSystem.EnabledState) = "Running" then
vmShutdown = vmShut.ItemIndex(0).InitiateShutdown(True,"Scripted Shutdown")
end if
end Function

Function stopOneVM(oneVM)
dim vmReturn, VMState, count, vmName
vmName = oneVM.ElementName
WriteLog " at " & now & " : Stopping..."
count = 0
VMState = theVMState(vmName)
If VMState = "Running" then
vmReturn = vmShutdown(oneVM)
do
if (count = 19) then
vmStop(oneVM)
end if
count = count + 1
VMState = theVMState(vmName)
WriteLog VMInfo(oneVM)
WScript.Sleep 3500

loop while (VMState <> "PowerOff" and count < 20)
End if
end Function

Function stopAllVM(vmList)
dim oneVM, vmName
For Each oneVM In vmList
if oneVM.Caption <> "Hosting Computer System" then
' vmShutdown(oneVM.ElementName)
stopOneVM(oneVM.ElementName)
End if
Next
end Function

Function startAllVM(vmList)
dim oneVM, vmName
For Each oneVM In vmList
if oneVM.Caption <> "Hosting Computer System" then
vmStart(oneVM)
End if
Next
end Function

sub outHeader(outType)
' WriteLog VbCrLF
WriteLog " at " & now & " : " & outType
WriteLog "Name Description State "
WriteLog "------------------- ---------------------------------- -------------"
end sub

sub doRun(cmd)
WScript.Echo cmd
gShell.run "cmd /C " &cmd
end sub

Function getDOW(TheDate)
dim weekday
weekday = DatePart("w", TheDate)
Select Case weekday
Case 1 getDOW = "Sun"
Case 2 getDOW = "Mon"
Case 3 getDOW = "Tue"
Case 4 getDOW = "Wed"
Case 5 getDOW = "Thu"
Case 6 getDOW = "Fri"
Case 7 getDOW = "Sat"
Case 1 getDOW = "Sun"
End Select
End Function

' Read a file eg: larray=rdFile("pc.txt")
Function getVMFromFile(filespec)
Dim oInFile
Dim sData, lArray
dim gdebug
gdebug = false
' WScript.Echo "file:" & filespec
If (gFso.FileExists(filespec)) Then
Set oInFile = gFso.OpenTextFile(filespec, OPEN_FILE_FOR_READING)
lArray = Split(oInFile.ReadAll, vbNewline)
if gdebug = true then
For each sData In lArray
WScript.Echo "line:" & sData
Next
end if
oInFile.Close
getVMFromFile = lArray
end if
end Function

Forums: