Skip to content
Menu
Jumlins TechBlog
  • Home
  • About
Jumlins TechBlog

Backup VMs in a Hyper-V Cluster (Core-version)

Posted on 2010-04-212012-03-06 by Niklas Jumlin

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

Share on Facebook

Leave a Reply

Your email address will not be published. Required fields are marked *

Recent Posts

  • Office 365 Groups/Teams powershell reporting script 2019-03-25
  • [Powershell Script] Convert ImmutableID 2018-09-12
  • Script to Schedule Mem and or vCPU up/downgrade 2015-05-13
  • Backing up VMs in Hyper-V 2013-01-09
  • Windows Server 2012 as a Workstation 2012-09-13

Archives

Categories

  • Exchange 2003
  • Exchange 2010
  • HTML/CSS
  • Imagick
  • JavaScript
  • Linux
  • Misc
  • Office 365 / Azure
  • PHP
  • PowerCLI
  • PowerShell
  • Projects
  • Scripts
  • Server
  • System Administration
  • VMware
  • Winbatch
  • Windows
  • Windows Server 2012

Recent Posts

  • Office 365 Groups/Teams powershell reporting script 2019-03-25
  • [Powershell Script] Convert ImmutableID 2018-09-12
  • Script to Schedule Mem and or vCPU up/downgrade 2015-05-13
  • Backing up VMs in Hyper-V 2013-01-09
  • Windows Server 2012 as a Workstation 2012-09-13
©2025 Jumlins TechBlog | Powered by WordPress & Superb Themes