PowerShell: Hyper-V Backup With Checkpoints/Snapshots

UPDATE: This post is old and intended as a walk through on how my original script was put together for those wishing to learn PowerShell. For the finished, up-to-date script please check out this post and you can download the script from my TechNet profile.

I previously posted a quick and dirty Hyper-V backup script that was very basic but did the job required at the time. I always wanted to revisit it and improve it.

I’m moving the change log and updates to this page, as this post is more about the script itself.

I started to look at improving the script by using the Export-VM cmdlet, however exporting to a network share requires that the Active Directory computer account of the server running the export have permission to the share, which may not be possible for some administrators. In my case, I’m backing up the VMs to a NAS appliance which I cannot add the necessary permissions to.

The finished script below will copy all files related to a VM into a folder which, should you need to restore, can be easily imported through the Hyper-V import function.

The script is intended to be run locally on a Hyper-V host and has been tested on Windows 10 Enterprise, Windows Server 2016 Datacenter Core, and Windows Server 2012 R2 Datacenter.

Here’s the complete script, scroll down for a detailed explanation of each section.

# -------------------------------------------
# Script: hyper-v-backup_v3-2.ps1
# Version: 3.2
# Author: Mike Galvin twitter.com/digressive
# Date: 24/04/2017
# -------------------------------------------

##Set Variables
$vs = $env:computername
#$vms = Get-VM | Where-Object {$_.State -eq 'Running'}
$vms = Get-VM
$backup = "\\nas\VM_Backups\$vs"

##Set Log Location
$log = "F:\scripts\$vs.log"

##Set Mail Config
$toaddress = "it@contoso.com"
$fromaddress = "$vs@contoso.com"
$subject = "$vs Hyper-V Backup Log"
$mailserver = "mail.contoso.com"

##Start Log
Start-Transcript $log

##For each VM do the following
ForEach ($vm in $vms) { 

   ##Create Directories
   New-Item "$backup\$($vm.name)\Virtual Machines" -ItemType directory -Force
   New-Item "$backup\$($vm.name)\VHD" -ItemType directory -Force
   New-Item "$backup\$($vm.name)\Snapshots" -ItemType directory -Force

   ##Stop the VM
   Stop-VM $vm -Verbose
   Start-Sleep -s 5

   ##Copy the config files and folders
   Copy-Item "$($vm.ConfigurationLocation)\Virtual Machines\$($vm.id)" "$backup\$($vm.name)\Virtual Machines\" -Recurse -Force -Verbose
   Copy-Item "$($vm.ConfigurationLocation)\Virtual Machines\$($vm.id).*" "$backup\$($vm.name)\Virtual Machines\" -Recurse -Force -Verbose

   ##Copy the VHD
   Copy-Item $vm.HardDrives.Path -Destination "$backup\$($vm.name)\VHD\" -Recurse -Force -Verbose

   ##Get the snapshots
   $snaps = Get-VMSnapshot $vm

       ##For each snapshot do the following
       ForEach ($snap in $snaps) { 

       ##Copy the snapshot config files and folders
       Copy-Item "$($vm.ConfigurationLocation)\Snapshots\$($snap.id)" "$backup\$($vm.name)\Snapshots\" -Recurse -Force -Verbose
       Copy-Item "$($vm.ConfigurationLocation)\Snapshots\$($snap.id).*" "$backup\$($vm.name)\Snapshots\" -Recurse -Force -Verbose

       ##Copy the snapshot root VHD
       Copy-Item $snap.HardDrives.Path -Destination "$backup\$($vm.name)\VHD\" -Recurse -Force -Verbose
       }

   ##Start the VM
   Start-VM $vm -Verbose
   Start-Sleep -s 60
}

##Stop Log
Stop-Transcript

##Send Mail
$body = Get-Content -Path $log | Out-String
Send-MailMessage -To $toaddress -From $fromaddress -Subject $subject -Body $body -SmtpServer $mailserver

##END

 

Let’s go through each section of the script.

##Set Variables
$vs = $env:computername
#$vms = Get-VM | Where-Object {$_.State -eq 'Running'}
$vms = Get-VM
$backup = "\\nas\VM_Backups\$vs"

##Set Log Location
$log = "F:\scripts\$vs.log"

##Set Mail Config
$toaddress = "it@contoso.com"
$fromaddress = "$vs@contoso.com"
$subject = "$vs Hyper-V Backup Log"
$mailserver = "mail.contoso.com"

Here, we set the name of the server the script is running on, along with the locations of the backup folder and the log folder. By default the script will backup all VMs on the server. 3.2 update: here we also set the parameters for the emailing of the log file.

##For each VM do the following
ForEach ($vm in $vms) { 

   ##Create Directories
   New-Item "$backup\$($vm.name)\Virtual Machines" -ItemType directory -Force
   New-Item "$backup\$($vm.name)\VHD" -ItemType directory -Force
   New-Item "$backup\$($vm.name)\Snapshots" -ItemType directory -Force

   ##Stop the VM
   Stop-VM $vm -Verbose
   Start-Sleep -s 5

   ##Copy the config files and folders
   Copy-Item "$($vm.ConfigurationLocation)\Virtual Machines\$($vm.id)" "$backup\$($vm.name)\Virtual Machines\" -Recurse -Force -Verbose
   Copy-Item "$($vm.ConfigurationLocation)\Virtual Machines\$($vm.id).*" "$backup\$($vm.name)\Virtual Machines\" -Recurse -Force -Verbose

   ##Copy the VHD
   Copy-Item $vm.HardDrives.Path -Destination "$backup\$($vm.name)\VHD\" -Recurse -Force -Verbose

Now we create the necessary destination folders, stop the VM, copy the configuration files (.vmcx and .vmrs for Windows 10/Windows Server 2016, .xml for Windows Server 2012 R2) and folders to the destination. For VMs with no snapshots/checkpoints the VHDs associated with the VM are copied, for VMs with snapshots/checkpoints, the current active snapshot files are copied the appropriate destination location.

   ##Get the snapshots
   $snaps = Get-VMSnapshot $vm

       ##For each snapshot do the following
       ForEach ($snap in $snaps) { 

       ##Copy the snapshot config files and folders
       Copy-Item "$($vm.ConfigurationLocation)\Snapshots\$($snap.id)" "$backup\$($vm.name)\Snapshots\" -Recurse -Force -Verbose
       Copy-Item "$($vm.ConfigurationLocation)\Snapshots\$($snap.id).*" "$backup\$($vm.name)\Snapshots\" -Recurse -Force -Verbose

       ##Copy the snapshot root VHD
       Copy-Item $snap.HardDrives.Path -Destination "$backup\$($vm.name)\VHD\" -Recurse -Force -Verbose
       }

For VMs with snapshots/checkpoints, this section copies the configuration files and folders to the backup location, along with the root VHD files which are not copied in the previous section when snapshots/checkpoints are present.

   ##Start the VM
   Start-VM $vm -Verbose
   Start-Sleep -s 60
}

##Stop Log
Stop-Transcript

##Send Mail
$body = Get-Content -Path $log | Out-String
Send-MailMessage -To $toaddress -From $fromaddress -Subject $subject -Body $body -SmtpServer $mailserver

When copy is complete, the VM is started and the script waits 60 seconds before moving on to the next VM. 3.2 update: Once all VMs have been backed up, the log file is closed and then emailed using the parameters specified at the top of the script.

I hope this helps you out. If you have any questions or comments, please tweet them to me.

-Mike

Follow Mike on Twitter: @Digressive

2 thoughts on “PowerShell: Hyper-V Backup With Checkpoints/Snapshots

  1. Pingback: A Quick and Dirty Hyper-V Backup Script | Stick To The Script!

  2. Pingback: Virtual Machine Backup for Hyper-V | Stick To The Script!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s