Backup VMware with Veeam Free PowerShell and FreeNAS

I use FreeNAS to store the virtual machines for my lab. I had previously used NFS as it allowed for really simple backups of VMs – take a snapshot, do a copy from the snapshot directory to a different dataset, remove the snapshot, done. NFS is nice because it allows you to view the filesystem directly from the storage server…NFS on FreeNAS\ZFS is not nice because it essentially requires flash for the intent log due to the way VMware writes to NFS using synchronous writes – it also essentially requires tons of RAM – 96GB is a good starting point. But I had some flash for the ZIL and performance was pretty good – plus I could see the file system and take snapshots whenever I wanted and backups were easy.

Then 9.3 came out and iSCSI moved into CTL and supports a good chunk of VAAI primitives. And I tried using the new iSCSI target and found performance to be considerably better…so needless to say I converted my VMware hosts storage to iSCSI. The next question was how to backup the VMs – using Veeam free was my preferred choice as it was simple and worked quite well – only problem was that it was a manual process to run the backups…until update 2 came out in May:

http://www.veeam.com/blog/veeam-backup-free-edition-now-with-powershell.html

As soon as you apply the update, the VeeamPSSnapin snapin becomes available. In the article noted above, Vladimir posts a sample script for VMware and HyperV which gives more than enough information about how to use the snapin – it uses a static array of VM names and runs through them creating VeeamZIP jobs. It works quite well, but as with everything PowerShell, it can do more. Read through Vladimir’s article about the original script(s) – most of that functionality is preserved in my script, but I’ve added a few features for the VMware script:

  • Use PowerCLI to gather a list of VMs based on tags (you should be using tags if you’re using 5.5 – the script can be modified to use the older annotations if needed)
  • Cleanup backup files older than X days (which the free version of VeeamZIP does not do)*
  • Cleanup backup files to only keep 1 backup per VM (this is the free version after all)

*Update 7/20/2015: Thanks to clarification from Vladimir, the free VeeamZIP will in fact clean up old backup files based on the options in the script – Never , Tonight, TomorrowNight, In3days, In1Week, In2Weeks, In1Month. 

So yes, you’ll need to install PowerCLI on your Veeam backup server. A few notes about issues:

The backup destination needs to have write rights for the computer account running Veeam – so VeeamServer$ for example. This is an issue with FreeNAS and CIFS\AD as I don’t know of a way to get computer accounts set in the permissions. You’ll see the following errors:

Error: Access is denied. Cannot create folder . Folder path: [\\server\share\directory]. –tr:FC: Failed to create directory. Directory path: [\\server\share\directory].

When running jobs in the console, this is not an issue as the job will use the credentials specified – this is only an issue when creating jobs from command line. The fix for this is to set the data mover service to run as an account that does have permissions to write to the target.

Services

If this is not done, you will see several failures in the job history:

failed

The next issue is with PowerCLI – I noted with version 6.0 of PowerCLI that the PSModulePath variable was only updated for the user that originally installed PowerCLI. So first, check to make sure that the account running the scheduled task is able to open PowerCLI and get connected etc. If you need to modify the path, use this cmdlet:

[code language=”powershell”]

$PowerCLIModulePath = "C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\Modules"
$env:PSModulePath += ";$PowerCLIModulePath"
[environment]::SetEnvironmentVariable(‘PSModulePath’,"$env:PSModulePath",’Machine’)

[/code]

And finally the script.

[code language=”powershell”]

# Veeam Free VMware backup script with PowerCLI
# Jacob Rutski
# [email protected]
# http://blogs.serioustek.net
# @JRutski on Twitter

# Author: Vladimir Eremin
# Created Date: 3/24/2015
# http://forums.veeam.com/member31097.html
#

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

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

# Name of virtual machine tag used to select VMs to backup – must be a unique tag; category does not matter
# Possible values: any string
$VMTag = ""

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

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

# 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 = "Never"

# Script cleanup – this script can cleanup old backup files older than $oldFileDays if $scriptCleanup is set to $true
# Possible values: $true to enable cleanup; $false to disable
$scriptCleanup = $false

# Possible values: any integer value for number of days
$oldFileDays = 0

# Alternatively, if space is low, use the replace option to delete backup files as soon as a new backup file is created
# Only works with single backup file per VM; replace will ONLY delete the oldest file that matches the VM name
# Possible values: $true to enable; $false to disable
$replace = $false

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

# Enable notification (Optional)
$EnableNotification = $False

# Email SMTP server
$SMTPServer = ""

# Email FROM
$EmailFrom = ""

# Email TO
$EmailTo = ""

# Email subject
$EmailSubject = ""

##################################################################
# Gather VM list dynamically
##################################################################

# Load PowerCLI – note this path may be different on a non-x64 platform
$PCLIPath = "C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\Scripts\Initialize-PowerCLIEnvironment.ps1"
Import-Module $PCLIPath

# Connect to vCenter – Note: Set-PowerCLIConfiguration requires admin if UAC is enabled
Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -DisplayDeprecationWarnings $False -Confirm:$False
Connect-VIServer $hostname

# Add Veeam Snap-in
asnp VeeamPSSnapin

# Get VMs marked to backup
$Script:VMList = (Get-VM -Tag $VMTag)

Disconnect-VIServer -confirm:$false

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

$style = "<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 + "</style>"

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

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

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

foreach ($VMName in $VMList.Name)
{
$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
}

$TaskSessions = $ZIPSession.GetTaskSessions().logger.getlog().updatedrecords
$FailedSessions = $TaskSessions | where {$_.status -eq "EWarning" -or $_.Status -eq "EFailed"}

# Remove old backup if $replace is enabled
If(($TaskSessions | where {$_.Status -eq "EFailed"}) -eq $Null -and $replace)
{
Remove-Item (Get-ChildItem $Directory -Filter "*$($VMName)*" | Sort CreationTime | Select -First 1).FullName -Confirm:$false
}

If ($EnableNotification)
{
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)
$SMTP.Send($Message)
}

##################################################################
# Sceduled Backup file cleanup
##################################################################

If($scriptCleanup)
{
foreach($backupFile in Get-ChildItem $Directory -Filter "*.vbk")
{
If($backupFile.LastWriteTime -lt (Get-Date).AddDays(-$oldFileDays))
{
Remove-Item $backupFile.FullName -Confirm:$false
}
}
}

[/code]

4 thoughts on “Backup VMware with Veeam Free PowerShell and FreeNAS”

  1. I fought with scripting a backup to a CIFS share for 6 hours or so today, and now I find your post telling how to fix it. Grrrr….

    Thanks!

    For anyone else running into it, the problem comes back looking like this:
    11/22/2015 3:24:08 PM :: Error: Access is denied.
    Cannot create folder. Folder path: [\\servername\sharename\backupfolder].
    –tr:FC: Failed to create directory. Directory path: [\\servername\sharename\backupfolder].
    –tr:Failed to call DoRpc. CmdName: [FcCreateDir].
    Access is denied.
    Cannot create folder. Folder path: [\\servername\sharename\backupfolder].

    Reply
    • Michael-

      Glad you were able to get it sorted. I’ve added the actual text from the error to the post, so that the indexers will pick it up. I’m still not sure how samba deals with computer accounts – I ran into something similar when labbing out some Hyper-V work – computer accounts needed to have permissions on the SMB share to use it with CSV….needless to say I never got that one working.

      Reply

Leave a Reply

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