Windows Backup Powershell Script

by Stan Czerno March 1, 2014 13:13 CST

So I was browsing for a good PowerShell script that uses the built-in Windows Server Backup features. Augagneur Alexandre's script http://gallery.technet.microsoft.com/scriptcenter/WSB-Backup-network-email-9793e315 was a good start, so I took it a little further.

Uses Windows Server Backup Features.

Creates a Bare Metal Recovery and Full VSS Backup.

Saves Backup to \\ServerName\ShareName\ComputerName\Current.

Allows for retaining older backups, you can set how many.

If the $MaxBackup variable is set to anything greater than 0 and the previous backup was successful, the script copies the current backup folder to \\ServerName\ShareName\ComputerName\Archive\<CurrentDate> before starting a new backup.

Sends an email with a summary and full log as an attachment.


http://www.czerno.com/default.asp?inc=/html/windows/WindowsBackup/WindowsBackupScript.asp


Here is a version that will work with Windows Server 2012:


http://www.czerno.com/default.asp?inc=/html/windows/WindowsBackup/2012/WindowsBackupScript2012.asp

Tags: ,

Catergories: Windows | Powershell | Windows Backup

Comments (12) -

Ken
November 12, 2014 22:58 CST

This script worked great for us with just a little tweaking to get it to not backup one of the locally attached drives and to use that drive as the backup target. Thanks for sharing this!

Martin Chayer
February 20, 2015 02:12 CST

Hi Ken,

I'm having a hard time to figure out how I can check if the destination USB disk is already in the device targets, if not send me a reminder email.
Any help on this with the modification you made ?

Regards

abdel
February 20, 2015 05:05 CST

Very usefull !! perfect !!

Mark
June 24, 2015 15:41 CST

Awesome work!  Any chance on getting an updated version for 2012 R2???  

Stan Czerno
June 25, 2015 02:26 CST

The script is basically the same except MS renamed the Backup Feature. If you had installed them manually the script would work. Here is an updated version for 2012 R2:

# Based on Augagneur Alexandre's script; gallery.technet.microsoft.com/.../WSB-Backup-network-email-9793e315
# as well as other parts and pieces from other blogs and forums
# A good artist creates, a great artist steals. Smile

# Uses Windows Server Backup Feature

# Creates a Bare Metal Restoreable and Full VSS Backup

# Saves Backup to \\ServerName\ShareName\ComputerName\Current

# Allows for retaining older backups, you can set how many

# If the $MaxBackup variable is set to anything greater than 0 and the previous backup was successful,
# the script copies the current backup folder to \\ServerName\ShareName\ComputerName\Archive\<CurrentDate>
# before starting a new backup
cls
$ComputerName = $env:computername

#------------------------------------------------------------------
# User Provided Variables
#------------------------------------------------------------------  

# Path to store Backup, no backslash at end.
# Example: "\\servername\ShareName\Folder"
# The Computer Name will be appended to the path
# If you used the example above, the script will create a folder like this: \\servername\ShareName\Folder\ComputerName
$BackupRootPath = "\\servername\ShareName\Folder"

# Number of backups to retain (value of "0" equals disable rotation, keep no backups)
$MaxBackup = 2

# From Email Address
$from = "$ComputerName@email.com"

# To Email Address
$to = "someone@email.com","someoneelse@email.com"

# SMTP Server
$smtpserver = "smtp.email.com"

# Send Email on Failure only? 1 = Send on Failure only, 0 = Send always
$SendEmail = 1

##### **********************************
##### Functions and Main Script Routine
##### **********************************

$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition

#------------------------------------------------------------------  
# Function to check if the Windows Backup Features are installed
# Installs if not and loadds the Snapin
#------------------------------------------------------------------  

function CheckForFeatures()
{
echo ""
echo "Checking to see if Windows Server Backup Features are installed"
echo ""
Import-Module ServerManager

$WSBBackupFeature = Get-WindowsFeature | Where-Object {$_.Name -eq "Windows-Server-Backup"}
$WSBBackupFeatureTools = Get-WindowsFeature | Where-Object {$_.Name -eq "Windows-Server-Backup"}

If ($WSBBackupFeature.Installed -and $WSBBackupFeatureTools.Installed -eq "True")

    {
       echo ""
       echo "Loading Windows Server Backup"
       echo ""
       if ( (Get-PSSnapin -Name Windows.ServerBackup -ErrorAction SilentlyContinue) -eq $null ) { }
    }

else

    {
    echo ""
    echo "Installing Windows Server Backup Features"
    echo ""
    $InstallWSB = Add-WindowsFeature -Name Windows-Server-Backup -IncludeAllSubFeature
    If ($InstallWSB.Success)
      {
      echo ""
      echo "Windows Backup Features installed successfully"
      echo ""
      echo ""
       }
      
    else {
            echo ""
            echo "Windows Backup Features not installed successfully"
            echo ""
            exit
         }
    }
}
  
#------------------------------------------------------------------  
# Function to compare the number of folders to retain with
# $MaxBackup (Not called if $MaxBackup equals 0)
#------------------------------------------------------------------  

function Rotation()
  {
  echo ""
  echo "Starting Backup Rotation and Cleanup"
  echo ""
  $BackupFolderNameArchiveName = Get-Date -Format yyyy-MM-dd_hhmmss;
  $BackupFolderNameArchive = ($BackupRootPath+"\$ComputerName\Archive")
  # Backup Archive folder creation
  if(!(Test-Path -Path $BackupFolderNameArchive )){
    New-Item $BackupFolderNameArchive -Type Directory | Out-Null
    }
  
  if ((Test-Path -path $FullBackupPath) -eq $True){
    Move-Item -Path $FullBackupPath $BackupFolderNameArchive | Out-Null
    Rename-Item $BackupFolderNameArchive"\Current" $BackupFolderNameArchiveName
    }        

  # List all backup folders
  $Backups = @(Get-ChildItem -Path $BackupFolderNameArchive\*)

  #Number of backups folders
  $NbrBackups = $Backups.count
  echo ""
  echo "Number of existing backups at $BackupFolderNameArchive is $NbrBackups"
  echo ""
    
  $i = 0
    
  #Delete oldest backup folders
  while ($NbrBackups -gt $MaxBackup)
    {
    $Backups[$i] | Remove-Item -Force -Recurse -ConfirmEmbarassedfalse
    $NbrBackups -= 1
    $i++
    }
  }
  
#------------------------------------------------------------------
# Function to send email notification
#------------------------------------------------------------------  

function EmailNotification()
    {
    echo ""
    echo "Starting Email Function"
    echo ""
    $BackupResult = (Get-WBSummary).LastBackupResultHR
    $CurrentVersion = (Get-WBJob -Previous 1)
    $head = "<style> body { background-color:white; font-family:Tahoma; font-size:12pt; } td, th { border:1px solid black; border-collapse:collapse; } th { color:black; background-color:white; } table, tr, td, th { padding: 2px; margin: 0px } table { margin-left:50px; width:90%} </style>"
    $b =  Get-WBSummary | Select-Object LastBackupTime,LastBackupResultHR,LastSuccessfulBackupTime | ConvertTo-Html -Fragment -PreContent "<h2>Backup Summary:</h2>" |  Out-String
    $b += Get-WBBackupSet | Select-Object VersionID,BackupTarget,RecoverableItems,@{Name="Volume";Expression={$_.Volume}},VssBackupOption | where {$_.VersionId -eq $CurrentVersion.VersionId} | ConvertTo-Html -as list -Fragment -PreContent "<h2>Backup Set info:</h2>" |  Out-String
    $b += Get-WBJob -Previous 1 | Select-Object StartTime,EndTime,JobState,HResult,DetailedHResult,ErrorDescription,@{Name="JobItems";Expression={$_.JobItems}} | ConvertTo-Html -as list -Fragment -PreContent "<h2>Job Properties:</h2>" |  Out-String

    if ($BackupResult -eq 0) {
      $message = "<h2>Backup Succesful for $ComputerName</h2>"
        $body = $head,$message,$b  | Out-String
        $Subject = "Backup Report for "+$env:computername
        $attachments = $LogFile
        }
    else
        {
        $errordesc = (Get-WBJob -Previous 1).ErrorDescription
        $message = "<h2>There was a Backup error on $ComputerName</h2> Error Description: $errordesc"
        $body = $head,$message,$b  | Out-String
        $Subject = "Backup Failed on "+$env:computername
        $attachments = $LogFile
        }
  
    # Send the email
      
    if ($SendEmail -eq 0) {
      echo ""
      echo "Sending email"
      echo ""
      Send-MailMessage -to $to -from $from  -subject $Subject -body $Body -smtpserver $smtpserver -BodyAsHtml -attachments $attachments
      }
    if ($SendEmail -eq 1 -and $BackupResult -ne 0) {
      echo ""
      echo "Backup Failed, sending email"
      echo ""
      Send-MailMessage -to $to -from $from  -subject $Subject -body $Body -smtpserver $smtpserver -BodyAsHtml -attachments $attachments
      }
    if ($SendEmail -eq 1 -and $BackupResult -eq 0) {
        echo ""
        echo "Backup successful, Script is set to send email only on Failure"
        echo ""
        }
    }

#------------------------------------------------------------------
# Main Backup Routine
#------------------------------------------------------------------  

$LogFile = $scriptPath+"\FullBackup.log"

Start-Transcript -path $LogFile

# Check to see if Windows Server Backup Features are installed
CheckForFeatures

# Volumes to backup, defaults to All Volumes
# Reference: technet.microsoft.com/en-us/library/ee706679.aspx
# If you only want Critical Volumes, such as the System Reserved and C:, you could use Get-WBVolume -CriticalVolumes
$Volumes = Get-WBVolume -AllVolumes

# Example, to backup just Critical Volumes
# $Volumes = Get-WBVolume -CriticalVolumes

# Backup folder
$FullBackupPath = ($BackupRootPath+"\$ComputerName\Current")

# Execute rotation if enabled
if ($MaxBackup -ne 0)
  {
      # If last backup was successful, execute the rotation function
    $jobs = Get-WBJob -Previous 1
    if ($jobs.HResult -eq 0)
      {
      Rotation
      }
  }

# Backup folder creation
if(!(Test-Path -Path $FullBackupPath )){
    New-Item $FullBackupPath -Type Directory | Out-Null
}

$WBPolicy = New-WBPolicy

Add-WBSystemState -Policy $WBPolicy | Out-Null
  
# Enable BareMetal functionnality (system state included)
# Reference: technet.microsoft.com/en-us/library/ee706681.aspx
Add-WBBareMetalRecovery -Policy $WBPolicy | Out-Null
  
# Add Network Backup target
$BackupLocation = New-WBBackupTarget -network ($FullBackupPath)
echo ""
echo "Backup Location is $BackupLocation"
echo ""
Add-WBBackupTarget -Policy $WBPolicy -Target $BackupLocation -force -WarningAction:SilentlyContinue | Out-Null
  
# Make this a full VSS backup as opposed to a copy backup.
# Reference: blogs.technet.com/.../...-windows-server-2008.aspx
Set-WBVssBackupOptions -policy $WBPolicy -vssfullbackup | Out-null

# Pause before continuing
echo ""
echo "Sleeping for 30 seconds, CTRL+C to abort"
echo ""
Start-Sleep 30

# Start capturing output
# Determine where the Script is being ran from for the log (start-transcript) file


# Backup folder creation
if (!(Test-Path -path $BackupLocation))
  {
    New-Item $BackupLocation -type directory | Out-Null
  }

# Displays the backup settings prior to running the job.
echo ""
echo "Starting Backup Job with the following properties:"
$WBPolicy
echo ""

# Runs the backup task.
echo ""
echo "Starting Backup"
echo ""
Start-WBBackup -Policy $WBPolicy

# Stop capturing output
Stop-Transcript

# Call email notification function
EmailNotification

Matt
July 9, 2015 13:52 CST

Hi

Great script, but for some reason when running this script on my system it would always delete the newest archive folder out of the directory rather than the oldest.

I had to sort the directory first by creation time, I added the sort-object command to the $backups line in the rotation function.


$Backups = @(Get-ChildItem -Path $BackupFolderNameArchive\*) | Sort-Object -Property CreationTime



This is what I was getting when I was run without adding Sort-Object:

PS C:\Users\Administrator> $Backups = @(Get-ChildItem -Path $BackupFolderNameArchive\*)
PS C:\Users\Administrator> $Backups


    Directory: \\x.x.x.x\Share\Server\Archive


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----         9/07/2015   4:06 PM            2015-07-09_053007
d----         8/07/2015   8:41 AM            2015-07-08_104401


PS C:\Users\Administrator> $Backups = @(Get-ChildItem -Path $BackupFolderNameArchive\*) | Sort-Object -Property CreationTime
PS C:\Users\Administrator> $backups


    Directory: \\x.x.x.x\Share\ServerArchive


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----         8/07/2015   8:41 AM            2015-07-08_104401
d----         9/07/2015   4:06 PM            2015-07-09_053007



Before Change:

PS C:\Users\Administrator> $I
0
PS C:\Users\Administrator> $Backups[$i]


    Directory: \\x.x.x.x\Share\Server\Archive


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----         9/07/2015   4:06 PM            2015-07-09_053007




After change:

PS C:\Users\Administrator> $i
0
PS C:\Users\Administrator> $Backups[$i]


    Directory: \\x.x.x.x\Share\Server\Archive


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----         8/07/2015   8:41 AM            2015-07-08_104401



Hope this helps someone.

Stan Czerno
July 9, 2015 14:21 CST

Thanks, good catch.

TC
July 29, 2015 04:48 CST

My computer have a hyphen in the and when I use the computer name in $ComputerName = $env:computername. I get unexpected token

Samer
October 27, 2015 01:07 CST

Thank you for that!!
It worked perfectly. I have only question, What I have to do if I want the LogFile to contain all the previous backups logs? since it's replacing the log file each time the backup runs.

Michael
November 25, 2015 08:49 CST

For some reason it was only backing up the system volumes for me (Windows 2012R2).  I had to insert these lines at line 245:

# Add Volumes to Policy
Add-WBVolume -Policy $WBPolicy -Volume $Volumes

After that works great.  Love the script!!

Thanks!!

Matthew
August 18, 2016 05:33 CST

I manage a small company of developers & have been working on implementing this script on our servers (all 2008r2 environment).

I have run into a small goofy issue & cannot seem to nail down the reason for it.

First, our Max Backup is set to 0.  The reason for this is one of our servers was taking about 4 hours to backup... after consideration, I decided to leave the last backup in place so that future backups would be vss per traditional WSB 2k8.  This reduced the backup time to about 30m after having the initial full backup seeded.

So the issue is...

Everyone once in a while (seems to happen every few backups or every few days, difficult to nail down) the "Backup Set info" of the email contains no information.  I would expect this to always have info as the set always exists now.

I plan to implement robocopy in the future for backupcopy job in order to fulfill GFS.

Am I missing something in the script or has anyone else had this trouble?

Thanks for your time!

Stan Czerno
August 18, 2016 05:40 CST

I have not seen this, but I only email on a failure so I rarely see the email. What does the log file show? I really need to re-visit this script and try to make it more robust.

Comments are closed