So… I have been working on a little VBScript that can:
- take persistent VSS snapshots
- mount them to a folder (from which you can then backup the files)
- unmount VSS snapshots
It relies on vshadow.exe (documentation), part of the Volume Shadow Copy Service SDK 7.2 as available from Microsoft. I've been working with this version: "VSHADOW.EXE 2.2 - Volume Shadow Copy sample client, Copyright (C) 2005 Microsoft Corporation."
Basically, it is a neat little wrapper around these four vshadow commands:
vshadow.exe -q - List all shadow copies in the system vshadow.exe -p {volume list} - Manages persistent shadow copies vshadow.exe -el={SnapID},dir - Expose the shadow copy as a mount point vshadow.exe -ds={SnapID} - Deletes this shadow copy Here is its help screen:
VSS Snapshot Create/Mount Tool Usage: cscript /nologo VssSnapshot.vbs /target:path { /volume:X | /unmount } [/debug] /volume - drive letter of the volume to snapshot /target - the path (absolute or relative) to mount the snapshot to /debug - swich on debug output Examples: cscript /nologo VssSnapshot.vbs /target:C:\Backup\DriveD /volume:D cscript /nologo VssSnapshot.vbs /target:C:\Backup\DriveD /unmount Hint: No need to unmount before taking a new snapshot. Here some sample output:
C:\VssSnapshot>cscript /nologo VssSnapshot.vbs /target:MountPoints\E /volume:E 05/03/2010 17:13:04 preparing VSS mount point... 05/03/2010 17:13:04 mount point prepared at: C:\VssSnapshot\MountPoints\E 05/03/2010 17:13:04 creating VSS snapshot for volume: E 05/03/2010 17:13:08 snapshot created with ID: {4ed3a907-c66f-4b20-bda0-9dcda3b667ec} 05/03/2010 17:13:08 VSS snapshot mounted sucessfully 05/03/2010 17:13:08 finished C:\VssSnapshot>cscript /nologo VssSnapshot.vbs /target:MountPoints\E /unmount 05/03/2010 17:13:35 preparing VSS mount point... 05/03/2010 17:13:36 nothing else to do 05/03/2010 17:13:36 finished And here is the script itself. The usual disclaimer applies: The software is provided as is, I give no warranties, use at your own risk, if something breaks the only one to blame is yourself. I have tested it quite thoroughly, though and it works fine for me. Feel free to notify me of any bugs via the comments below.
''# VssSnapshot.vbs ''# http://serverfault.com/questions/119120/how-to-use-a-volume-shadow-copy-to-make-backups/119592#119592 Option Explicit Dim fso: Set fso = CreateObject("Scripting.FileSystemObject") ''# -- MAIN SCRIPT ------------------------------------------- Dim args, snapshotId, targetPath, success Set args = WScript.Arguments.Named CheckEnvironment Log "preparing VSS mount point..." targetPath = PrepareVssMountPoint(args("target")) If args.Exists("unmount") Then Log "nothing else to do" ElseIf targetPath <> vbEmpty Then Log "mount point prepared at: " & targetPath Log "creating VSS snapshot for volume: " & args("volume") snapshotId = CreateVssSnapshot(args("volume")) If snapshotId <> vbEmpty Then Log "snapshot created with ID: " & snapshotId success = MountVssSnapshot(snapshotId, targetPath) If success Then Log "VSS snapshot mounted sucessfully" Else Die "failed to mount snapshot" End If Else Die "failed to create snapshot" End If Else Die "failed to prepare mount point" End If Log "finished" ''# -- FUNCTIONS --------------------------------------------- Function PrepareVssMountPoint(target) ''# As String Dim cmd, result, outArray Dim path, snapshot, snapshotId Dim re, matches, match PrepareVssMountPoint = VbEmpty target = fso.GetAbsolutePathName(target) If Not fso.FolderExists(fso.GetParentFolderName(target)) Then Die "Invalid mount point: " & target End If ''# create or unmount (=delete existing snapshot) mountpoint If Not fso.FolderExists(target) Then If Not args.Exists("unmount") Then fso.CreateFolder target Else Set re = New RegExp re.MultiLine = False re.Pattern = "- Exposed locally as: ([^\r\n]*)" cmd = "vshadow -q" result = RunCommand(cmd, false) outarray = Split(result, "*") For Each snapshot In outArray snapshotId = ParseSnapshotId(snapshot) If snapshotId <> vbEmpty Then Set matches = re.Execute(snapshot) If matches.Count = 1 Then path = Trim(matches(0).SubMatches(0)) If fso.GetAbsolutePathName(path) = target Then cmd = "vshadow -ds=" & snapshotId RunCommand cmd, true Exit For End If End If End If Next If args.Exists("unmount") Then fso.DeleteFolder target End If PrepareVssMountPoint = target End Function Function CreateVssSnapshot(volume) ''# As String Dim cmd, result If Not fso.DriveExists(volume) Then Die "Drive " & volume & " does not exist." End If cmd = "vshadow -p " & Replace(UCase(volume), ":", "") & ":" result = RunCommand(cmd, false) CreateVssSnapshot = ParseSnapshotId(result) End Function Function MountVssSnapshot(snapshotId, target) ''# As Boolean Dim cmd, result If fso.FolderExists(targetPath) Then cmd = "vshadow -el=" & snapshotId & "," & targetPath result = RunCommand(cmd, true) Else Die "Mountpoint does not exist: " & target End If MountVssSnapshot = (result = "0") End Function Function ParseSnapshotId(output) ''# As String Dim re, matches, match Set re = New RegExp re.Pattern = "SNAPSHOT ID = (\{[^}]{36}\})" Set matches = re.Execute(output) If matches.Count = 1 Then ParseSnapshotId = matches(0).SubMatches(0) Else ParseSnapshotId = vbEmpty End If End Function Function RunCommand(cmd, exitCodeOnly) ''# As String Dim shell, process, output Dbg "Running: " & cmd Set shell = CreateObject("WScript.Shell") On Error Resume Next Set process = Shell.Exec(cmd) If Err.Number <> 0 Then Die Hex(Err.Number) & " - " & Err.Description End If On Error GoTo 0 Do While process.Status = 0 WScript.Sleep 100 Loop output = Process.StdOut.ReadAll If process.ExitCode = 0 Then Dbg "OK" Dbg output Else Dbg "Failed with ERRORLEVEL " & process.ExitCode Dbg output If Not process.StdErr.AtEndOfStream Then Dbg process.StdErr.ReadAll End If End If If exitCodeOnly Then Runcommand = process.ExitCode Else RunCommand = output End If End Function Sub CheckEnvironment Dim argsOk If LCase(fso.GetFileName(WScript.FullName)) <> "cscript.exe" Then Say "Please execute me on the command line via cscript.exe!" Die "" End If argsOk = args.Exists("target") argsOk = argsOk And (args.Exists("volume") Or args.Exists("unmount")) If Not argsOk Then Say "VSS Snapshot Create/Mount Tool" & vbNewLine & _ vbNewLine & _ "Usage: " & vbNewLine & _ "cscript /nologo " & fso.GetFileName(WScript.ScriptFullName) & _ " /target:path { /volume:X | /unmount } [/debug]" & _ vbNewLine & vbNewLine & _ "/volume - drive letter of the volume to snapshot" & _ vbNewLine & _ "/target - the path (absolute or relative) to mount the snapshot to" & _ vbNewLine & _ "/debug - swich on debug output" & _ vbNewLine & vbNewLine & _ "Examples: " & vbNewLine & _ "cscript /nologo " & fso.GetFileName(WScript.ScriptFullName) & _ " /target:C:\Backup\DriveD /volume:D" & vbNewLine & _ "cscript /nologo " & fso.GetFileName(WScript.ScriptFullName) & _ " /target:C:\Backup\DriveD /unmount" & _ vbNewLine & vbNewLine & _ "Hint: No need to unmount before taking a new snapshot." & vbNewLine Die "" End If End Sub Sub Say(message) If message <> "" Then WScript.Echo message End Sub Sub Log(message) Say FormatDateTime(Now()) & " " & message End Sub Sub Dbg(message) If args.Exists("debug") Then Say String(75, "-") Say "DEBUG: " & message End If End Sub Sub Die(message) If message <> "" Then Say "FATAL ERROR: " & message WScript.Quit 1 End Sub
I hope this helps somebody. Feel free to use it in accordance with cc-by-sa. All I ask is that you leave the link intact that points back here.