11

The plan is to create a shadow copy of a quite large, I/O-heavy volume. It's 350GB, containing a filesystem-based fulltext index organized in hundreds of folders and hundred-thousands of tiny files that need to be in a consistent state for a successful restore.

Currently the indexer is stopped, the backup task runs, and then the indexer is restarted. This results in the index being unavailable for hours during the backup. I'd like to make consistent backups via shadow copy, ideally without ever having to stop the indexer at all.

So, I've switched on Shadow Copy for that volume and configured it to make a snapshot once every night, to a different volume.

Now I'm a bit at a loss - how can I access the shadow copy as a whole, so that I can make a backup? I envision a read-only drive that contains the files as they were at the time of the last snapshot, but maybe things work entirely different.

OS is Windows Server 2003 SP2, backup software is CommVault Galaxy 7.0.


EDIT: Note that - in the meantime - two answers have been created that implement the necessary functionality in form of a script:

storsan
  • 83
  • 9
Tomalak
  • 1,615

8 Answers8

11

So in the spirit of reinventing the wheel, I present to you Tomalak's excellent script (see above) but completely rewritten in Powershell!!! The main reason I did this was to evangelise the awesome powers of Powershell, but also because I despise vbscript with my entire being.

It's mostly feature to feature identical, but I did implement some things a little differently for various reasons. The debugging output is definitely more verbose.

One very important thing to note is that this version detects the OS version and bitness and calls the appropriate version of vshadow.exe. I've included a chart below to show which versions of vshadow.exe to use, where to get them, and what to name them.


Here's the usage info:

VssSnapshot.ps1

Description:
  Create, mount or delete a Volume Shadow Copy Service (VSS) Shadow Copy (snapshot)

Usage:
  VssSnapshot.ps1 Create -Target <Path> -Volume <Volume> [-Debug]
  VssSnapshot.ps1 Delete -Target <Path> [-Debug]

Paremeters:
  Create  - Create a snapshot for the specified volume and mount it at the specified target
  Delete  - Unmount and delete the snapshot mounted at the specified target
  -Target - The path (quoted string) of the snapshot mount point
  -Volume - The volume (drive letter) to snapshot
  -Debug  - Enable debug output (optional)

Examples:
  VssSnapshot.ps1 Create -Target D:\Backup\DriveC -Volume C
  - Create a snapshot of volume C and mount it at "D:\Backup\DriveC"

  VssSnapshot.ps1 Delete -Target D:\Backup\DriveC
  - Unmount and delete a snapshot mounted at "D:\Backup\DriveC"

Advanced:
  VssSnapshot.ps1 create -t "c:\vss mount\c" -v C -d
  - Create a snapshot of volume C and mount it at "C:\Vss Mount\C"
  - example mounts snapshot on source volume (C: --> C:)
  - example uses shortform parameter names
  - example uses quoted paths with whitespace
  - example includes debug output

Here's the script:

# VssSnapshot.ps1
# http://serverfault.com/questions/119120/how-to-use-a-volume-shadow-copy-to-make-backups/119592#119592

Param ([String]$Action, [String]$Target, [String]$Volume, [Switch]$Debug)
$ScriptCommandLine = $MyInvocation.Line
$vshadowPath = "."

# Functions
Function Check-Environment {
  Write-Dbg "Checking environment..."

  $UsageMsg = @'
VssSnapshot

Description:
  Create, mount or delete a Volume Shadow Copy Service (VSS) Shadow Copy (snapshot)

Usage:
  VssSnapshot.ps1 Create -Target <Path> -Volume <Volume> [-Debug]
  VssSnapshot.ps1 Delete -Target <Path> [-Debug]

Paremeters:
  Create  - Create a snapshot for the specified volume and mount it at the specified target
  Delete  - Unmount and delete the snapshot mounted at the specified target
  -Target - The path (quoted string) of the snapshot mount point
  -Volume - The volume (drive letter) to snapshot
  -Debug  - Enable debug output (optional)

Examples:
  VssSnapshot.ps1 Create -Target D:\Backup\DriveC -Volume C
  - Create a snapshot of volume C and mount it at "D:\Backup\DriveC"

  VssSnapshot.ps1 Delete -Target D:\Backup\DriveC
  - Unmount and delete a snapshot mounted at "D:\Backup\DriveC"

Advanced:
  VssSnapshot.ps1 create -t "c:\vss mount\c" -v C -d
  - Create a snapshot of volume C and mount it at "C:\Vss Mount\C"
  - example mounts snapshot on source volume (C: --> C:)
  - example uses shortform parameter names
  - example uses quoted paths with whitespace
  - example includes debug output
'@

  If ($Action -eq "Create" -And ($Target -And $Volume)) {
    $Script:Volume = (Get-PSDrive | Where-Object {$_.Name -eq ($Volume).Substring(0,1)}).Root
    If ($Volume -ne "") {
      Write-Dbg "Verified volume: $Volume"
    } Else {
      Write-Dbg "Cannot find the specified volume"
      Exit-Script "Cannot find the specified volume"
    }
    Write-Dbg "Argument check passed"
  } ElseIf ($Action -eq "Delete" -And $Target ) {
    Write-Dbg "Argument check passed"
  } Else {
    Write-Dbg "Invalid arguments: $ScriptCommandLine"
    Exit-Script "Invalid arguments`n`n$UsageMsg"
  }


  $WinVer = ((Get-WmiObject Win32_OperatingSystem).Version).Substring(0,3)
    Switch ($WinVer) {
    "5.2" {
      $vshadowExe = "vshadow_2003"
      $WinBit = ((Get-WmiObject Win32_Processor)[0]).AddressWidth
    }
    "6.0" {
      $vshadowExe = "vshadow_2008"
      $WinBit = (Get-WmiObject Win32_OperatingSystem).OSArchitecture
    }
    "6.1" {
      $vshadowExe = "vshadow_2008R2"
      $WinBit = (Get-WmiObject Win32_OperatingSystem).OSArchitecture
    }
    Default {
      Write-Dbg "Unable to determine OS version"
      Exit-Script "Unable to determine OS version"
    }
  }

  Switch ($WinBit) {
    {($_ -eq "32") -or ($_ -eq "32-bit")} {$vshadowExe += "_x86.exe"}
    {($_ -eq "64") -or ($_ -eq "64-bit")} {$vshadowExe += "_x64.exe"}
    Default {
      Write-Dbg "Unable to determine OS bitness"
      Exit-Script "Unable to determine OS bitness"
    }
  }

  $Script:vshadowExePath = Join-Path $vshadowPath $vshadowExe
  If (Test-Path $vshadowExePath) {
    Write-Dbg "Verified vshadow.exe: $vshadowExePath"
  } Else {
    Write-Dbg "Cannot find vshadow.exe: $vshadowExePath"
    Exit-Script "Cannot find vshadow.exe"
  }

  Write-Dbg "Environment ready"
}

Function Prepare-Target {
  Write-Log "Preparing target..."
  Write-Dbg "Preparing target $Target"


  If (!(Test-Path (Split-Path $Target -Parent))) {
  Write-Dbg "Target parent does not exist"
  Exit-Script "Invalid target $Target"
  }
  If ((Test-Path $Target)) {
    Write-Dbg "Target already exists"
    If (@(Get-ChildItem $Target).Count -eq 0) {
      Write-Dbg "Target is empty"
    } Else {
      Write-Dbg "Target is not empty"
      Exit-Script "Target contains files/folders"
    }
  } Else {
    Write-Dbg "Target does not exist. Prompting user..."
    $PromptYes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Create target folder"
    $PromptNo = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Do not create target folder"
    $PromptOptions = [System.Management.Automation.Host.ChoiceDescription[]]($PromptYes, $PromptNo)
    $PromptResult = $Host.UI.PromptForChoice("Create folder", "The target folder `"$target`" does not exist.`nWould you like to create the folder?", $PromptOptions, 0) 
    Switch ($PromptResult) {
      0 {
        Write-Dbg "User Accepted. Creating target..."
        $Null = New-Item -Path (Split-Path $Target -Parent) -Name (Split-Path $Target -Leaf) -ItemType "Directory"
      }
      1 {
        Write-Dbg "User declined. Exiting..."
        Exit-Script "Target does not exist"
      }
    }
  }
  Write-Log "Target ""$Target"" ready"
  Write-Dbg """$Target"" ready"
}

Function Create-Snapshot {
  Write-Log "Creating snapshot..."
  Write-Dbg "Creating snapshot of $Volume"
  $Cmd = "$vshadowExePath -p $Volume"
  $CmdResult = Run-Command $Cmd -AsString

  Write-Dbg "Snapshot created successfully"

  $SnapshotID = $CmdResult -Match 'SNAPSHOT ID = (\{[^}]{36}\})'
  If ($SnapshotID) {
    $SnapshotID = $Matches[1]
    Write-Dbg "SnapshotID: $SnapshotID"
    Write-Log "Snapshot $SnapshotID created"
  } Else {
    Write-Dbg "Unable to determine SnapshotID"
    Exit-Script "Unable to determine SnapshotID"
  }

  Return $SnapshotID
}

Function Mount-Snapshot ($SnapshotID) {
  Write-Log "Mounting snapshot..."
  Write-Dbg "Mounting $SnapshotID at ""$Target"""

  $Cmd = "$vshadowExePath `"-el=$SnapshotId,$Target`"" #Must use escaped quotes because Invoke-Expression gets all weird about curly braces
  $CmdResult = Run-Command $Cmd

  Write-Log "Snapshot $SnapshotID mounted at target ""$Target"""
  Write-Dbg "$SnapshotID mounted at ""$Target"""
}

Function Delete-Snapshot {
  Write-Log "Deleting snapshot..."
  Write-Dbg "Deleting snapshot at target ""$Target"""

  $SnapshotID = Get-SnapshotIdbyTarget

  $Cmd = "$vshadowExePath `"-ds=$SnapshotId`""
  $CmdResult = Run-Command $Cmd

  Write-Log "Snapshot $SnapshotID deleted at target ""$Target"""
  Write-Dbg "$SnapshotID deleted at ""$Target"""
}

Function Get-SnapshotIdbyTarget {
  Write-Dbg "Finding SnapshotID for $Target"

  $Cmd = "$vshadowExePath -q"
  $CmdResult = Run-Command $Cmd -AsString

  $TargetRegEx = '(?i)' + $Target.Replace('\','\\') + '\\?\r'
  $Snapshots = ($CmdResult.Split('*')) -Match $TargetRegEx | Out-String

  If ($Snapshots) {
    $Null = $Snapshots -Match '(\{[^}]{36}\})'
    $SnapshotID = $Matches[0]
  } Else {
    Write-Dbg "Unable to determine SnapshotID for target $Target"
    Exit-Script "Unable to determine SnapshotID"
  }  

  Write-Dbg "SnapshotID: $SnapshotID"

  Return $SnapshotID
}

Function Run-Command ([String]$Cmd, [Switch]$AsString=$False, [Switch]$AsArray=$False) {
  Write-Dbg "Running: $Cmd"

  $CmdOutputArray = Invoke-Expression $Cmd
  $CmdOutputString = $CmdOutputArray | Out-String
  $CmdErrorCode = $LASTEXITCODE

  If ($CmdErrorCode -eq 0 ) {
    Write-Dbg "Command successful. Exit code: $CmdErrorCode"
    Write-Dbg $CmdOutputString
  } Else {
    Write-Dbg "Command failed. Exit code: $CmdErrorCode"
    Write-Dbg $CmdOutputString
    Exit-Script "Command failed. Exit code: $CmdErrorCode"
  }

  If (!($AsString -or $AsArray)) {
    Return $CmdErrorCode
  } ElseIf ($AsString) {
    Return $CmdOutputString
  } ElseIf ($AsArray) {
    Return $CmdOutputArray
  }
}

Function Write-Msg ([String]$Message) {
  If ($Message -ne "") {
    Write-Host $Message
  }
}

Function Write-Log ([String]$Message) {
  Write-Msg "[$(Get-Date -Format G)] $Message"
}

Function Write-Dbg ([String]$Message) {
  If ($Debug) {
    Write-Msg ("-" * 80)
    Write-Msg "[DEBUG] $Message"
    Write-Msg ("-" * 80)
  }
}

Function Exit-Script ([String]$Message) {
  If ($Message -ne "") {
    Write-Msg "`n[FATAL ERROR] $Message`n"
  }
  Exit 1
}

# Main
Write-Log "VssSnapshot started"
Check-Environment

Switch ($Action) {
  "Create" {
    Prepare-Target
    $SnapshotID = Create-Snapshot
    Mount-Snapshot $SnapshotID
  }
  "Delete" {
    Delete-Snapshot
  }
}

Write-Log "VssSnapshot finished"

Here are the vshadow.exe versions to use:

  1. Windows 2003/2003R2
    • Volume Shadow Copy Service SDK 7.2
    • x86: C:\Program Files\Microsoft\VSSSDK72\TestApps\vshadow\bin\release-server\vshadow.exe
      • Rename to: vshadow_2003_x86.exe
    • x64: I have not been able to locate an x64 version of vshadow.exe for Windows 2003 x64
  2. Windows 2008
    • Windows SDK for Windows Server 2008 and .NET Framework 3.5
    • x86: C:\Program Files\Microsoft SDKs\Windows\v6.1\Bin\vsstools\vshadow.exe
      • Rename to: vshadow_2008_x86.exe
    • x64: C:\Program Files\Microsoft SDKs\Windows\v6.1\Bin\x64\vsstools\vshadow.exe
      • Rename to: vshadow_2008_x64.exe
  3. Windows 2008R2
    • Microsoft Windows SDK for Windows 7 and .NET Framework 4
    • x86: C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\vsstools\vshadow.exe
      • Rename to: vshadow_2008R2_x86.exe
    • x64: C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\x64\vsstools\vshadow.exe
      • Rename to: vshadow_2008R2_x64.exe
John Homer
  • 1,343
9

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.

Tomalak
  • 1,615
6
  1. Use the command vssadmin list shadows to list all the available shadow copies. You'll get an output like this...
C:\> vssadmin list shadows
vssadmin 1.1 - Volume Shadow Copy Service administrative command-line tool
(C) Copyright 2001 Microsoft Corp.

Contents of shadow copy set ID: {b6f6fb45-bedd-4b77-8f51-14292ee921f3}
   Contained 1 shadow copies at creation time: 9/25/2016 12:14:23 PM
      Shadow Copy ID: {321930d4-0442-4cc6-b2aa-ec47f21d0eb1}
         Original Volume: (C:)\\?\Volume{ad1dd231-1200-11de-b1df-806e6f6e6963}\
         Shadow Copy Volume: \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy68
         Originating Machine: joshweb.josh.com
         Service Machine: joshweb.josh.com
         Provider: 'Microsoft Software Shadow Copy provider 1.0'
         Type: ClientAccessible
         Attributes: Persistent, Client-accessible, No auto release, No writers, Differential

Contents of shadow copy set ID: {c4fd8646-57b3-4b39-be75-47dc8e7f881d}
   Contained 1 shadow copies at creation time: 8/25/2016 7:00:18 AM
      Shadow Copy ID: {fa5da100-5d90-493c-89b1-5c27874a23c6}
         Original Volume: (E:)\\?\Volume{4ec17949-12b6-11de-8872-00235428b661}\
         Shadow Copy Volume: \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy3
         Originating Machine: joshweb.josh.com
         Service Machine: joshweb.josh.com
         Provider: 'Microsoft Software Shadow Copy provider 1.0'
         Type: ClientAccessible
         Attributes: Persistent, Client-accessible, No auto release, No writers, Differential

C:\
  1. Note the Shadow Copy Volume name for the shadow copy you want (easiest to the clipboard).

  2. Mount the Shadow Copy

On Windows 2003...

You will need to download the resource kit tools for 2003 if you don't already have it.

Enter the command...

linkd c:\shadow \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy69\

...where c:\shadow is the path where you want the shadow copy to appear and \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy69 is the name you copied above. Note that you must add a backslash at the end of the shadow copy name!

On Windows 2008 and up...

Enter the command...

mklink c:\shadow \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy69\

...where c:\shadow is the path where you want the shadow copy to appear and \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy69 is the name you copied above. Note that you must add a backslash at the end of the shadow copy name!

  1. Use whatever tool you want (including windows explorer or XCOPY) to access the files from c:\shadow.
Tero Kilkanen
  • 38,887
2

You're misunderstanding how VSS works with the filesystem (how it works with databases is completely different). On the file system, VSS is used to implement the "Previous Versions" feature, which is solely used for snapshotting changes to files and folders at predefined points in time for recovery via the Previous Versions tab in clients. These changes then get merged with the data on the volume for building the recovery set. So it's dependent on the original volume still being there in order to perform the recovery, which is in other words useless for the purposes of proper backup and restore.

I think you need to step back from how you want to do this and think again about what it is you want to do.

350 GB of data is not a lot really, and I'm willing to bet that the percentage of that which gets actively used on a day to day basis is quite low. Have you considered doing nightly differential backups with full backups only on weekends? Or using scheduled DFS replication to alternative storage in order to get a "snapshot" (which is then backed up)?

2

Hope this is what you want:

diskshadow -s vssbackup.cfg

vssbackup.cfg:

set context persistent
set metadata E:\backup\result.cab
set verbose on
begin backup
     add volume C: alias ConfigVolume
     create
     EXPOSE %ConfigVolume% Y:
     # Y is your VSS drive
     # run your backup script here
     delete shadows exposed Y:
end backup
jackbean
  • 63
  • 1
  • 5
1

This is a working VB example using Visual Studio 2013 Pro .NET Framework 4.5 and AlphaVSS. To add AlphaVSS to your project, in Visual Studio click View, Other Windows, Package Manager Console. Go to the Package Manager Console and run this command:

NuGet\Install-Package AlphaVSS -Version 2.0.3

Add this to the beginning of your modules or classes that need to talk to VSS.

Imports Alphaleonis.Win32

This sub creates a shadow copy of an entire volume and mounts it as a file share. Files can then be copied from the temporary file share to a backup medium. The shadow copy is then deleted.

Imports Alphaleonis.Win32

Public Shared Sub CopyFromShadow()

Dim SnapshotGUID As New Guid
Dim SnapSetGUID As New Guid
Dim ExposedShare As String = &quot;VBSHADOW&quot;                                        ' The temporary file share to which the snapshot will be mounted.

Dim vssFactory = Vss.VssFactoryProvider.Default.GetVssFactory()                ' Create vssFactory interface.
Dim vssBackupComponents = vssFactory.CreateVssBackupComponents                 ' Create backup components.

vssBackupComponents.InitializeForBackup(Nothing)                               ' Must be called before SetBackupState.
vssBackupComponents.SetBackupState(False, True, Vss.VssBackupType.Full, False) ' ComponentMode, Bootable, BackupType, PartialSupport
vssBackupComponents.GatherWriterMetadata()                                     ' Initiates communication with VSS.
vssBackupComponents.GatherWriterStatus()                                       ' Must be called before StartSnapshotSet.
SnapSetGUID = vssBackupComponents.StartSnapshotSet                             ' Creates a new SnapshotSet, returns the GUID of the new set.
SnapshotGUID = vssBackupComponents.AddToSnapshotSet(&quot;C:\&quot;)                     ' Add the volume containing your files to be backed up.
vssBackupComponents.PrepareForBackup()                                         ' PrepareForBackup needs to be called before DoSnapshotSet.
vssBackupComponents.DoSnapshotSet()                                            ' Tells VSS to create the snapshot (set).
vssBackupComponents.GatherWriterStatus()                                       ' Must be called before ExposeSnapshot.

' Exposes a snapshot of the entire volume as a file share.
Dim ExposeResult As String = vssBackupComponents.ExposeSnapshot(SnapshotGUID, &quot;\&quot;, Vss.VssVolumeSnapshotAttributes.ExposedRemotely, ExposedShare)

' At this point, all data on the volume should be accessible via the exposed share. [\\localhost\VBSHADOW]
' Copy all of your files to your backup medium here.
' ---
'
'IO.File.Copy(&quot;\\localhost\VBSHADOW\Users\xyz\Documents\Outlook.pst&quot;, &quot;D:\backup\Outlook.pst&quot;)
'
' ---
' When finished with the shadow, it must be deleted.

vssBackupComponents.GatherWriterStatus()                                       ' Must be called before BackupComplete.
vssBackupComponents.BackupComplete()                                           ' Signals to VSS that we're about to delete the snapshot.
vssBackupComponents.DeleteSnapshotSet(SnapSetGUID, True)                       ' Delete the snapshot (set). [SetGUID, ForceDelete]

End Sub

It took me 3 days of reading the VSS documentation and googling and experimenting to figure this out. VSS is much, much more complex and capable than what we're doing here. This is one quick and simple way to backup a single file that is in use.

benvss
  • 11
0

Using the VSS API, it's possible to take a "snapshot" of the volume. Then you'd have to mount that snapshot in order to copy from it. I'm familiar with a now-dead product which used this technique to replicate data despite files being exclusively opened by other processes in the live file system. Valid questions can be raised about whether the files in the VSS snapshot are self-consistent if they're being written by apps that aren't integrated with the VSS APIs. There may be other products which offer similar capabilities.

Fred
  • 396
-1

Short answer: You can't.

Slightly longer answer: The shadow copy service can be used programatically via its API to allow backing up of open files but the service doesn't create complete snapshots of the system, only partial snapshots.