PowerShell: Create and Delete VSS Snapshots

Update 7/30/20: use this newer version

# There are 2 functions in this snippet
# 1. Create a VSS Snapshot Shadow on a specific volume
# 2. Delete a VSS Snapshot using Snapshot ID
#
# Limitations:
# 1. Microsoft VSS must be available on the target system.
# 2. Environmental checks are assumed to have been performed to ensure that there is adequate disk space for storing snapshots.

function createShadow{
[cmdletbinding()]
param(
[string]$targetVolume="C:\"
)
# Sanitation
if (!($targetVolume -like "*\")){$targetVolume+="\"}
$GLOBAL:shadowMount="C:\shadowcopy";
if(Test-Path $shadowMount){(Get-Item $shadowMount).Delete()}

write-host "Initiating VSS snapshot..."
$shadowCopyClass=[WMICLASS]"root\cimv2:win32_shadowcopy"
$thisSnapshot = $shadowCopyClass.Create($targetVolume, "ClientAccessible")
$thisShadow = Get-WmiObject Win32_ShadowCopy | Where-Object { $_.ID -eq $thisSnapshot.ShadowID }
$thisShadowPath = $thisShadow.DeviceObject + "\"

# Creating symlink
$voidOutput=cd C:
$voidOutput=cmd /c mklink /d $shadowMount $thisShadowPath | Out-Null
write-host "Shadow of $targetVolume has been made and it's accessible at this local file system (LFS): $shadowMount."

# Validation
if(Test-Path $shadowMount){
$snapshotId=$thisShadow.ID;
write-host "Snapshot $snapshotId has been created.";
return $snapshotId;
}else{
write-host "Failed to create client accessible VSS Snapshot.";
return $false;
}
}

function deleteShadow{
[cmdletbinding()]
param(
[string]$targetVolume="C:\",
[string]$snapShotId
)

# Deterministic method of obtaining newest snapshot ID if it is not specified
if(!($snapShotId)){
$lastSnapshotIdString=vssadmin list shadows /for=$targetVolume|`
%{if($_ -like "*Shadow Copy ID:*"){$_}}|`
select-object -last 1|`
%{[void]($_ -match "{(.*)}$"); $matches[1]}
$snapShotId="{$lastSnapshotIdString}"
}

# Remove a single snapshot
write-host "Removing snapshot Id $snapShotId..."
#$removeSnapshotCommand="cmd.exe /c vssadmin delete shadows /Shadow=$snapShotId /quiet"
#$voidOutput=cmd.exe /c vssadmin delete shadows /Shadow=$lastSnapshotId /quiet
<#
invoke-expression 'cmd /c start powershell -Command {
param($snapShotId);
$command="cmd.exe /c vssadmin delete shadows /Shadow=$snapShotId /quiet";
write-host $command;
invoke-expression $command;
pause; } -Args $snapShotId'
#>
# This is the workaround to the annoyance of antivirus software terminating sessions upon invoking the snapshot removal procedure
$newSession=New-PSSession
Invoke-Command -Session $newSession -ScriptBlock{param($snapShotId);vssadmin delete shadows /Shadow=$snapShotId /quiet} -args $snapShotId
$newSession|Remove-PSSession

# Remove symlink
write-host "Removing symlink $shadowMount..."
(Get-Item $shadowMount).Delete()

# Remove all Snapshots
#Get-WmiObject Win32_ShadowCopy | % {$_.delete()}
#vssadmin delete shadows /For=$targetVolume /Quiet

# Validation
#vssadmin list shadows /for=$targetVolume
$validateLastSnapshot=vssadmin list shadows /for=$targetVolume|`
%{if($_ -like "*Shadow Copy ID:*"){$_}}|`
select-object -last 1|`
%{[void]($_ -match "{(.*)}$"); $matches[1]}
$validateLastSnapshotId="{$validateLastSnapshot}"
if(!($validateLastSnapshotId -eq $snapShotId)){
write-host "Last snapshot Id is now $validateLastSnapshotId"
return $true
}else{
write-host "$snapShotId still exists. There was an error in its removal";
return $false
}
}
$lastSnapshotId=createShadow
deleteShadow -snapshotId $lastSnapshotId

Leave a Reply

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