Veeam Backup & Replication Free Edition Experience and Sample

This post is a bit outside of what I normally write about, but I had some trouble getting it working and all of the web searching didn’t really help.  So just in case someone else is having the same problems I was having, Google should provide this for you!

First a quick introduction.  I have a server in my house (I’d like to thank my wife for allowing this) and it runs VMWare ESXi 5.5.  I have a couple of dozen VM’s that I run 24/7 and I’ve given access to a variety of these VM’s to about a dozen colleagues and clients.  My problem has always been backing up the VM’s with this many people in the system.  There is no production data on this system, but I’d prefer that we have a good backup of everyone’s work in case a RAID array in the system fails or in case someone breaks something on accident.

My current environment consists of a single server with a variety of RAID arrays.  All of the VM’s live on SSD storage, most of which is in RAID 0.  So given that I’m running RAID 0, I have even more reason to make sure I have up to date backups.  In addition to the SSD’s, I also have a traditional platter-based RAID 6 array.  This is where my backups will be stored.  Up until now, I have done occasional manual backups using the Veeam Backup & Replication product.  They have a free version that has worked well for one-off backups.  But, until now, it did not support a way of doing automated backups.

That all changed recently with the release of Update 2 to the Version 8 product.  Once I heard this, I of course had to get it running.  So I went to Veeam’s very own blog post on the topic:

Veeam’s Blog Post

So what trouble did I run into?  First, with one VM getting it all setup went perfectly.  It worked just fine on the first attempt and I was pretty excited.  But when I decided to add VM’s to the list, I hit a snag.  On the blog post, it clearly says to separate the list of VM’s with commas, but the sample file they have available for download says to use semi-colons.  So I finally figured that problem out and I was off and running.

The other cool thing that the backup script supports is an e-mail notification at the end.  So not only do I get free backups on an automated schedule, but I don’t have to check in to verify that they ran.  But this is where I hit my second snag.  The provided samples did not take into account that many SMTP servers require authentication to actually send mail.  So, I finally figured out how to get it working with authentication and while I was at it, I went ahead and added a logging section.  Now Veeam has a log of all of the backups, so this may be extraneous, but I like having it.  Too many MaxL scripts over the years…

So here’s my code.  I hope it helps!

# Orignal Author: Vladimir Eremin
# Created Date: 3/24/2015
# http://forums.veeam.com/member31097.html
# Modified By: Brian Marshall
# Modified Date: 7/26/2015

##################################################################
#                   User Defined Variables
##################################################################

# Names of VMs to backup separated by commas (Mandatory)
$VMNames = "Hyperion SS","Hyperion ES","Hyperion PL","Hyperion FM","Hyperion SQL","Hyperion BI","Hyperion DR","Hyperion ET"

# Name of vCenter or standalone host VMs to backup reside on (Mandatory)
$HostName = "HyperionESXI"

# Directory that VM backups should go to (Mandatory; for instance, C:\Backup)
$Directory = "G:\Backup\ESXi"

# Directory that log files should go to
$LogDirectory = "G:\Backup\ESXi\Logs"

# Description of what is being backed up
$BackupDescription = "Hyperion11123500"

# Desired compression level (Optional; Possible values: 0 - None, 4 - Dedupe-friendly, 5 - Optimal, 6 - High, 9 - Extreme) 
$CompressionLevel = "0"

# Quiesce VM when taking snapshot (Optional; VMware Tools are required; Possible values: $True/$False)
$EnableQuiescence = $True

# Protect resulting backup with encryption key (Optional; $True/$False)
$EnableEncryption = $False

# Encryption Key (Optional; path to a secure string)
$EncryptionKey = ""

# Retention settings (Optional; By default, VeeamZIP files are not removed and kept in the specified location for an indefinite period of time. 
# Possible values: Never , Tonight, TomorrowNight, In3days, In1Week, In2Weeks, In1Month)
$Retention = "In2Weeks"

##################################################################
#                   Notification Settings
##################################################################

# Enable notification (Optional)
$EnableNotification = $True

# Email SMTP server
$SMTPServer = "smtp.gmail.com"

# Email SMTP server port
$SMTPPort = "587"

# Email SMTP username
$SMTPUser="yourgmailaccount@gmail.com"

# Email SMTP password
$SMTPPass="youcanthavemypassword"

# Email FROM
$EmailFrom = "Brian Marshall " 

# Email TO
$EmailTo = "yourgmailaccount@gmail.com"

# Email subject
$EmailSubject = "ESXi 11.1.2.3.500 Backup Complete"

##################################################################
#                   Email formatting 
##################################################################

$style = ""

##################################################################
#                   End User Defined Variables
##################################################################

#################### DO NOT MODIFY PAST THIS LINE ################
Asnp VeeamPSSnapin

$Server = Get-VBRServer -name $HostName
$MesssagyBody = @()

foreach ($VMName in $VMNames)
{
  $VM = Find-VBRViEntity -Name $VMName -Server $Server
  
  If ($EnableEncryption)
  {
    $EncryptionKey = Add-VBREncryptionKey -Password (cat $EncryptionKey | ConvertTo-SecureString)
    $ZIPSession = Start-VBRZip -Entity $VM -Folder $Directory -Compression $CompressionLevel -DisableQuiesce:(!$EnableQuiescence) -AutoDelete $Retention -EncryptionKey $EncryptionKey
  }
  
  Else 
  {
    $ZIPSession = Start-VBRZip -Entity $VM -Folder $Directory -Compression $CompressionLevel -DisableQuiesce:(!$EnableQuiescence) -AutoDelete $Retention
  }
  
  If ($EnableNotification) 
  {
    $TaskSessions = $ZIPSession.GetTaskSessions().logger.getlog().updatedrecords
    $FailedSessions =  $TaskSessions | where {$_.status -eq "EWarning" -or $_.Status -eq "EFailed"}
  
  if ($FailedSessions -ne $Null)
  {
    $MesssagyBody = $MesssagyBody + ($ZIPSession | Select-Object @{n="Name";e={($_.name).Substring(0, $_.name.LastIndexOf("("))}} ,@{n="Start Time";e={$_.CreationTime}},@{n="End Time";e={$_.EndTime}},Result,@{n="Details";e={$FailedSessions.Title}})
  }
   
  Else
  {
    $MesssagyBody = $MesssagyBody + ($ZIPSession | Select-Object @{n="Name";e={($_.name).Substring(0, $_.name.LastIndexOf("("))}} ,@{n="Start Time";e={$_.CreationTime}},@{n="End Time";e={$_.EndTime}},Result,@{n="Details";e={($TaskSessions | sort creationtime -Descending | select -first 1).Title}})
  }
  
  }   
}

If ($EnableNotification)
{
$Message = New-Object System.Net.Mail.MailMessage $EmailFrom, $EmailTo
$Message.Subject = $EmailSubject
$Message.IsBodyHTML = $True
$message.Body = $MesssagyBody | ConvertTo-Html -head $style | Out-String
$SMTP = New-Object Net.Mail.SmtpClient($SMTPServer, $SMTPPort)
$SMTP.EnableSsl = $true 
$SMTP.Credentials = New-Object System.Net.NetworkCredential($SMTPUser, $SMTPPass); 
$SMTP.Send($Message)
}

$LogMessage = $MesssagyBody | ConvertTo-Html -head $style | Out-String
$LogMessage | Out-File "$LogDirectory\$BackupDescription-$(get-date -f yyyy-MM-dd-hh-mm).html"

You can find the rest of the setup information at the official blog post by Veeam, so I won’t try to re-invent the wheel here.  I’m no PowerShell expert, but the modifications served my purpose!

The Planning Repository: HSP_MEMBER (Part 2 – HSP_ALIAS)
The Planning Repository: HSP_MEMBER (Part 3 – UDA’s)

Comments

  1. Great Vladimir, It is exactly what I was looking for!

    just one question: what version of vmware are you using? the paid version, right?

    • Brian Marshall
      June 29, 2016 - 12:51 pm

      Veeam does require the paid version of ESXi. The free version does not give you access at the right level to perform a backup of this nature. I use the VMUG Advantage for my licensing. You pay it once a year and get pretty much everything for a three-server cluster.

  2. Find-VBRViEntity : Object reference not set to an instance of an object.
    At C:\veeambackup.ps1:175 char:9
    + $VM = Find-VBRViEntity -Name $VMName -Server $Server
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Find-VBRViEntity], NullReferenceException
    + FullyQualifiedErrorId : System.NullReferenceException,Veeam.Backup.PowerShell.Cmdlets.FindVBRViEntity

    Start-VBRZip : Cannot validate argument on parameter ‘Entity’. The argument is null. Provide a valid value for the argument, and then try running the
    command again.
    At C:\veeambackup.ps1:195 char:42
    + $ZIPSession = Start-VBRZip -Entity $VM -Folder $Directory -Compression $Co …
    + ~~~
    + CategoryInfo : InvalidData: (:) [Start-VBRZip], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Veeam.Backup.PowerShell.Cmdlets.StartVBRZip

    You cannot call a method on a null-valued expression.
    At C:\veeambackup.ps1:204 char:7
    + $TaskSessions = $ZIPSession.GetTaskSessions().logger.getlog().updatedrecor …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    Find-VBRViEntity : Object reference not set to an instance of an object.
    At C:\veeambackup.ps1:175 char:9
    + $VM = Find-VBRViEntity -Name $VMName -Server $Server
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Find-VBRViEntity], NullReferenceException
    + FullyQualifiedErrorId : System.NullReferenceException,Veeam.Backup.PowerShell.Cmdlets.FindVBRViEntity

    Start-VBRZip : Cannot validate argument on parameter ‘Entity’. The argument is null. Provide a valid value for the argument, and then try running the
    command again.
    At C:\veeambackup.ps1:195 char:42
    + $ZIPSession = Start-VBRZip -Entity $VM -Folder $Directory -Compression $Co …
    + ~~~
    + CategoryInfo : InvalidData: (:) [Start-VBRZip], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Veeam.Backup.PowerShell.Cmdlets.StartVBRZip

    You cannot call a method on a null-valued expression.
    At C:\veeambackup.ps1:204 char:7
    + $TaskSessions = $ZIPSession.GetTaskSessions().logger.getlog().updatedrecor …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    New-Object : Exception calling “.ctor” with “2” argument(s): “The specified string is not in the form required for an e-mail address.”
    At C:\veeambackup.ps1:238 char:12
    + $Message = New-Object System.Net.Mail.MailMessage $EmailFrom, $EmailTo
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

    The property ‘Subject’ cannot be found on this object. Verify that the property exists and can be set.
    At C:\veeambackup.ps1:240 char:1
    + $Message.Subject = $EmailSubject
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

    The property ‘IsBodyHTML’ cannot be found on this object. Verify that the property exists and can be set.
    At C:\veeambackup.ps1:242 char:1
    + $Message.IsBodyHTML = $True
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

    The property ‘Body’ cannot be found on this object. Verify that the property exists and can be set.
    At C:\veeambackup.ps1:244 char:1
    + $message.Body = $MesssagyBody | ConvertTo-Html -head $style | Out-String
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

    Exception calling “Send” with “1” argument(s): “Value cannot be null.
    Parameter name: message”
    At C:\veeambackup.ps1:252 char:1
    + $SMTP.Send($Message)
    + ~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ArgumentNullException

    Out-File : Could not find a part of the path ‘F:\Veeambackup\Logs\DewanHost2-ADC-Fileserver-2017-03-27-03-51.html’.
    At C:\veeambackup.ps1:260 char:15
    + $LogMessage | Out-File “$LogDirectory\$BackupDescription-$(get-date -f yyyy-MM-d …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : OpenError: (:) [Out-File], DirectoryNotFoundException
    + FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand

  3. Got the issue.
    We have to update the command to Find-VBRHvEntity for Microsoft HyperV host.
    The commands are Virtualisation platform specific.

  4. PS C:\> C:\veeambackup.ps1
    New-Object : Exception calling “.ctor” with “2” argument(s): “The specified string is not in the form required for an e-mail address.”
    At C:\veeambackup.ps1:235 char:12
    + $Message = New-Object System.Net.Mail.MailMessage $EmailFrom, $EmailTo
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

    The property ‘Subject’ cannot be found on this object. Verify that the property exists and can be set.
    At C:\veeambackup.ps1:237 char:1
    + $Message.Subject = $EmailSubject
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

    The property ‘IsBodyHTML’ cannot be found on this object. Verify that the property exists and can be set.
    At C:\veeambackup.ps1:239 char:1
    + $Message.IsBodyHTML = $True
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

    The property ‘Body’ cannot be found on this object. Verify that the property exists and can be set.
    At C:\veeambackup.ps1:241 char:1
    + $message.Body = $MesssagyBody | ConvertTo-Html -head $style | Out-String
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

    Exception calling “Send” with “1” argument(s): “Value cannot be null.
    Parameter name: message”
    At C:\veeambackup.ps1:249 char:1
    + $SMTP.Send($Message)
    + ~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ArgumentNullException

    Out-File : Could not find a part of the path ‘F:\Veeambackup\Logs\DewanHost2-ADC-Fileserver-2017-03-27-06-42.html’.
    At C:\veeambackup.ps1:257 char:15
    + $LogMessage | Out-File “$LogDirectory\$BackupDescription-$(get-date -f yyyy-MM-d …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : OpenError: (:) [Out-File], DirectoryNotFoundException
    + FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand

  5. I guess one issue I found that:
    the correct format is:

    $emailMessage.From = “John Smith “

  6. The issue left:

    I am getting the following text in th email body:

    BODY{font-family: Arial; font-size: 10pt;}TABLE{border: 1px solid black; border-collapse: collapse;}TH{border: 1px solid black; background: #dddddd; padd
    ing: 5px; }TD{border: 1px solid black; padding: 5px; }

  7. I guess we were missing &

    ##################################################################
    # Email formatting
    ##################################################################

    $style = “BODY{font-family: Arial; font-size: 10pt;}”
    $style = $style + “TABLE{border: 1px solid black; border-collapse: collapse;}”
    $style = $style + “TH{border: 1px solid black; background: #dddddd; padding: 5px; }”
    $style = $style + “TD{border: 1px solid black; padding: 5px; }”
    $style = $style + “”

  8. oh got it … I typed & but it disappeared in the page (style without blanks)

  9. we need to add style tags …. style tage is not visible after posting

  10. Hello, thanks for excellent skript. I have just one question. How to set more emails, where to send a report? I tried to insert to $EmailTo = “email1@email.com” ; “email2@email.com” .. or $EmailTo = “email1@email.com ; email2@email.com” but 1st solution work only for 1 one email and that second dont work at all? Can you give me a advice?

    Thanks for respond 🙂

  11. Try:
    $EmailTo = “email1@email.com”, “email2@email.com”
    Basically this should make your $EmailTo a string array. If that doesn’t work we may have to set up a for each.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.