This is a script I wrote to delete snapshots over a certian age wth an override. The override is in the name of the snapshot, so the snapshot can be deleted after x days or after a date.
If the snapshot is named “my snapshot _ DeleteAfter60days” the script will look for “DeleteAfter” or “DA” after the last “_”. If it finds it it will remove that string (and “days”) if the resulting string is numeric and less then 6 didgits it will assume the number is days to keep. If on the other hand the number is 6 or more didgits it will assume its a date.
This is best expianed by example.
Snapshot name Action
- snapshot Deleted after the defualt days
- snap_DeleteAfter10 Deleted after 10 days
- snap_da20 Deleted after 20 days
- snap_da50days Deleted after 50 days
- snap _da01/01/2020 Deleted after the 01/01/2020
- snap_DoNotDelete Is ignored by the script
- snap_da01012021 Deleted afterthe 01/01/2021
- snap_da101220 Deleted afterthe 10/12/2020
So here is the script. I have left the -whatif attribute on the delete snapshot commands so running this script should not do any harm.
# Semi smart snapshot deleter script
# Jason Street
# 10/2020
#
# script will delete any snapshot over $DeleteAfterDays old.
# unles the end of the snap name is _DAx or _DeleteAfter x (x = date, in format ddmmyyyy or dd/mm/yyyy) or
# or _DAn (n = number of days sinse creation)
# or _$IgnoreString (will be ignored by the sctipt)
$vCenter = "vcenter001.jase.local"
$DeleteAfterDays = 60
$IgnoreString = "DoNotDelete"
$LogFile = ".\SnapDel.log"
$DebugFile = ".\SnapDel_debug.log"
### start of functions
function Send-DebugLog
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $false,ValueFromPipeline=$true)] $Message,
[Parameter(Mandatory = $false)] $Result, # result of last command ($?)
[Parameter(Mandatory = $false)] [string]$LogFilePath = $null, # log file location
[Parameter(Mandatory = $false)] [string]$DebugFilePath = $null, # debug file location
[Parameter(Mandatory = $false)] [switch]$ScreenPrint = $false, # print message to the screen
[Parameter(Mandatory = $false)] [switch]$LogIt = $false, # send message to log file if set
[Parameter(Mandatory = $false)] [switch]$ClearLog = $false, # clears the log file
[Parameter(Mandatory = $false)] [switch]$ClearDebug = $false # clears the debug log file
)
#if ($DebugFilePath -eq $null){$DebugFilePath = $DebugFile}
#if ($LogFilePath -eq $null){$LogFilePath = $LogFile}
if ($ClearLog -eq $true){"Script run at " + ((get-date).toString()) | Out-File $LogFile}
if ($ClearDebug -eq $true){"Script run at " + ((get-date).toString()) | Out-File $DebugFile}
if ($Message -ne $Null)
{
if ($Result -eq '' -or $Result -eq $null){$OutMessage = $Message}
if ($Result -eq $true){$OutMessage = "OK " + $Message}
if ($Result -eq $false){$OutMessage = "Fail " + $Message}
$OutMessage | out-file $DebugFile -Append
if ($LogIt -eq $true){$OutMessage | out-file $LogFile -Append}
if ($ScreenPrint -eq $true)
{
if ($Result -eq $Null){write-host $OutMessage -ForegroundColor Gray}
if ($Result -eq $true){write-host $OutMessage -ForegroundColor Green}
if ($Result -eq $false){write-host $OutMessage -ForegroundColor red}
}
}
}
function isNumeric ($x) {
try {
0 + $x | Out-Null
return $true
} catch {
return $false
}
}
<#
.SYNOPSIS
To look for a date string in a text string
.DESCRIPTION
will look for the following at the end of a text string _DAddmmyy, _DeleteAfterxDays, _DA dmyy
and return the a data object (if a date is found
or a int of the number of days
or a $null if not found
.PARAMETER NameString
The test string to be searched
.PARAMETER Dilimitor
the Dilimitor between the user test and the date text
#>
function Get-DateFromName
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)] [string]$NameString,
[Parameter(Mandatory = $false)] [string]$Dilimitor = "_",
[Parameter(Mandatory = $false)] [string]$IgnoreString = $null
)
"Entering function Get-DatefromName" | Send-DebugLog
" with $NameString" | Send-DebugLog
# get last "word" and remove spaces
$LWord = ($NameString.split($Dilimitor))[($NameString.split($Dilimitor)).count - 1]
$LWord = $LWord -replace ' ',''
# check the last word has the keep override in
if ($IgnoreString -ne $null)
{
if ($LWord -eq $IgnoreString)
{
return "Ignore"
}
}
# check the last word has the delete override in
if (($LWord -like "da*") -or ($Lword -like "deleteafter*"))
{
# remove the known text as it is not needed.
$LWord = $LWord -replace "deleteafter","da"
$LWord = $LWord -replace "days",''
$LWord = $LWord -replace "da",''
$LWord = $LWord -replace "%2f","."
$LWord = $LWord -replace "%5c","."
$LWord = $LWord -replace "%252f","."
$LWord = $LWord -replace " ",''
" LWord = $LWord" | Send-DebugLog
# test if all we have in numbers left
if (isNumeric $LWord)
{
# is most likely a day counter but could be a date with no delims
if ($LWord.length -lt 6)
{
# number less then 6 didgits, assuming its just a day
#"Assuming is a day"
" returning int = $LWord"| Send-DebugLog
return [int32]$LWord
}else{
# nummer 6 more more didgits, assuming its a date with the format DDMMYY
if ($LWord.length -eq 6)
{
# assuming date is in format YYMMDD
" returning date = " + (get-date -Year $LWord.Substring(4,2) -Month $LWord.Substring(2,2) -day $Lword.Substring(0,2)) | Send-DebugLog
return get-date -Year $LWord.Substring(4,2) -Month $LWord.Substring(2,2) -day $Lword.Substring(0,2)
}else{
if ($LWord.length -eq 8)
{
# assuming date is in format DDMMYYYY
" returning date = " + (get-date -Year $LWord.Substring(8,4) -Month $LWord.Substring(4,2) -day $Lword.Substring(0,2)) | Send-DebugLog
return get-date -Year $LWord.Substring(8,4) -Month $LWord.Substring(4,2) -day $Lword.Substring(0,2)
}else{
# No idea what the number is
" returning null"| Send-DebugLog
return $null
}
}
}
}else{
# could be a date
if (!(isNumeric $LWord.Substring(2,1)) )
{
# 3rd charrictor is not a number, could be a delimitor
# remove all occerances of the 3rd charrictor and see if we get a number.
if (isNumeric $LWord.replace($LWord.Substring(2,1),''))
{
# date most likely to be DDxMMxYYYY
$DateBits = $LWord.split($LWord.Substring(2,1))
# check the yaer is 2 digits, if so add 2000 to it
if ($DateBits[2].tostring().Length -eq 2){$DateBits[2] = [int]$DateBits[2] + 2000}
" returning date = " + (get-date -Year $DateBits[2] -Month $DateBits[1] -day $DateBits[0]) | Send-DebugLog
return get-date -Year $DateBits[2] -Month $DateBits[1] -day $DateBits[0]
}else{
# date is not a number with out the dilims removed. unable to process.
" returning null"| Send-DebugLog
return $null
}
}else{
if (!(isNumeric $LWord.Substring(1,1)) )
{
# 2nd charrictor is not a number, could be a delimitor
# check $LWord is a number after removing the 2nd charrictor (in all locations as there will be 2 dilims)
if (isNumeric $LWord.replace($LWord.Substring(1,1),''))
{
# date is most likely in the format DxMMxYYYY
$DateBits = $LWord.split($LWord.Substring(1,1))
# check if the yaer is 2 digits, if so add 2000 to it
if ($DateBits[2].tostring().Length -eq 2){$DateBits[2] = [int]$DateBits[2] + 2000}
" returning date = " + (get-date -Year $DateBits[2] -Month $DateBits[1] -Day $DateBits[0]) | Send-DebugLog
return get-date -Year $DateBits[2] -Month $DateBits[1] -Day $DateBits[0]
}else{
# date is not a number with out the dilims removed. unable to process.
" returning null"| Send-DebugLog
return $null
}
}else{
" returning null"| Send-DebugLog
return $null
}
}
}
}else{
# Override keywords not found
" returning null, override keywords not found"| Send-DebugLog
return $null
}
}
### end of functions
Send-DebugLog -LogIt -ClearLog -ClearDebug
connect-viserver vCenter001.jase.local
$Snaps = get-vm | get-snapshot
foreach ($Snap in $Snaps)
{
# Loop throug each snapshot
# read the snapshot name and work out if there is delete override
$DelInfo = Get-DateFromName ($Snap.name) "_" -IgnoreString $IgnoreString
if ($DelInfo)
{
# function returned a date
if ($DelInfo.gettype().name -eq "DateTime")
{
# snap has a delete after date
if ($DelInfo -lt (get-date))
{
# snap has a delete date is is now old enougth to be deleted
("VM " + $Snap.vm + " with snapshot " + $Snap.name + " will be deleted !! " + $DelInfo.tostring() + " in " + [int](($DelInfo - (get-date)).TotalDays) + " days") | Send-DebugLog -LogIt -ScreenPrint
Remove-Snapshot $Snap -WhatIf
"Deleting Snapshot on VM " + $Snap.vm + " called " + $Snap.name | Send-DebugLog -Result $? -LogIt -ScreenPrint
}else{
# snap has a delete date is is not old enougth yet to be deleteded
("VM " + $Snap.vm + " with snapshot " + $Snap.name + " will be deleted after " + $DelInfo.tostring() + " in " + [int](($DelInfo - (get-date)).TotalDays) + " days") | Send-DebugLog -LogIt -ScreenPrint
}
}
# function returned a number
if ($DelInfo.gettype().name -eq "Int32")
{
# snap has a number of days to keep
if ($Snap.created.adddays($DelInfo) -lt (get-date))
{
# snap has reached its expirary in days and will be deleted.
("VM " + $Snap.vm + " with snapshot " + $Snap.name + " will be deleted as its " + $DelInfo.tostring() + " days old " ) | Send-DebugLog -LogIt -ScreenPrint
Remove-Snapshot $Snap -WhatIf
"Deleting Snapshot on VM " + $Snap.vm + " called " + $Snap.name | Send-DebugLog -Result $? -LogIt -ScreenPrint
}else{
# snap has not reached it "expirary" het so will not be deleted (yet).
("VM " + $Snap.vm + " with snapshot " + $Snap.name + " will be deleted in " + [int](($Snap.created.adddays($DelInfo) - (get-date)).totaldays) + " days " ) | Send-DebugLog -LogIt -ScreenPrint
}
}
# function returned the string "ignore"
if ($DelInfo -eq "ignore")
{
# the function fond the ignor sting. This script will not delete this snapshot.
("VM " + $Snap.vm + " with snapshot " + $Snap.name + " has been set to ignore ") | Send-DebugLog -LogIt -ScreenPrint
}
}else{
# function returned a null
if ($Snap.created.adddays($DeleteAfterDays) -lt (get-date))
{
("VM " + $Snap.vm + " with snapshot " + $Snap.name + " unknown, but its older then $DeleteAfterDays days") | Send-DebugLog -LogIt -ScreenPrint
Remove-Snapshot $Snap -WhatIf
"Deleting Snapshot on VM " + $Snap.vm + " called " + $Snap.name | Send-DebugLog -Result $? -LogIt -ScreenPrint
}else{
("VM " + $Snap.vm + " with snapshot " + $Snap.name + " unknown, but its not going to get deleted for " + [int](($Snap.created.adddays($DeleteAfterDays) - (get-date)).TotalDays) + " days") | Send-DebugLog -LogIt -ScreenPrint
}
}
}
One thing to note is that the log will be over-written every time the script runs. So currently there will no perminant log of what snapshots where deleted.