Mount and dismount volume shadow copies

Volume shadow copies can help you to:

All the demos, I’ve seen so far were using the built-in DOS mklink command to mount a volume shadow copy and vssadmin to list shadow copies. While playing with vssadmin, I’ve found a use of case of the context parameter of the Select-String cmdlet.

vssadmin list shadows | 
Select-String -Pattern "shadow copies at creation time" -Context 0,3 |
ForEach-Object {
    [pscustomobject]@{
        Path = (($_.Context.PostContext -split "\r\n")[2] -split ':')[1].Trim();
        InstallDate = ($_.Line -split ':\s',2)[1];
    }
}


Ugly, I know. Let’s forget about this, there’s a very simple way return the list volume shadow copies on Windows as objects.

Last year, Boe Prox wrote an excellent article where he showed how to Create a Symbolic Link using PowerShell. Kudos to him, I’ll reuse his brilliant code 😀

Unfortunately, he didn’t show how to remove these symbolic links. The following MSDN article tells us how:

To remove a symbolic link, delete the file (using DeleteFile or similar APIs) or remove the directory (using RemoveDirectory or similar APIs) depending on what type of symbolic link is used.

Now let’s see what I propose to mount and dismount volume shadow copies:

Function Mount-VolumeShadowCopy {
<#
    .SYNOPSIS
        Mount a volume shadow copy.
    
    .DESCRIPTION
        Mount a volume shadow copy.
     
    .PARAMETER ShadowPath
        Path of volume shadow copies submitted as an array of strings
     
    .PARAMETER Destination
        Target folder that will contain mounted volume shadow copies
             
    .EXAMPLE
        Get-CimInstance -ClassName Win32_ShadowCopy | 
        Mount-VolumeShadowCopy -Destination C:\VSS -Verbose

#>
[CmdletBinding()]
Param(
    [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
    [ValidatePattern('\\\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy\d{1,}')]
    [Alias("DeviceObject")]
    [String[]]$ShadowPath,

    [Parameter(Mandatory)]
    [ValidateScript({
        Test-Path -Path $_ -PathType Container
    }
    )]
    [String]$Destination
)
Begin {
    Try {
        $null = [mklink.symlink]
    } Catch {
        Add-Type @"
        using System;
        using System.Runtime.InteropServices;
 
        namespace mklink
        {
            public class symlink
            {
                [DllImport("kernel32.dll")]
                public static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags);
            }
        }
"@
    }
}
Process {

    $ShadowPath | ForEach-Object -Process {

        if ($($_).EndsWith("\")) {
            $sPath = $_
        } else {
            $sPath = "$($_)\"
        }
       
        $tPath = Join-Path -Path $Destination -ChildPath (
        '{0}-{1}' -f (Split-Path -Path $sPath -Leaf),[GUID]::NewGuid().Guid
        )
        
        try {
            if (
                [mklink.symlink]::CreateSymbolicLink($tPath,$sPath,1)
            ) {
                Write-Verbose -Message "Successfully mounted $sPath to $tPath"
            } else  {
                Write-Warning -Message "Failed to mount $sPath"
            }
        } catch {
            Write-Warning -Message "Failed to mount $sPath because $($_.Exception.Message)"
        }
    }

}
End {}
}

Function Dismount-VolumeShadowCopy {
<#
    .SYNOPSIS
        Dismount a volume shadow copy.
    
    .DESCRIPTION
        Dismount a volume shadow copy.
     
    .PARAMETER Path
        Path of volume shadow copies mount points submitted as an array of strings
     
    .EXAMPLE
        Get-ChildItem -Path C:\VSS | Dismount-VolumeShadowCopy -Verbose
        

#>

[CmdletBinding()]
Param(
    [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
    [Alias("FullName")]
    [string[]]$Path
)
Begin {
}
Process {
    $Path | ForEach-Object -Process {
        $sPath =  $_
        if (Test-Path -Path $sPath -PathType Container) {
            if ((Get-Item -Path $sPath).Attributes -band [System.IO.FileAttributes]::ReparsePoint) {
                try {
                    [System.IO.Directory]::Delete($sPath,$false) | Out-Null
                    Write-Verbose -Message "Successfully dismounted $sPath"
                } catch {
                    Write-Warning -Message "Failed to dismount $sPath because $($_.Exception.Message)"
                }
            } else {
                Write-Warning -Message "The path $sPath isn't a reparsepoint"
            }
        } else {
            Write-Warning -Message "The path $sPath isn't a directory"
        }
     }
}
End {}
}

Let’s see these two functions in action:

  • Mount all volume shadow copies
  • Get-CimInstance -ClassName Win32_ShadowCopy | 
    Mount-VolumeShadowCopy -Destination C:\VSS -Verbose
    

  • Dismount all volume shadow copies mount points located in C:\VSS
  • Get-ChildItem -Path C:\VSS | 
    Dismount-VolumeShadowCopy -Verbose
    

PowerShell rocks! No doubt 😎

Advertisements

5 thoughts on “Mount and dismount volume shadow copies

  1. Pingback: « rakhesh.com

  2. Pingback: Shadow copy készítése Powershell-lel | Informatikai Tippek

  3. Pingback: Native powershell support for VSS snapshot mounting | mbrownnyc

    • Hi,
      Yes, you can probably execute the code through PowerShell remoting as a scriptblock. I haven’t tried it myself but you could try

      Invoke-Command -ComputerName "target" -ScriptBlock {
       # 1. create locally a folder
       mkdir C:\VSS
       # 2. Embbed the function in the scriptblock
       Function Mount-VolumeShadowCopy {
        # copy/paste here the code of the function
       }
       # 3. Try to mount
       Get-CimInstance -ClassName Win32_ShadowCopy | 
       Mount-VolumeShadowCopy -Destination C:\VSS -Verbose
      }
      

      If the above works, you can then access the shadow copies of the target computer through the path \\target\c$\VSS

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s