Accessing Volume Shadow Copy (VSS) Snapshots from powershell
Asked Answered
T

4

21

I am attempting to create and access a Volume Shadow Copy snapshot using the Windows Power Shell in Windows 7. I found that I can create snapshots using the following via a previous superuser question:

(Get-WmiObject -list win32_shadowcopy).create("C:\","ClientAccessible")

I am unable to find any documentation indicating that it is possible to set a shadow copy to "Exposed" so that it can be mapped to a drive letter using WMI. An article linked in the same question shows a hack using a junction to access the snapshot.

When I try to access the symlink, I get the following:

PS C:\Windows\system32> ls C:\shadowcopy
Get-ChildItem : The parameter is incorrect.

At line:1 char:3
+ ls <<<<  C:\shadowcopy
    + CategoryInfo          : ReadError: (C:\shadowcopy:String) [Get-ChildItem], IOException
    + FullyQualifiedErrorId : DirIOError,Microsoft.PowerShell.Commands.GetChildItemCommand

Attempting to access the snapshot directly gives the following:

PS C:\Windows\system32> ls '\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy14'
Get-ChildItem : Paths that begin with \\?\GlobalRoot are internal to the kernel and should not be opened by managed applications.
At line:1 char:3
+ ls <<<<  '\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy14'
    + CategoryInfo          : NotSpecified: (:) [Get-ChildItem], ArgumentException
    + FullyQualifiedErrorId : System.ArgumentException,Microsoft.PowerShell.Commands.GetChildItemCommand

How can I access a VSS snapshot from a powershell script?

Theona answered 8/1, 2013 at 3:30 Comment(2)
@Dominique that's not only a different question, it was also asked 3 years after this oneTheona
cmd /c "dir \\?\HarddiskVolumeShadowCopy3\\"Jelene
A
34

How did you create the symlink? As outlined in that article, you have to specify the device path with a trailing backslash:

$s1 = (Get-WmiObject -List Win32_ShadowCopy).Create("C:\\", "ClientAccessible")
$s2 = Get-WmiObject Win32_ShadowCopy | Where-Object { $_.ID -eq $s1.ShadowID }
$d  = $s2.DeviceObject + "\\"

Then $d is your volume letter specifier:

cmd /c mklink /d C:\shadowcopy "$d"

After this, I was able to access the shadow copy mounted to C:\shadowcopy just fine.

To unmount the shadow copy call $s2.Delete(), as @KeyszerS pointed out in the comments.

Accoucheur answered 8/1, 2013 at 10:49 Comment(5)
The trailing "\" was the critical part that I missed.Theona
For future readers, here is how to remove the shadow copy: "vssadmin delete shadows /Shadow=""$($s2.ID.ToLower())"" /Quiet" | iex then Remove-Item c:\shadowcopy -Confirm:$false -Force. Put this code after your work with the shadow copy.Legislate
Removing the shadow copy can be done natively through WMI using $s2.Delete() rather than executing vssadminZendah
Also works for cmd.exe if you can get the DeviceObject from somewhere, e.g. wmic shadowcopy get DeviceObject.Turbidimeter
The mklink does not need administrator privileges since directory junction works aswell cmd /c mklink /j C:\shadowcopy "$d". (Note the /j)Turbidimeter
L
3

So with the information provided here and the official microsoft documentation here https://learn.microsoft.com/en-us/previous-versions/windows/desktop/vsswmi/create-method-in-class-win32-shadowcopy I created a couple powershell functions/cmdlets that solve this problem. Random note, as of powershell 5 new-item has the itemType of symbolicLink but when I tried to make one with the target being the shadow snapshot it fails saying the path doesn't exist, so the mklink tool is still the way to go.

function New-ShadowLink {
[CmdletBinding()]
param (
    $linkPath="$($ENV:SystemDrive)\ShadowCopy"
)

begin {
    Write-Verbose "Creating a snapshot of $($ENV:SystemDrive)\"
    $class=[WMICLASS]"root\cimv2:win32_shadowcopy";
    $result = $class.create("$ENV:SystemDrive\", "ClientAccessible");
    Write-Verbose "Getting the full target path for a symlink to the shadow snapshot"
    $shadow = Get-CimInstance -ClassName Win32_ShadowCopy | Where-Object ID -eq $result.ShadowID
    $target = "$($shadow.DeviceObject)\";
}

process {
    Write-Verbose "Creating SymLink to shadowcopy at $linkPath"
    Invoke-Expression -Command "cmd /c mklink /d '$linkPath' '$target'";
}

end {
    Write-Verbose "Created link to shadowcopy snapshot of $($ENV:SystemDrive)\ at $linkPath";
    Write-Verbose "Returning shadowcopy snapshot object"
    return $shadow;
}

}

function Remove-ShadowLink {
[CmdletBinding()]
param (
    $shadow,
    $linkPath="$($ENV:SystemDrive)\ShadowCopy"
)

begin {
    Write-verbose "Removing shadow copy link at $linkPath"
}

process {
    Write-Verbose "Deleting the shadowcopy snapshot"
    $shadow.Delete();
    Write-Verbose "Deleting the now empty folder"
    Try {
        Remove-Item -Force -Recurse $linkPath -ErrorAction Stop;
    }
    catch {
        Invoke-Expression -Command "cmd /c rmdir /S /Q '$linkPath'";
    }
}

end {
    Write-Verbose "Shadow link and snapshot have been removed";
    return;
}

}

These could be utilized by copy pasting both functions then running them like this

$shadow = New-ShadowLink -Verbose;
ls C:\ShadowCopy # would show snapshot version of c drive
Remove-ShadowLink -shadow $shadow -Verbose;
ls C:\ShadowCopy # will give error as it doesn't exist

$s = New-ShadowLink -verbose
VERBOSE: Creating a snapshot of C:\
VERBOSE: Getting the full target path for a symlink to the shadow snapshot
VERBOSE: Creating SymLink to shadowcopy at C:\ShadowCopy
VERBOSE: Created link to shadowcopy snapshot of C:\ at C:\ShadowCopy
VERBOSE: Returning shadowcopy snapshot object
PS C:\> ls C:\ShadowCopy
    Directory: C:\ShadowCopy
#ommitted my C drive listing, but it would be here
PS C:\> Remove-ShadowLink -shadow $s -Verbose
VERBOSE: Removing shadow copy link at C:\ShadowCopy
VERBOSE: Deleting the shadowcopy snapshot
VERBOSE: Deleting the now empty folder
VERBOSE: Shadow link and snapshot have been removed
PS C:\> ls C:\ShadowCopy
ls : Cannot find path 'C:\ShadowCopy' because it does not exist.
Laryngitis answered 3/8, 2018 at 22:30 Comment(3)
Please format and indent your code properly when posting answers - otherwise, it's hard to read.Disarming
I tried using the two functions you provided, the Remove-ShadowLink does not seem to work, it throws "Method invocation failed because [System.String] does not contain a method named 'Delete'." so I end up having to remove it using vssadmin. Any idea why? This is a Windows 2016 box.Defect
The shadow object you pass to the function may not have been created right. I think it works best if no client accessible shadow copies currently exist when you create. It saying that string doesnt contain delete though makes it sound like you passed a string to the remove function instead of the special shadow object made with new shadowlinkLaryngitis
T
0

This is because the Invoke-Expression in the New-ShadowLink returns a string. If you modify the line to:

Invoke-Expression -Command "cmd /c mklink /d '$linkPath' '$target'" | Out-Null

it will fix the problem.

Tracietracing answered 28/6, 2019 at 10:35 Comment(0)
R
0

Try replacing $shadow.Delete() with Remove-CimInstance -InputObject $shadow

This has worked for me

Before I was getting similar error:

$shadow.Delete() was giving me error:

Error en la invocación del método porque [Microsoft.Management.Infrastructure.CimInstance] no contiene ningún método llamado 'Delete'. En línea: 42 Carácter: 9 + $shadow.Delete(); + ~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (Delete:String) [], RuntimeException + FullyQualifiedErrorId : MethodNotFound

Rescue answered 24/1, 2020 at 10:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.