PowerCLI: A Simple VM Backup Script

Just lately I've been doing a lot of work in a Lab environment. Some of the work I'm doing is quite important to me so I decided to workout a way I can backup my VM's onto a backup device. To my surprise this was pretty simple to do.

Here is how I decided the script should function:

  1. Send myself an email telling me that the backup process has started
  2. Import the name of the VM to be backed up and the destination datastore from a CSV file
  3. Create a Snapshot of the VM I want to backup
  4. Create a Clone'd VM from the Snapshot
    • Place the backup onto my Backup Datastore
    • Name the backup <master VM name>-<date stamp>
    • Thin provision the backup
  5. Remove the Snapshot from the master VM
  6. Send myself an email telling me that the backup process has completed

So that's what I wanted to do, this is what I ended up with.

Backup Script

# Import Backup CSV
$backupinfo =  Import-Csv C:\scripts\mybackups.csv

#Set Date format for clone names
$date = Get-Date -Format "yyyyMMdd"

#Set Date format for emails
$time = (Get-Date -f "HH:MM")

#Connect to vCenter
Connect-VIServer "<vc server>"

foreach ($customer in $backupinfo)
{
	$vm = Get-VM $customer.MasterVM
	
	#Send Start Email
	C:\scripts\backupstartedemail.ps1
	
	# Create new snapshot for clone
	$cloneSnap = $vm | New-Snapshot -Name "Clone Snapshot"
	
	# Get managed object view
	$vmView = $vm | Get-View
	
	# Get folder managed object reference
	$cloneFolder = $vmView.parent
	
	# Build clone specification
	$cloneSpec = new-object Vmware.Vim.VirtualMachineCloneSpec
	$cloneSpec.Snapshot = $vmView.Snapshot.CurrentSnapshot

	# Make linked disk specification
	$cloneSpec.Location = new-object Vmware.Vim.VirtualMachineRelocateSpec
	$cloneSpec.Location.Datastore = (Get-Datastore -Name $customer.BackupDS | Get-View).MoRef
	$cloneSpec.Location.Transform =  [Vmware.Vim.VirtualMachineRelocateTransformation]::sparse

	$cloneName = "$vm-$date"

	# Create clone
	$vmView.CloneVM( $cloneFolder, $cloneName, $cloneSpec )

	# Write newly created VM to stdout as confirmation
	Get-VM $cloneName

	# Remove Snapshot created for clone
	Get-Snapshot -VM (Get-VM -Name $customer.MasterVM) -Name $cloneSnap | Remove-Snapshot -confirm:$False
	
	#Send Complete Email
	C:\scripts\backupcompletedemail.ps1
}
#Disconnect from vCentre
Disconnect-VIServer -Confirm:$false

Send Started Email Script

#Set Date format for emails
$timestart = (Get-Date -f "HH:MM")

$emailFrom = "<senders address>"
$emailTo = "<recipients address>"
$subject = "[$vm - Backup Started]"
$body = "Backup Details
-------------
VM Name:",$vm,"
Clone Name:","$vm-$date","
Target Datastore:", $customer.BackupDS,"
Time Started:", $timestart

$smtpServer = "<smtp server>"
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($emailFrom,$emailTo,$subject,$body)

Send Completed Email Script

#Set Date format for emails
$timecomplete = (Get-Date -f "HH:MM")

$emailFrom = "<senders address>"
$emailTo = "<recipients address>"
$subject = "[$vm - Backup Complete]"
$body = "Backup Details
-------------
VM Name:",$vm,"
Clone Name:","$vm-$date","
Target Datastore:", $customer.BackupDS,"
Time Started:", $timestart,"
Time Completed:", $timecomplete
$smtpServer = "<smtp server>"
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($emailFrom,$emailTo,$subject,$body)

Content of CSV

The content of the csv file is very simple. This is what mine looks like:

MasterVM,BackupDS
VM1,BackupDataStore
VM2,BackupDataStore

Summary

So as you can see, it's simple, but very effective. Let me know your thoughts/Idea's on this could be improved.

Sources:

Simon Long

View Comments

  • I am trying this out and get the error below. Any ideas what I may be doing wrong?

    Exception calling "CloneVM" with "3" argument(s): "A specified parameter was no
    t correct.
    host"
    At C:scriptsbackup.ps1:41 char:17
    + $vmView.CloneVM <<<< ( $cloneFolder, $cloneName, $cloneSpec )
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    • Hi Mark, contact me via the contact page and I'll help you through it. It'll save clogging this thread up.

      Simon

      • I am a novice in scripting , Please help me .

        1- Could you please what is the csv file format ?
        2-How can I execute this script ?

        Many thanks

      • Hi Simon,

        first.thanks for the great idea behind your script.
        I get the error mentioned by MarkZi in one of the environments I try to use the script.
        In this case I want to clone the machine from local datastore to the local datastore on another server. For that I added
        "$CloneSpec.Location.Host = ( get-vmhost -Name $Machine.BackupHost | get-view).MoRef" to the script.

        The datastore exists and the value of $CloneSpec.Location.Datastore looks fine.
        Can I find out where the error is more exactly?

      • Hello Simon, 

        Do you have other ideas or a workaround? I even tried setting $cloneSpec.Location.Datastore with MoRef of the datastore I gathered manually and still getting the above error. 

        Thank you, 

        Alex

        • Morning Alex, can you email me via the contact me page. Paste the error you are getting.

          Cheers

  • Did not expect it! (Abercrombie & Fitch) so kind of young, dynamic, innovative, works ahead of its time, much as a hundred years of history, not only that, the past few years, more is high style, high quality and super clean style, all over the United States and Canada, continue to spread open to, create a new index the new global fashion.

  • Hi Simon,

    Thanks a lot for this script, it is working like a charm. However, it seems to clone the backup-vm in thin format, Now I can surely see the value in that, but I would like to make my back-up thick. Can you tell me how I can manage that?

    regards,

    Ronald

    • Ronald, my apologies for the delay in getting back to you.

      Change the word "sparse" to "flat" on line 36. That should make them Thick format.

      Regards

      Simon

  • If you can help me understand this, I'd be grateful.

    1. Why snapshot and then clone? Doesn't cloning involve a snapshot anyways?

    2. Why the ".MasterVM" in your statement in line 15? (Get-VM $customer.MasterVM)

    I presume that this is related to the format of the input CSV, that the first line of the CSV labels the type in each successive line. It's just not obvious to me what mechanism is that accomodates this.

    Thanks,

    Marge

    • Hi Marge, thanks for the questions.

      1. Good question. Cloning does indeed take a snapshot. Maybe it's worth trying to see if it works.

      2. .MasterVM refers to the column in the CSV. $customer is a line of that CSV

  • Why this error?
    Method invocation failed because [System.Object[]] doesn't contain a method named 'CloneVM'.
    At C:Scriptsbackup.ps1:41 char:17
    + $vmView.CloneVM <<<< ( $cloneFolder, $cloneName, $cloneSpec )
    + CategoryInfo : InvalidOperation: (CloneVM:String) [], RuntimeException
    + FullyQualifiedErrorId : MethodNotFound

    Using PowerCLI 4.1 build 264274
    Host are ESX 4.0.0, 294855

  • I am trying to test out this script. I have a host that I set up with similar info that you have in your test. I keep getting errors. It does seem to create the snapshot but everything else seems to fail. The mail feature also works. Here are my errors:

    Get-VM : Cannot validate argument on parameter 'Name'. The argument is null or empty. Supply an argument that is not nu
    ll or empty and then try the command again.
    At C:scriptsorig_backupScript.ps1:15 char:17
    + $vm = Get-VM <<<< $customer.MasterVM
    + CategoryInfo : InvalidData: (:) [Get-VM], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,VMware.VimAutomation.ViCore.Cmdlets.Commands.GetVM

    Get-Datastore : Cannot validate argument on parameter 'Name'. The argument is null or empty. Supply an argument that is
    not null or empty and then try the command again.
    At C:scriptsorig_backupScript.ps1:35 char:57
    + $cloneSpec.Location.Datastore = (Get-Datastore -Name <<<< $BackupDS | Get-View).MoRef
    + CategoryInfo : InvalidData: (:) [Get-Datastore], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,VMware.VimAutomation.ViCore.Cmdlets.Commands.GetDatasto
    re

    Exception calling "CloneVM" with "3" argument(s): "The operation is not supported on the object."
    At C:scriptsorig_backupScript.ps1:41 char:20
    + $vmView.CloneVM <<<< ( $cloneFolder, $cloneName, $cloneSpec )
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    Get-VM : 4/28/2011 8:09:19 AM Get-VM VM with name 'MasterVM-20110428' was not found, using the specified filt
    er(s).
    At C:scriptsorig_backupScript.ps1:44 char:11
    + Get-VM <<<< $cloneName
    + CategoryInfo : ObjectNotFound: (:) [Get-VM], VimException
    + FullyQualifiedErrorId : Core_OutputHelper_WriteNotFoundError,VMware.VimAutomation.ViCore.Cmdlets.Commands.GetVM

    Get-VM : Cannot validate argument on parameter 'Name'. The argument is null or empty. Supply an argument that is not nu
    ll or empty and then try the command again.
    At C:scriptsorig_backupScript.ps1:47 char:35
    + Get-Snapshot -VM (Get-VM -Name <<<< $customerMasterVM) -Name $cloneSnap | Remove-Snapshot -confirm:$False
    + CategoryInfo : InvalidData: (:) [Get-VM], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,VMware.VimAutomation.ViCore.Cmdlets.Commands.GetVM

  • I have been using a copy of this script for over a year and it has been working great as a poor man's backup solution.  I backup over 27TB of data in less than 24 hours!

    I was going to write a cleanup script to prune some of my older backups, but I was wondering if I could use something like the -force option when invoking clonevm so that the older files are overwritten in stead of appending with an underscore and a number.

    Any ideas on how to overwirte old files using this process?

    -Mark 

  • That is excellent, thanks Simon! I can confirm that it works fine leaving the snapshot to be taken by the clone operation itself, as suggested in your earlier reply to Marge.

Share
Published by
Simon Long

Recent Posts

Google Cloud VMware Engine @ VMworld 2021

Another VMworld is upon us!!! Sadly, it's only virtual again this year. However, that does…

3 years ago

Google Cloud VMware Engine – Learning Resources

As part of my recent move to Google, I'm working on quickly getting up to…

3 years ago

Hey Google!

I am delighted to announce the next chapter in my career. Today is my first…

3 years ago

EP13 – Defending Remotely

In episode thirteen of The VCDX Podcast, I am joined by two special guests who…

3 years ago

Getting Started With Oracle Cloud VMware Solution (OCVS) – Migrating Workloads Using VMware HCX

In my recent ‘Getting started with Oracle Cloud VMware Solution (OVCS)’ post; Getting Started With…

3 years ago

Getting Started With Oracle Cloud VMware Solution (OCVS) – Connecting To An On-Premises Environment

In my recent ‘Getting started with Oracle Cloud VMware Solution (OVCS)’ post; Getting Started With…

3 years ago