Home
Categories: Hyper-V, PowerShell, Windows Server Posted by Tore Lervik on 6/1/2008 6:30 PM | Comments (58)

PowerShellPowershell script for creating snapshots

The script creates a snapshot for each VM on the Hyper-V server.
Then it removes all snapshots older than 6 days (except the ones made on a Sunday).

CreateVmSnapshots.ps1 (1.01 kb)

 

Powershell script for exporting(backup) Virtual Machines

The script exports all the VM's to a given backup location.
It pauses each VM if it's running, exporting and turn the VM back on if it was running before.

CreateVmBackups.ps1 (1.75 kb)

 

Task Scheduling

Combine these two scripts with the Task Scheduler and you have a fully automated backup system!
To run a Powershell script in Task Scheduler enter Powershell as program and "& 'C:\Hyper-V\CreateVmSnapshots.ps1'" as argument.

PS: Run the scripts once to see that they work before adding them to the Task Scheduler, as no errors will be shown if they fail in the Task Scheduler.

Comments

Fed on 7/11/2008 10:20 PM Awesome! thanks for posting these scripts. I have been looking for something to backup our Hyper-V images.
Fed on 7/16/2008 10:55 PM Can you discuss how a restore using your scripts would work? Is it a matter of copying the .VHD over the "broken" one?
TG on 8/13/2008 9:07 AM I created a task scheduler task for your scripts and it looks like for some reason the System process is holding open handles to the .avhd files.  The snapshot metadata is getting removed when it's older than 4 days but the .avhd files are stuck.  I can't get rid of them because the system process is holding on.  Any idea why this might be happening?
Tore Lervik on 8/13/2008 8:20 PM The .avhd files are not removed because the VM that the .avhd belongs to have't been turned off or in a saved state since the snapshot was removed. Try saving the state on that VM and it should start merging and removing the snapshot .avhd's.
Shiva on 8/28/2008 2:48 PM When I execute the below command

$ListofVMs = get-wmiobject -namespace root\virtualization Msvm_ComputerSystem -filter  "ElementName <> Name"

I get $ListofVMs -eq $null, but I have 5 VMs on the server.


And when I execute the command

$ListofVMs = get-wmiobject -namespace root\virtualization Msvm_ComputerSystem

it return only one element in the array which refers to the server on which the VMs are hosted.

Can you please tell me if I have to start any service to make the command work as expected.

Thanks
Shiva on 9/1/2008 4:21 PM I resolved the issue by opening "Power Shell" with Admin rights (open as Administrator), as in Win Server 2008, even when you are Admin on the machine, you are not really running as an Admin.

Please delete my entries if it may not be helpful to others.

Matt on 10/6/2008 9:32 PM Nice scripts.  I used a different PowerShell script I found out there to create a batch file for offline (non-snapshot) backups.  It's different than yours in that you can specify which VMs to backup, and in what order to shutdown or power-on the virtual machines.  Check it out at:
bunkerhollow.com/.../...kup-script-batch-file.aspx

Thanks for your scripts, I like being able to export all my VMs quickly and easily.
Pascal on 10/7/2008 11:15 PM Hi Tore,
thanks for your great script!
But I have a problem. I'm loged in on my Server as local Administrator and opened the PowerShell-Windows with Run As Administrator. When I start the script, I get the error:

Exporting VM
1
Ecxeption calling "Move" with "2" argument(s): "Could not find a part of the path "C:\temp\TmpDir\TestVM'."
At C:\scripts\test\CreateVmBackups.ps1:53 char:23
+ [IO.Directory]::Move ( (((( "$dest\TmpDir\$VMName", "$dest\$VMName")
Done with TestVM

If I look in your script, I see no reason to manually create the TmpDir and VMName Folders, right?
Any Idea what could solve this error?
Regards, Pascal
pascal on 10/23/2008 5:43 PM please delete my comment, it was a mistake...
ups on 10/24/2008 11:33 AM Before run script install SCVMM

Get-pssnapin -registered | add-pssnapin
Get-VMMServer -ComputerName NameServer
Get-VM | where { $_.Name -eq "NameVirtualMashine" } | New-VMCheckpoint

where
NameServer - Host-PC
NameVirtualMashne - VM

Enjoy ))
mathew on 11/18/2008 9:07 AM Hi, you saved me a lot of time on searching an self-writing a script!

I tweaked your procedure a little my usage, using our secondary SAS-Disks of the Server for export. This way "down-time" of each VM can be put down to a minumum, while afterwards the exported files are backed up to a slower NAS. - Without need for an expensive SAN because we only have one Hyper-V Host.
Robert on 11/18/2008 9:36 PM I'm having the same problem as Pascal said above. However, he edited his saying it was a mistake. What am I doing wrong? I'm getting the "Could not find part of the path" error as well. I've tried several machines and all do the same.

On another note, in case anybody wants to know, it's pretty easy to filter for certain machines in this script. For instance, I changed the end of line 7 from...

-filter "ElementName <> Name"

to...

-filter 'ElementName LIKE "%Production%"'

This filtered the script to only do my machines that have the word "Production" in the name (that's how I name my production versus testing VM's).
Robert on 11/18/2008 9:53 PM Nevermind, I figured it out now. I was probably doing the same as Pascal. I was trying to use the $dest directory as a mapped network drive. This is not allowed per the ExportVirtualSystem...it must be a local drive.
Karl on 12/12/2008 11:33 PM When trying to run the vmbackups script, if the path is not local, it fails. I wanted to backup to a nas device, but it won't let me. Any thoughts?
Dave on 12/16/2008 2:00 PM I'm also looking for a way to backup to a non-local path, i.e. a UNC path. Any ideas how I could best do this?

Many thanks!
Tore Lervik on 12/16/2008 3:10 PM Hi Dave,
I don't think the export function in hyper-v support a non-local path.
Dave on 12/16/2008 3:59 PM Tore, thank you for your response.

What I'm looking for is something fairly simple. It would be sufficient if I could shut down a VM (even if I have to name this manaully in the script), copy the VHD to a file share (I also don't mind adding the path to the file directly) and then restart the VM. I could simply duplicate this for each VM on the host only having to change the name of the VM each time.
I'm not much of a scripting person and have been searching the web for a solution.

Any pointers would be really appreciated.

Cheers,
Tore Lervik on 12/17/2008 8:34 PM Dave,
See in the backup script how I backup to one location and then move the finished backup to another folder.
That move command can be used between shares and other paths, so you should be able to modify the script to backup to a local path and then copy the files to a share.
hasdou on 12/18/2008 4:02 AM Hi,

I run VM's on a W2008 core edition.
I would like to remotly run the scripts to backup my vm's ... is there any changes in my case (cannot making it work) ?

Thank you for helping,
Joe Boyce on 1/9/2009 1:15 PM Hi guys,

Loving the scripts Tore

Not to familiar with powershell as of yet!

How can I edit this script to backup only specific virtual machines?

Thanks,

Joe
Tore Lervik on 1/20/2009 8:24 PM I think you can do that by changing this line
$ListofVMs = get-wmiobject -namespace root\virtualization Msvm_ComputerSystem -filter  "ElementName <> Name"  
Into something like this
$ListofVMs = get-wmiobject -namespace root\virtualization Msvm_ComputerSystem -filter  "ElementName = 'Name of your VM'"  

Have't tested it though.
Kampanye Damai Pemilu Indonesia 2009 on 2/4/2009 12:09 AM @Tore Levik
I am gonna try it. thanks!
adam on 2/11/2009 11:11 PM HI,

What is syntax to use this scripts , i have around 50 VM's , i am not good at scripting, please help.

Thanks!

ADam
Tore Lervik on 2/11/2009 11:14 PM Hi Adam,

There are no syntax on these scripts, just run them and they will do what their designed to do.
Open it up with Notepad and see what's inside, and you might want to change the $dest variable to something on your system.
adam on 2/11/2009 11:53 PM Thanks much Tore, I figured the issue, FIrst i need to set the Execution policy to unrestrict in PS and run as admin , that helped .

And one more quick question please correct me if i am wrong, Snapshot files(AVHD) will have latest changes what we make in VM , but not the VHD file , so if we move the VM to another host without the snapshot we end lossing everything if we did not merge the AVHD with VHD. my question is how do we overcome this to make sure the VHD is upto date with the changes what we make to VM.

Sorry for the lenghty post.

Thanks!
ADAM

Tore Lervik on 2/11/2009 11:58 PM Hyper-V's .avhd format works like a differensial disk. If you move the .vhd without the .avdh's you end up loosing what was added after the .avhd was created.

The export function will however move both the vhd and the .avhd snapshots.
Cedric on 2/18/2009 6:45 PM Working perfectly every days to replace the poor WBS2008.

Thank you very much for this great job !!!
Matt B on 2/20/2009 9:05 PM I have everything setup as directed and the script works wonderfully when I try executing it manually.

However, when I try to run through the task scheduler it fails. When I execute this manually through powershell it prompts with a security warning, "Do you want to run...  {D} Do not run {R} Run Once.." Is this an implication that my rights are not setup correctly?

I'm running this task under the local admin account and I've included the correct path to my script in my action list, "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe & 'C:\Hyper-V\CreateVmSnapshots.ps1"

Please help! Thank you!
Tore Lervik on 2/26/2009 7:03 PM Matt B,

I think your powershell script rights are not setup correctly.
Check out this page on how to run powershell scripts. www.microsoft.com/.../run.mspx
Joe on 4/5/2009 5:48 PM Hello Tore,

I like your script and have recommended it to my readers at VMinstall.com. I know how important a good image backup of the complete virtual machine is and your script is a step in the right direction. Too often system admins get hung up trying to do a full system restore from a legacy backup and loose the system state in the process. Your script allows for a complete restore, not a system rebuild. Good going Tore! You can read my post at www.vminstall.com/.../.

Thanks,
Joe
Pete Solano on 4/22/2009 5:29 PM I am trying to run this script on a server running 6 VM's.  However, it is not working.  I do not know what I have to change on the script to allow it to work for me.  Could you please help me?  I have edited the script to the following:

##
##  Take a snapshot of all the vm's
##

$VM_Service = get-wmiobject -namespace root\virtualization Msvm_VirtualSystemManagementService
$ListofVMs = get-wmiobject -namespace root\virtualization Msvm_ComputerSystem -filter  "ElementName <> Name "  

if ($VM_Service -eq $null) { write-Host "VM_Service is null"; break; }
if ($ListofVMs  -eq $null) { write-Host "ListofVMs is null"; break; }


foreach ($VM in [array] $ListOfVMs)
{
  $VM_service.CreateVirtualSystemSnapShot($VM.__PATH)
}



##
##  Giving Hyper-V 30 sec to take the snapshots
##

Start-Sleep(30)



##
##  Remove snapshots older than 6 days (except Sundays)
##

$VSVR = get-wmiobject -namespace root\virtualization -class Msvm_ComputerSystem
$VSSNAP = get-wmiobject -namespace root\virtualization -class Msvm_VirtualSystemSettingData -filter "SettingType = 5"

if (!($VSSNAP -eq $null))
{
  foreach ($SS in [array] $VSSNAP)
  {
    if ($SS.ConvertToDateTime($SS.CreationTime) -le [DateTime]::Now.AddDays(-6).Date)
    {
      if (!($SS.ConvertToDateTime($SS.CreationTime).DayOfWeek -eq 0))
      {
        $VM_Service.RemoveVirtualSystemSnapshot($SS)
        Start-Sleep(3)
      }
    }
  }
}


********

The script is failing for me at VM-Service

Any help would be greatly appreciated!

Tore Lervik on 4/22/2009 7:34 PM Hey Pete,

Have you tried to do some of this stuff in PowerShell directly?
You say VM_service is failing, might it be because of wrong credentials?

The script is pretty basic, so there's really not much logic that could fail.
Joannes Vermorel on 5/16/2009 1:08 PM Thanks, the VM export script is really nice. We have been using it quite easily. Although, there is a minor defect in the current script: basically, if a VM fails at being exported because there is no enough disk space, the process is failing silently. It would be much better to "advertise" the backup failure.
Mickel Mamdouh on 5/23/2009 11:13 PM Dear Who Want To Backup VMs To SAN Or Non-Local Drivers,
Now You Can Do So , If You Use Mounted Disk Drive to Local One .
First When You Format The SAN Image (LUN) Don't Assign Letter to it But Mount the new Volume to a NTFS Folder Located in Local Drive.you can create NTFS Folder by create a normal Folder in a NTFS Drive
Then Use the Mounted Disk As A backup Driver and Change the $dest="C:\...." To
C:\Backup
Where the C: is a Local Disk(NTFS) and \Backup Folder is the Mounted Drive (NTFS Folder).
Thank You ...
Tom Partosh on 10/7/2009 4:24 PM I have Hyper-V host on server 2008 R2.
When I run the script to create snapshots, I get this error:


You cannot call a method on a null-valued expression.
At C:\Temp\VMsnapshots.ps1:11 char:41
+     $VM_service.CreateVirtualSystemSnapShot <<<< ($VM.__PATH)
    + CategoryInfo          : InvalidOperation: (CreateVirtualSystemSnapShot:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

On a different server with windows 2008 it was working great and still worked after I upgraded it to R2.

Any ideas?

Thank you
Juan Larrayoz on 10/7/2009 7:09 PM Dear, who change the directory, i want to create de snapshots in a different directory.


Thank you.
Jonathan Cohen on 10/12/2009 1:22 AM This works great! Do we have a script to import a VM back?
Karl on 10/20/2009 4:12 PM I can't get these scripts to run anymore since upgrading to Server 2008 R2.
Kevin on 10/22/2009 5:34 PM I know this is an old post, but MS specifically recommends *against* using snapshots as a backup... (not that the scripts are without use - I'm using a modified version to export the VM configurations).

technet.microsoft.com/.../dd560637(WS.10).aspx
Tore Lervik on 10/23/2009 12:24 PM Kevin: A snapshot is NOT a backup in the sense of a system crash. It's just a point in time that can easily be reverted to if something changes that you want to revert from.
Nicolas on 10/26/2009 5:52 PM Hi, thanks a lot for this script.
I am trying to edit "Powershell script for creating snapshots". I would like the snapshot to be taken every 3hours, I tried to edit the following :

    if ($SS.ConvertToDateTime($SS.CreationTime) -le [DateTime]::Now.AddDays(-6).Date)

Someone can help ? My experience with powershel is less than 0



Tore Lervik on 10/26/2009 5:55 PM Hey Nicolas,

The code you pasted only removed all snapshots older than 6 days. For the script to take a snapshot every 3 hours you need to run the task with the Task Scheduler every 3 hours.
Nicolas on 10/26/2009 5:55 PM I made a mistake, would like snapshot to be DELETED every 3 hours.
Tore Lervik on 10/26/2009 6:00 PM Look at the AddDays() method, change it to -3 (Now - 3 days = 3 days ago).
Nicolas on 10/26/2009 6:03 PM Sorry Wink but I asked for 3 HOURS ago. I tried to change the code like that :

if ($SS.ConvertToDateTime($SS.CreationTime) -le [DateTime]::Now.AddHours(-3).Date)
but it doesn't work

Thanks a lot for your help guy !
Bigot Arnaud on 11/20/2009 6:01 PM Hello,

thx for this great simple script ! CreateVmBackups.ps1
this is really what I want.
Is it possible to give me the way to STOP VMs instead of saving state during the export. ?

regards,
Jim on 11/24/2009 7:02 PM Hi Tore,
I'm using your script and its working great. I have noticed an issue with some of the vm's not being moved from the temp directory after the export.

Do you know what might cause this?

Also, is there any significant reason why they are exported to a temp directory first and then moved?
Kurt on 12/10/2009 1:52 PM Hi Tore
Thank's for the script.
soma on 1/21/2010 6:41 AM Hi all,
       How to write a script to apply a snapshot to revert virtual machine. I can able to create a snapshot but can not apply the snapshot. Please help me.

Thanks,
George.
caesar on 1/26/2010 10:37 AM I can run the script in the diagbox of powershell, but I can't running it by
double-click, & also I can't running it by setting the taskschdule...pls help me. thx!
Robert Vabo on 2/3/2010 8:51 AM Thanks Tore for the script. After several disk crashes I found out that maybee backup is a good thing, and it is!
I modified the script a bit so that it sends mail to me after each backup and it is working fine. I just made a scheduled task to run the script with bypassing the ExecutionPolicy:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -File D:\BackupVM.ps1
Script:
##
##  Create a backup of all the vm's
##

$dest = "f:\"
$VM_Service = get-wmiobject -namespace root\virtualization Msvm_VirtualSystemManagementService
$ListofVMs = get-wmiobject -namespace root\virtualization Msvm_ComputerSystem -filter  "ElementName <> Name"  
$email = "name@mailserver.com"
$emailFrom = "BackupCheck@mailserver.com"
$smtpServer = "mailserver"
$computerName = get-content env:computername

foreach ($VM in [array] $ListOfVMs)
{
  $VMReturnState = $VM.EnabledState
  $VMName = $VM.ElementName

  if (($VM.EnabledState -eq 2) -or ($VM.EnabledState -eq 32768) -or ($VM.EnabledState -eq 32770))
  {
    $VM.RequestStateChange(32769)
    echo "Saving the state of $VMName"
  }

  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]:Laughingelete("$dest\TmpDir\$VMName", $True)
  }

  echo "Exporting the VM"
  $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 remove the old files.

  if ([IO.Directory]::Exists("$dest\$VMName"))
  {
    [IO.Directory]::Move("$dest\$VMName", "$dest\$VMName-OldTmpDir")
    [IO.Directory]::Move("$dest\TmpDir\$VMName", "$dest\$VMName")
    [IO.Directory]:Laughingelete("$dest\$VMName-OldTmpDir", $True)
  }
  else
  {
    [IO.Directory]::Move("$dest\TmpDir\$VMName", "$dest\$VMName")
  }
  
  echo "Done with $VMName"
  $smtp = new-object Net.Mail.SmtpClient($smtpServer)
  $smtp.Send($emailFrom, $email, "Backup of VM on $computerName", "Finished backing up virtual machine: $VMName")

  $VM.RequestStateChange($VMReturnState)
}
Matthew on 2/21/2010 12:56 PM I am new to Powershell

How to you run this script manually?
registry clenaer reviews on 2/21/2010 2:39 PM It's a great tool. Thanks for making my life much easier!
Arnaud on 2/22/2010 2:49 PM Hello Tore!
Your script is awesome!
But I would like to schedule the script in order to execute it few times per week.
This is to have few versions of the VM's.
How can I modify your script to add the date of the backup day on the directory name?

Thank you a lot!
Arnaud on 2/23/2010 8:09 AM I found the way to create a directory with the date of the day.
Here is the modified script :

##
##  Create a backup of all the vm's
##

$dest = "D:\Backup Hyper-V -"
$VM_Service = get-wmiobject -namespace root\virtualization Msvm_VirtualSystemManagementService
$ListofVMs = get-wmiobject -namespace root\virtualization Msvm_ComputerSystem -filter  "ElementName <> Name"
$date = get-date -uformat "%d-%m-%Y"
$ddest = $dest += $date += "-"  

foreach ($VM in [array] $ListOfVMs)
{
  $VMReturnState = $VM.EnabledState
  $VMName = $VM.ElementName

  if (($VM.EnabledState -eq 2) -or ($VM.EnabledState -eq 32768) -or ($VM.EnabledState -eq 32770))
  {
    $VM.RequestStateChange(32769)
    echo "Saving the state of $VMName"
  }

  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("$ddest\TmpDir\$VMName"))
  {
    [IO.Directory]:Laughingelete("$ddest\TmpDir\$VMName", $True)
  }

  echo "Exporting the VM"
  $status = $VM_Service.ExportVirtualSystem($VM.__PATH, $True, "$ddest\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 remove the old files.

  if ([IO.Directory]::Exists("$ddest\$VMName"))
  {
    [IO.Directory]::Move("$ddest\$VMName", "$ddest\$VMName-OldTmpDir")
    [IO.Directory]::Move("$ddest\TmpDir\$VMName", "$ddest\$VMName")
    [IO.Directory]:Laughingelete("$ddest\$VMName-OldTmpDir", $True)
  }
  else
  {
    [IO.Directory]::Move("$ddest\TmpDir\$VMName", "$ddest\$VMName")
  }
  
  echo "Done with $VMName"
  $VM.RequestStateChange($VMReturnState)
}
Arnaud on 2/23/2010 10:34 AM Other problem,
I want to backup only 2 VMs (we've got 5 VMs) and I don't know how can I do.
I've see above that someone have post a modification but it doesn't work by me because all VM's name contain computer's name.

Thank you a lot!
Arnaud on 2/23/2010 11:16 AM Ok, I think I have found the solution at line 7 :
$ListofVMs = get-wmiobject -namespace root\virtualization Msvm_ComputerSystem -filter "ElementName = 'Exact name of your VM' OR ElementName = 'Exact name of your VM'"
ice dams on 2/25/2010 9:06 AM hi thanks for posting this script.

Add comment




biuquote
Loading