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.
$targetVolume="E:\"
$vssAccessLink="C:\vssSnapshot"
function createVssSnapshot{
[cmdletbinding()]
param(
[string]$targetVolume="C:\",
$vssAccessLink="C:\shadowcopy"
)
# Sanitation
if (!($targetVolume -like "*\")){$targetVolume+="\"}
if(Test-Path $vssAccessLink){(Get-Item $vssAccessLink).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
$null=cd C:
$null=cmd /c mklink /d $vssAccessLink $thisShadowPath
write-host "Vss Snapshot of $targetVolume has been made and it's accessible at this local file system (LFS): $vssAccessLink."
# Validation
if(Test-Path $vssAccessLink){
$snapshotId=$thisShadow.ID;
write-host "Snapshot $snapshotId has been created.";
return $snapshotId;
}else{
write-host "Failed to create client accessible VSS Snapshot.";
return $false;
}
}
createVssSnapshot $targetVolume $vssAccessLink
function deleteVssSnapshot{
[cmdletbinding()]
param(
[string]$targetVolume="C:\",
[string]$snapShotId,
$vssAccessLink
)
# 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 $vssAccessLink..."
(Get-Item $vssAccessLink).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=createVssSnapshot $targetVolume $vssAccessLink
deleteVssSnapshot $targetVolume $lastSnapshotId $vssAccessLink
Categories: