Exchange 2007 and Windows 2008: Offline Exchange Backup

In my article Getting a List of Stores in a PowerShell Script you learned how to obtain a list of all the files involved for the Exchange database stores on a particular Exchange server. In the preceding article, Getting a List of Storage Groups in a PowerShell Script, you learned how to obtain a list of all the files unique to the Exchange storage groups on a particular Exchange server.

As a part of both of those articles, you learned how to create a list of the volumes used by the files in the storage groups and in the database stores.

Now that we have that information, what can we do with it?

Easy! We can generate a script that can create an offline backup of our Exchange databases. In future articles, you’ll learn how to turn this offline backup into an online backup, using VSS (the Volume Shadow Copy Service).

As a quick reminder, the following global objects are important and were introduced in the earlier articles of this series:

$volumes – a hash array containing the disk volumes used by the Exchange storage groups and database stores

$pathPattern – a hash array containing a list of all the regular-expression patterns required to back up all the files involved for all Exchange storage groups and database stores

getStores – a function populating $volumes and $pathPattern for the files used by Exchange database stores

getStorageGroups – a function populating $volumes and $pathPattern for the files used by Exchange storage groups

validateArrays – a function verifying that the $volumes and $pathPattern arrays are not empty; the function returns zero if the main program should proceed.

A utility that we have not previously discussed is robocopy. Robocopy was introduced as a part of the Windows 2000 Server Resource Kit. Among other features, it copies large files as quickly as possible, much more quickly than the cmd.exe copy and xcopy. Robocopy is a standard utility in Windows Vista and Windows Server 2008.

Two additional global variables need to be introduced:

$destination – the directory below which backups will be stored; in the case of Exchange backups, the directory structure is reproduced identically. For example, if $destination is “C:\Backups” and the file “C:\Program Files\Microsoft\Exchange Server\Mailbox\First Storage Group\E00.CHK” is a file to be backed up, then the destination file name will be “C:\Backups\Program Files\Microsoft\Exchange Server\Mailbox\First Storage Group\E00.CHK”.

$nl – a DOS newline

So, after all the preparation we’ve already done, an offline backup script actually turns out to be quite simple. The PowerShell script below will generate a DOS script to be executed by cmd.exe. Robocopy will copy all relevant files to the backup location specified by $destination. If all copies succeed then the script succeeds. If any copy fails, the script aborts.

A couple of things to be careful of – this script actually executes the backup! To do that, in an offline mode, the Microsoft Exchange Information Store service must be stopped. When that service is stopped, Exchange is basically down. So…don’t run this on your production system without commenting out the line that starts with cmd.exe (unless you are actually doing the offline backup).

Secondly, offline backups do not purge transaction logfiles. We’ll need to learn how to do an online backup before we can make that happen.

Finally, the out-file cmdlet uses an unusual parameter: “-encoding ascii”. This is because cmd.exe does not understand Unicode files (which is the default for out-file). Something to remember for your own scripts!

  
    $destination = "C:\backups"

    $nl = "`r`n"

    function buildRobocopyString($collection)
    {

        $str = ""

        foreach ($filepath in $collection)
        {
            $file = split-path $filepath -leaf
            $path = split-path $filepath -parent
            #
            # the destination path is the source path appended to
            # the backup folder location.
            #
            $destpath = join-path $destination $path.SubString(3, $path.Length - 3)

            $str += "echo Copying " + $file + "..." + $nl
            $str += "robocopy " + '"' + $path + '" "' + $destpath + 
                '" "' + $file + '" /copyall /ZB >nul' + $nl
            $str += "if not errorlevel 0 goto :abort" + $nl
        }

        return $str
    }

    function buildCMD
    {
        $script = "@echo off" + $nl

        $script += 'net stop "Microsoft Exchange Information Store" /y' + $nl

        $script += buildRobocopyString $pathPattern.keys
	$script += $nl
        $script += 'net start "Microsoft Exchange Information Store"' + $nl
        $script += "exit 0" + $nl
        $script += ":abort" + $nl
        $script += "exit 1" + $nl

        $script | out-file (join-path (gc env:temp) "offline-backup.cmd") -encoding ascii
        cmd.exe /c (join-path (gc env:temp) "offline-backup.cmd")
    }

    #
    # Main
    #

    if ((getStorageGroups) -eq 0)
    {
        getStores
        if ((validateArrays) -eq 0)
        {
            buildCMD
        }
    }

Until next time…

As always, if there are items you would like me to talk about, please drop me a line and let me know!


Follow me on twitter: @EssentialExch

Leave a Reply

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