As far as I know, after some googling I couldn’t find any program that could take backups of Virtual Machines running on Hyper-V Core when the .VHD files were located on a Cluster Shared Storage.
If you read my previous post you can probably understand you aren’t able to install any advanced programs in the Core-version either because many required runtime files and libraries aren’t available.
Therefore I decided to write a PowerShell script that would export the machines to a network storage server.
I currently don’t have the required environment to develop or do any testing for this script. Therefore I cannot give you a customized script for your environment or needs.
Please also understand that this script is offered to you freely without any support. Feel free to make your own custom versions of it, while still mentioning me as author for the original script.
Updated 2010-05-12 14:40 UTC +1 (Various fixes/Added option to exclude some virtual machines)
This script can and should be scheduled on every Hyper-V node in your cluster. It can ofcourse be used for backing up VMs in a non-clustered environment. The syntax to execute the script is as follow:
Powershell “& ‘D:\powershell\CreateVmBackups.ps1′”
## Copyright 2010 Niklas Jumlin
## VM Handling code based off Tore Lervik.
##
## Create a backup of all the vm's
##
## Name this Hyper-V
$name = hostname
## Where do we wanna store the backups?
$dest = "\\nas1\Backups\Hyper-V Backup"
## Specify where to save the log
$log = "$dest\CreateVmBackups-$name.log"
## Excluded Virtual Machines, comma separated: "MACHINE1","MACHINE2"
$excludevm = "Exchangesrv","Win7Reference","WinXP-Pro"
## Email settings, seperate each recipient with comma and no spaces
$recipient = "user1@domain.com,user2@domain.com"
$from= "administrator@domain.com"
$server = "172.16.30.7"
## Dont bother these
$type = "Daily"
$currentday = get-date -uformat "%d"
$currentmonth = get-date -uformat "%m"
$date = get-date -uformat "%Y-%m-%d-%A"
$VM_Service = get-wmiobject -namespace root\virtualization Msvm_VirtualSystemManagementService
$ListofVMs = get-wmiobject -namespace root\virtualization Msvm_ComputerSystem -filter "ElementName <> Name"
## Write start time to logfile
$dttm = get-date -uformat "%Y-%m-%d %H:%M:%S"
Add-Content $log "$dttm :: Backup started"
## Start loop
foreach ($VM in [array] $ListOfVMs)
{
$VMReturnState = $VM.EnabledState
$VMName = $VM.ElementName
## This part will exclude Virtual Machines that are specified within the $excludevm array
if ($excludevm -contains $VMname)
{
echo "$dttm :: Excluding $VMName from Backup"
Add-Content $log "$dttm :: Excluding $VMName from Backup"
}
else
{
## If the virtual machines isn't specified to be excluded, then continue
if (($VM.EnabledState -eq 2) -or ($VM.EnabledState -eq 32768) -or ($VM.EnabledState -eq 32770))
{
$VM.RequestStateChange(32769)
echo "Saving the state of $VMName - $date"
$savestart = get-date -uformat "%Y-%m-%d %H:%M:%S"
Add-Content $log "$savestart :: $VMName : Saving the state"
}
while (!($VM.EnabledState -eq 32769) -and !($VM.EnabledState -eq 3))
{
Start-Sleep(1)
$VM = get-wmiobject -namespace root\virtualization -Query "Select * From Msvm_ComputerSystem Where ElementName='$VMName'"
}
if ([IO.Directory]::Exists("$dest\TmpDir\$VMName"))
{
[IO.Directory]::Delete("$dest\TmpDir\$VMName", $True)
}
## Export to Temp dir
echo "Exporting the VM: $VMName"
$exportstart = get-date -uformat "%Y-%m-%d %H:%M:%S"
Add-Content $log "$exportstart :: $VMName : Exporting started"
$status = $VM_Service.ExportVirtualSystem($VM.__PATH, $True, "$dest\TmpDir")
if ($status.ReturnValue -eq 4096)
{
$job = [Wmi]$status.Job
while (!($job.PercentComplete -eq 100) -and ($job.ErrorCode -eq 0))
{
Start-Sleep(5)
$job = [Wmi]$status.Job
echo $job.PercentComplete
}
}
## Store the files on in a temp directory before moving them to their location and then rename folder to date of backup.
## Check to see if directories exists before we move
## and create them if they dont exist
if (![IO.Directory]::Exists("$dest\$VMName"))
{
[IO.Directory]::CreateDirectory("$dest\$VMName")
}
if (![IO.Directory]::Exists("$dest\$VMName\$type"))
{
[IO.Directory]::CreateDirectory("$dest\$VMName\$type")
}
if (![IO.Directory]::Exists("$dest\$VMName\$type\$currentmonth"))
{
[IO.Directory]::CreateDirectory("$dest\$VMName\$type\$currentmonth")
}
## Move from Temp directory to correct directory, and rename to date of backup
[IO.Directory]::Move("$dest\TmpDir\$VMName", "$dest\$VMName\$type\$currentmonth\$date")
$exportdone = get-date -uformat "%Y-%m-%d %H:%M:%S"
Add-Content $log "$exportdone :: $VMName : Exporting completed"
## Bring virtual machine back online
$VM.RequestStateChange($VMReturnState)
## Compress folder with 7-zip (Normal compression level).
## Make sure to have 7-Zip Command Line Version (7za) in C:\Windows\
## 7za version when this script was written: 7-Zip 4.65 2009-02-03
## Copyright (c) 1999-2009 Igor Pavlov
$startcompress = get-date -uformat "%Y-%m-%d %H:%M:%S"
Add-Content $log "$startcompress :: $VMName : Compression started"
& 7za a -mx3 -mmt -tzip $dest\$VMName\$type\$currentmonth\$date.zip $dest\$VMName\$type\$currentmonth\$date
## Check to see if 7za succeeded.
if ($?) ## if previous command succeeded
{
## Delete folder if compression is finnished
[IO.Directory]::Delete("$dest\$VMName\$type\$currentmonth\$date", $True)
$allfinnish = get-date -uformat "%Y-%m-%d %H:%M:%S"
Add-Content $log "$allfinnish :: Done with $VMName"
Add-Content $log "Backup saved to: $dest\$VMName\$type\$currentmonth\$date.zip"
}
else
{
$allfinnish = get-date -uformat "%Y-%m-%d %H:%M:%S"
Add-Content $log "$allfinnish :: $VMName : An error occured during compression!"
}
}
echo "Done with $VMName $date"
}
$allfinnish = get-date -uformat "%Y-%m-%d %H:%M:%S"
Add-Content $log "$allfinnish :: Backup finnished"
Add-Content $log " "
## Cleanup previous month backups every 28th of current month
if ($currentmonth -eq 1)
{
$prevmonth = 12
}
else
{
$prevmonth = $currentmonth - 1
}
if (($currentday -eq 28) -and [IO.Directory]::Exists("$dest\$VMName\$type\$prevmonth"))
{
Add-Content $log "$allfinnish :: Deleted old backups ($dest\$VMName\$type\$prevmonth)"
[IO.Directory]::Delete("$dest\$VMName\$type\$prevmonth", $True)
}
## Email the logfile
$file = $log
$smtpServer = $server
$msg = new-object Net.Mail.MailMessage
$att = new-object Net.Mail.Attachment($file)
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$msg.From = $from
$msg.To.Add("$recipient")
$msg.Subject = "$type Backup ($allfinnish)"
$msg.Body = "Attached Logfile: $log"
$msg.Attachments.Add($att)
$smtp.Send($msg)
$att.Dispose()