I have this snippet embedded in various programs. Perhaps, it’s useful to be posted as an independent script to be refactored into other codes.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
# createVssSnapshot.ps1
# Version 0.02
 
$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
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# createVssSnapshot.ps1
# version 0.0.1
 
function createVssSnapshot{
    [cmdletbinding()]
    param(
        [string]$targetVolume="C:\",
        [string]$accessPath='C:\vssSnapshot',
        [bool]$openSnapshotAfter=$true
    )
    # Fix targetVolume input if it's missing the suffix
    if (!($targetVolume -like "*\")){$targetVolume+="\"}   
    $volumeLetter=$targetVolume[0]
    $driveLabel=(get-volume -DriveLetter $volumeLetter).FileSystemLabel
    $volumelabel="Volume_$volumeLetter$(if($driveLabel){'_'+$driveLabel})"
    $localSnapshotPath="$accessPath\$env:computername\$volumeLabel\$(Get-Date -Format 'yyyy-MM-dd_hh.mm.ss')" 
      
    # Create the 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 + "\";       
 
    # Create snapshot parent directory
    $null=New-Item -ItemType Directory -Force -Path $(Split-Path $localSnapshotPath -Parent);
 
    # Make links to this snapshot
    $null=cmd /c mklink /J $localSnapshotPath $thisShadowPath;   
      
    if(test-path $localSnapshotPath){
        write-host "Snapshot of $targetVolume has been made and it's accessible at this path: $localSnapshotPath"
        if($openSnapshotAfter){invoke-item $localSnapshotPath}
        return $true
    }else{
        write-warning "Something went wrong. Snapshot was not taken."
        return $false
        }
}
  
createVssSnapshot

Removing VSS Snapshots via PowerShell is classified as high-risk by most antivirus software – read my Active Directory PEN testing articles for more background on such logic. This is an example of an interception by “rule heuristic.b.14” by this flavor of anti-malware. Hence, the below script will not run on such hosts.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
function deleteVssSnapshot{
    [cmdletbinding()]
    param(
        [string]$targetVolume="C:\",
        [string]$snapShotId,
        [bool]$removeAllSnapshots=$false
    )
    $ErrorActionPreference='stop'
    if(!(Test-WSMan)){Enable-PSRemoting –force}
  
    function removeOneSnapshot($snapShotId){
        # Remove a single snapshot is a function by itself because Antivirus will terminate any session that tries to remove a VSS snapshot
        write-host "Removing snapshot Id $snapShotId... Ctrl+C to Cancel"
        pause
 
        # This is the workaround to the annoyance of antivirus software terminating sessions upon invoking the snapshot removal procedure
        $session=New-PSSession
        Invoke-Command -Session $session -AsJob -ScriptBlock{param($snapShotId);$null=vssadmin delete shadows /Shadow=$snapShotId /quiet} -args $snapShotId|Receive-Job -Wait
        if($session.state -ne 'Opened'){                   
            Write-Warning "An error (probably antivirus heuristics detection) has prevented removal of snapshot ID $snapShotId"
            Remove-PSSession $session
            return $false
        }else{
            Remove-PSSession $session
            return $true
            }
        }
 
    if($removeAllSnapshots){
        try{
            #vssadmin delete shadows /For=$targetVolume /Quiet
            $volumeId=(get-volume|?{$_.DriveLetter -eq $targetVolume[0]}).UniqueId
            $targetSnapshotIds=Get-WmiObject Win32_ShadowCopy|?{$_.VolumeName -eq $volumeId}|%{[void]($_.__PATH -match '{(.*)}');($matches[1]).tolower()}
            Write-host "These snapshots IDs are detected on $targetVolume`r`n$(($targetSnapshotIds|out-string).trim())`r`n------------------------------`r`n"
            foreach ($targetSnapshotId in $targetSnapshotIds){
                $snapShotId="{$targetSnapshotId`}"
                $success=removeOneSnapshot $snapShotId
                if(!$success){
                    $remainingSnapshotIds=Get-WmiObject Win32_ShadowCopy|?{$_.VolumeName -eq $volumeId}|%{[void]($_.__PATH -match '{(.*)}');($matches[1]).tolower()}
                    Write-Warning "Program cannot proceed to remove these snapshots:`r`n$(($remainingSnapshotIds|out-string).trim())"
                    return $false
                    }
            }           
            $checkSnapshotIds=Get-WmiObject Win32_ShadowCopy|?{$_.VolumeName -eq $volumeId}|%{[void]($_.__PATH -match '{(.*)}');($matches[1]).tolower()}
            if($checkSnapshotIds){
                write-warning "These snapshot IDs still persists: $checkSnapshotIds"
                return $false
            }else{
                write-host "All snapshots of volume $targetVolume have been purged."
                return $true
                }
        }catch{
            return $false
            }
    }else{  
        # LIFO: select the last 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`}"
            }
        removeOneSnapshot $snapShotId
 
        # 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
                }
    }
}
    
deleteVssSnapshot