VSS - Delete snapshots having the same date, but different timestamp

I am trying to write a PS script that would do the following:

Creates a persistent, client accessible, shadow copy of the volume
Parses the existing snapshots and deletes those having the same date, but different timestamp, older than yesterday, keeping only the latest of that date.
Deletes snapshots older than a month

The script will run as a scheduled task every hour since power on.

This is an effort to circumscribe the damage of a cryptoplocker attack.

I found some code to accomplish part of it (the first and the third), but i don’t know how to accomplish the second one. Follows the code I found googling:

#get static method
$shadow=[WMICLASS]“root\cimv2:win32_shadowcopy”

#create a new shadow copy
$shadow.create($Drive, “ClientAccessible”)

#PowerShell Script
#This script deletes all shadow copies older than 30 days
#By Wayne Johnson

Get-WmiObject Win32_Shadowcopy | ForEach-Object {
$WmiSnapShotDate = $.InstallDate
$strShadowID = $
.ID
$dtmSnapShotDate = [management.managementDateTimeConverter]::ToDateTime($WmiSnapShotDate)
$strClientAccessible = $.ClientAccessible
$dtmCurDate = Get-Date
$dtmTimeSpan = New-TimeSpan $dtmSnapShotDate $dtmCurDate
$intNumberDays = $dtmTimeSpan.Days
If ($intNumberDays -ge 31 -and $strClientAccessible -eq “True”) {
$
.Delete()
}
}

I am confused by your request. Do you need to keep the latest shadow copy for each day then, at a later date, delete latest copies that are a month old? So far, what I have below will group copies, select the groups older than yesterday, and select the latest copy of each group.

# Group shadowcopies by date
$groups = (Get-WmiObject  -Class win32_shadowcopy) | ForEach-Object {
$datetime = $_.convertToDateTime($_.installdate)
    [pscustomobject]@{
    pscomputername = $_.pscomputername
    deviceobject = $_.deviceobject
    id = $_.id
    providerid = $_.providerid
    installdate = $_.installdate
    datetime = $datetime
    date = $datetime.date
    ClientAccessible = $_.ClientAccessible
    }} | Group-Object -Property date

# Select shadow copy groups older than yesterday
$groups | Where-Object {$_.group.date -lt (Get-Date).AddDays(-1)}

# Select latest copy for each date
$groups | ForEach-Object {$_.group | Select-Object -Last 1}

My script is similar to random’s. I just add another loop so it will check the ID and delete those snapshots that no need anymore.

$delsnap=@()
Get-WmiObject -ComputerName sydittest -Class win32_shadowcopy | 
select deviceobject,ID,@{n='datetime';e={[management.managementDateTimeConverter]::ToDateTime($_.installdate)}},@{n='dayofyear';e={[management.managementDateTimeConverter]::ToDateTime($_.installdate).dayofyear}} | 
group dayofyear | Where-Object{$_.dayofyear -lt (get-date).DayOfYear} |
foreach {

if ($_.count -gt 1){

$max=$_.count-1

for($i=0;$i -lt $max;$i++ ){

$temp=[pscustomobject]@{id=$_.group[$i].id}
$delsnap+=$temp
}
}


}

$delsnap | foreach{

$id=$_.id.ToString()

Get-WmiObject -ComputerName sydittest -Class win32_shadowcopy | foreach {
if($_.id -eq $id){

$_.delete() 
}



}

}

Thank you both very much. in a few days i’ll test the final script and let know.
@random: Yes the idea is to keep all the snapshots of the last two days and the last snapshot of the day not more than a month. So in 24/7 system when the script starts it will take ≈ 24 snapshots a day for a total of ≈ 48 snapshots in two days. After that, on the third day of execution, it will delete all the snapshots that are more than 2 days older but the latest of that day. This way after 31 days of execution i’ll have 28 latest of the day snapshots plus ≈ 48 hourly snapshots of the last two days.

@random commandline
I tried the script. I have to find a manner to select not the last of the day but all the others but the last to delete them. I tried the -SkipLast 1 but it is not supported in PSH 4. I tried also the sort-object reverse and keep -Skip 1 but it keeps sorting from the farest to the latest.

@Yuan Li
I tried also your script, but it deletes all snapshots but the last of today also. I’ll try to find a manner to make it work.

@both of you
Thank you very much. At least i’m in the right way.

Hi,
this is the final script. Any suggestions to optimize it?

# Create shadowcopy of D:\ for Previous versions

$shadow = [WMICLASS]"root\cimv2:win32_shadowcopy"
$shadow.create("D:\", "ClientAccessible")

# Group shadowcopies by date sorted by time descending
$CurrDate = Get-Date
$groups = (Get-WmiObject  -Class win32_shadowcopy) | ForEach-Object {
	$datetime = $_.convertToDateTime($_.installdate)
    [pscustomobject]@{
		pscomputername = $_.pscomputername
		deviceobject = $_.deviceobject
		id = $_.id
		providerid = $_.providerid
		installdate = $_.installdate
		datetime = $datetime
		date = $datetime.ToShortDateString()
		time = $datetime.ToShortTimeString()
		timespan = (New-TimeSpan $datetime $CurrDate).Days
		ClientAccessible = $_.ClientAccessible
    }
} | Sort-Object -property @{
		Expression="time";Descending=$true
		} | Group-Object -Property date

# Select shadowcopy groups older than yesterday but the latest of the day

$oldshadow = $groups | Where-Object {
				$_.group.date -lt (Get-Date).AddDays(-2)
				} | ForEach-Object {
					$_.group | Select-Object -Skip 1
					}
					
# Select shadowcopy groups older than 31 days					
					
$oldestshadow = $groups | Where-Object {
				$_.group.timespan -ge 31
				} | ForEach-Object {
					$_.group | Select-Object
					}	

# Delete all shadowcopies older than yesterday but the latest of the day or older than a month
			
$shadow2 = Get-WmiObject Win32_ShadowCopy					
$oldshadow | ForEach-Object {
					$shadow2.Delete()
					}
$oldestshadow | ForEach-Object {
					$shadow2.Delete()
					}