{"id":515,"date":"2020-10-17T19:29:50","date_gmt":"2020-10-17T18:29:50","guid":{"rendered":"http:\/\/192.168.8.14\/?p=515"},"modified":"2020-10-17T20:17:37","modified_gmt":"2020-10-17T19:17:37","slug":"snapshot-deletion-script","status":"publish","type":"post","link":"https:\/\/www.jasonstreet.com\/?p=515","title":{"rendered":"Snapshot Deletion script"},"content":{"rendered":"\n<p>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.<\/p>\n\n\n\n<p>If the snapshot is named &#8220;my snapshot _ DeleteAfter60days&#8221; the script will look for &#8220;DeleteAfter&#8221; or &#8220;DA&#8221; after the last &#8220;_&#8221;. If it finds it it will remove that string (and &#8220;days&#8221;) 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.<\/p>\n\n\n\n<p>This is best expianed by example.<\/p>\n\n\n\n<p>Snapshot name                            Action<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>snapshot                        Deleted after the defualt days<\/li><li>snap_DeleteAfter10     Deleted after 10 days<\/li><li>snap_da20                     Deleted after 20 days<\/li><li>snap_da50days             Deleted after 50 days<\/li><li>snap _da01\/01\/2020     Deleted after the 01\/01\/2020<\/li><li>snap_DoNotDelete       Is ignored by the script<\/li><li>snap_da01012021        Deleted afterthe 01\/01\/2021<\/li><li>snap_da101220            Deleted afterthe 10\/12\/2020<\/li><\/ul>\n\n\n\n<p>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.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: powershell; title: ; notranslate\" title=\"\">\n# Semi smart snapshot deleter script\n# Jason Street\n# 10\/2020\n#\n# script will delete any snapshot over $DeleteAfterDays old. \n# unles the end of the snap name is _DAx or _DeleteAfter x (x = date, in format ddmmyyyy or dd\/mm\/yyyy) or \n#                                or _DAn (n = number of days sinse creation)\n#                                or _$IgnoreString (will be ignored by the sctipt)\n\n$vCenter = &quot;vcenter001.jase.local&quot;\n$DeleteAfterDays = 60\n$IgnoreString = &quot;DoNotDelete&quot;\n$LogFile = &quot;.\\SnapDel.log&quot;\n$DebugFile = &quot;.\\SnapDel_debug.log&quot;\n\n\n### start of functions\n\nfunction Send-DebugLog \n{\n    &#x5B;CmdletBinding()]\n    param\n    (  \n        &#x5B;Parameter(Mandatory = $false,ValueFromPipeline=$true)] $Message,\n        &#x5B;Parameter(Mandatory = $false)] $Result,                  # result of last command ($?)\n        &#x5B;Parameter(Mandatory = $false)] &#x5B;string]$LogFilePath = $null,   # log file location\n        &#x5B;Parameter(Mandatory = $false)] &#x5B;string]$DebugFilePath = $null, # debug file location\n        &#x5B;Parameter(Mandatory = $false)] &#x5B;switch]$ScreenPrint = $false,  # print message to the screen\n        &#x5B;Parameter(Mandatory = $false)] &#x5B;switch]$LogIt = $false,        # send message to log file if set\n        &#x5B;Parameter(Mandatory = $false)] &#x5B;switch]$ClearLog = $false,     # clears the log file\n        &#x5B;Parameter(Mandatory = $false)] &#x5B;switch]$ClearDebug = $false    # clears the debug log file\n    )\n\n    #if ($DebugFilePath -eq $null){$DebugFilePath = $DebugFile}\n    #if ($LogFilePath -eq $null){$LogFilePath = $LogFile}\n\n    if ($ClearLog -eq $true){&quot;Script run at &quot; + ((get-date).toString()) | Out-File $LogFile}\n    if ($ClearDebug -eq $true){&quot;Script run at &quot; + ((get-date).toString()) | Out-File $DebugFile}\n\n    if ($Message -ne $Null)\n    {\n        if ($Result -eq &#039;&#039; -or $Result -eq $null){$OutMessage = $Message}\n        if ($Result -eq $true){$OutMessage = &quot;OK    &quot; + $Message}\n        if ($Result -eq $false){$OutMessage = &quot;Fail  &quot; + $Message}\n\n        $OutMessage | out-file $DebugFile -Append\n        if ($LogIt -eq $true){$OutMessage | out-file $LogFile -Append}\n        if ($ScreenPrint -eq $true)\n        {\n            if ($Result -eq $Null){write-host $OutMessage -ForegroundColor Gray}\n            if ($Result -eq $true){write-host $OutMessage -ForegroundColor Green}\n            if ($Result -eq $false){write-host $OutMessage -ForegroundColor red}\n        }\n    }\n}\n\nfunction isNumeric ($x) {\n    try {\n        0 + $x | Out-Null\n        return $true\n    } catch {\n        return $false\n    }\n}\n\n&lt;#\n  .SYNOPSIS\n     To look for a date string in a text string\n\n  .DESCRIPTION\n     will look for the following at the end of a text string _DAddmmyy, _DeleteAfterxDays, _DA dmyy\n     and return the a data object (if a date is found\n       or a int of the number of days\n       or a $null if not found\n\n  .PARAMETER NameString\n    The test string to be searched\n\n  .PARAMETER Dilimitor \n    the Dilimitor between the user test and the date text\n#&gt;\nfunction Get-DateFromName \n{\n    &#x5B;CmdletBinding()]\n    param\n    (  \n        &#x5B;Parameter(Mandatory = $true)] &#x5B;string]$NameString,\n        &#x5B;Parameter(Mandatory = $false)] &#x5B;string]$Dilimitor = &quot;_&quot;,\n        &#x5B;Parameter(Mandatory = $false)] &#x5B;string]$IgnoreString = $null\n    )\n\n    &quot;Entering function Get-DatefromName&quot; | Send-DebugLog\n    &quot;   with $NameString&quot; | Send-DebugLog \n\n    # get last &quot;word&quot; and remove spaces\n    $LWord = ($NameString.split($Dilimitor))&#x5B;($NameString.split($Dilimitor)).count - 1]\n    $LWord = $LWord -replace &#039; &#039;,&#039;&#039; \n\n    # check the last word has the keep override in\n    if ($IgnoreString -ne $null)\n    {\n        if ($LWord -eq $IgnoreString)\n        {\n            return &quot;Ignore&quot;\n        }\n    }\n\n    # check the last word has the delete override in\n    if (($LWord -like &quot;da*&quot;) -or ($Lword -like &quot;deleteafter*&quot;))\n    {\n\n        # remove the known text as it is not needed.\n        $LWord = $LWord -replace &quot;deleteafter&quot;,&quot;da&quot;\n        $LWord = $LWord -replace &quot;days&quot;,&#039;&#039;\n        $LWord = $LWord -replace &quot;da&quot;,&#039;&#039;\n        $LWord = $LWord -replace &quot;%2f&quot;,&quot;.&quot;\n        $LWord = $LWord -replace &quot;%5c&quot;,&quot;.&quot;\n        $LWord = $LWord -replace &quot;%252f&quot;,&quot;.&quot;\n        $LWord = $LWord -replace &quot; &quot;,&#039;&#039;\n        &quot;   LWord = $LWord&quot; | Send-DebugLog\n\n\n\n        # test if all we have in numbers left\n        if (isNumeric $LWord)\n        {\n            # is most likely a day counter but could be a date with no delims\n            if ($LWord.length -lt 6)\n            {\n                # number less then 6 didgits, assuming its just a day\n                #&quot;Assuming is a day&quot;\n                &quot;   returning int = $LWord&quot;| Send-DebugLog\n                return &#x5B;int32]$LWord\n            }else{\n                # nummer 6 more more didgits, assuming its a date with the format DDMMYY\n                if ($LWord.length -eq 6)\n                {\n                    # assuming date is in format YYMMDD\n                    &quot;   returning date = &quot; + (get-date -Year $LWord.Substring(4,2) -Month $LWord.Substring(2,2) -day $Lword.Substring(0,2)) | Send-DebugLog\n                    return get-date -Year $LWord.Substring(4,2) -Month $LWord.Substring(2,2) -day $Lword.Substring(0,2)\n                }else{\n                    if ($LWord.length -eq 8)\n                    {\n                        # assuming date is in format DDMMYYYY\n                        &quot;   returning date = &quot; + (get-date -Year $LWord.Substring(8,4) -Month $LWord.Substring(4,2) -day $Lword.Substring(0,2)) | Send-DebugLog\n                        return get-date -Year $LWord.Substring(8,4) -Month $LWord.Substring(4,2) -day $Lword.Substring(0,2)\n                    }else{\n                        # No idea what the number is\n                        &quot;   returning null&quot;| Send-DebugLog\n                        return $null\n                    }\n                }\n            }\n        }else{\n            # could be a date\n            if (!(isNumeric $LWord.Substring(2,1)) )\n            {\n                # 3rd charrictor is not a number, could be a delimitor\n                # remove all occerances of the 3rd charrictor and see if we get a number.\n                if (isNumeric $LWord.replace($LWord.Substring(2,1),&#039;&#039;))\n                {\n                    # date most likely to be DDxMMxYYYY\n                    $DateBits = $LWord.split($LWord.Substring(2,1))\n\n                    # check the yaer is 2 digits, if so add 2000 to it\n                    if ($DateBits&#x5B;2].tostring().Length -eq 2){$DateBits&#x5B;2] = &#x5B;int]$DateBits&#x5B;2] + 2000}\n                    &quot;   returning date = &quot; + (get-date -Year $DateBits&#x5B;2] -Month $DateBits&#x5B;1] -day $DateBits&#x5B;0]) | Send-DebugLog\n                    return get-date -Year $DateBits&#x5B;2] -Month $DateBits&#x5B;1] -day $DateBits&#x5B;0]\n                }else{\n                    # date is not a number with out the dilims removed. unable to process.\n                    &quot;   returning null&quot;| Send-DebugLog\n                    return $null\n                }\n            }else{\n            \n                if (!(isNumeric $LWord.Substring(1,1)) )\n                {\n                    # 2nd charrictor is not a number, could be a delimitor\n                    # check $LWord is a number after removing the 2nd charrictor (in all locations as there will be 2 dilims)\n                    if (isNumeric $LWord.replace($LWord.Substring(1,1),&#039;&#039;))\n                    {\n                        # date is most likely in the format DxMMxYYYY\n                        $DateBits = $LWord.split($LWord.Substring(1,1))\n                        # check if the yaer is 2 digits, if so add 2000 to it\n                        if ($DateBits&#x5B;2].tostring().Length -eq 2){$DateBits&#x5B;2] = &#x5B;int]$DateBits&#x5B;2] + 2000}\n                        &quot;   returning date = &quot; + (get-date -Year $DateBits&#x5B;2] -Month $DateBits&#x5B;1] -Day $DateBits&#x5B;0]) | Send-DebugLog\n                        return get-date -Year $DateBits&#x5B;2] -Month $DateBits&#x5B;1] -Day $DateBits&#x5B;0]\n                    }else{\n                        # date is not a number with out the dilims removed. unable to process.\n                        &quot;   returning null&quot;| Send-DebugLog\n                        return $null\n                    }\n                }else{\n                    &quot;   returning null&quot;| Send-DebugLog\n                    return $null\n                }\n            }\n        }\n    }else{\n        # Override keywords not found\n        &quot;   returning null, override keywords not found&quot;| Send-DebugLog\n        return $null\n    }\n}\n\n### end of functions\n\n\n\nSend-DebugLog -LogIt -ClearLog -ClearDebug\n\nconnect-viserver vCenter001.jase.local\n\n$Snaps = get-vm | get-snapshot \n\nforeach ($Snap in $Snaps)\n{\n    # Loop throug each snapshot\n    # read the snapshot name and work out if there is delete override\n    $DelInfo = Get-DateFromName ($Snap.name)  &quot;_&quot; -IgnoreString $IgnoreString\n    if ($DelInfo)\n    {\n        # function returned a date\n        if ($DelInfo.gettype().name -eq &quot;DateTime&quot;)\n        {\n            # snap has a delete after date\n            if ($DelInfo -lt (get-date))\n            {\n                # snap has a delete date is is now old enougth to be deleted\n                (&quot;VM &quot; + $Snap.vm + &quot; with snapshot &quot; + $Snap.name + &quot; will be deleted !! &quot; +  $DelInfo.tostring() + &quot; in &quot; + &#x5B;int](($DelInfo - (get-date)).TotalDays) + &quot; days&quot;) | Send-DebugLog -LogIt -ScreenPrint\n                Remove-Snapshot $Snap -WhatIf\n                &quot;Deleting Snapshot on VM &quot; + $Snap.vm + &quot; called &quot; + $Snap.name | Send-DebugLog -Result $? -LogIt -ScreenPrint\n            }else{\n                # snap has a delete date is is not old enougth yet to be deleteded\n                (&quot;VM &quot; + $Snap.vm + &quot; with snapshot &quot; + $Snap.name + &quot; will be deleted after &quot; +  $DelInfo.tostring() + &quot; in &quot; + &#x5B;int](($DelInfo - (get-date)).TotalDays) + &quot; days&quot;) | Send-DebugLog -LogIt -ScreenPrint \n            }\n        }\n\n        # function returned a number \n        if ($DelInfo.gettype().name -eq &quot;Int32&quot;)\n        {\n            # snap has a number of days to keep\n            if ($Snap.created.adddays($DelInfo) -lt (get-date))\n            {\n                # snap has reached its expirary in days and will be deleted.\n                (&quot;VM &quot; + $Snap.vm + &quot; with snapshot &quot; + $Snap.name + &quot; will be deleted as its  &quot; +  $DelInfo.tostring() + &quot; days old &quot; ) | Send-DebugLog -LogIt -ScreenPrint\n                Remove-Snapshot $Snap -WhatIf\n                &quot;Deleting Snapshot on VM &quot; + $Snap.vm + &quot; called &quot; + $Snap.name | Send-DebugLog -Result $? -LogIt -ScreenPrint\n            }else{\n                # snap has not reached it &quot;expirary&quot; het so will not be deleted (yet).\n                (&quot;VM &quot; + $Snap.vm + &quot; with snapshot &quot; + $Snap.name + &quot; will be deleted in  &quot; +  &#x5B;int](($Snap.created.adddays($DelInfo) - (get-date)).totaldays) + &quot; days &quot; ) | Send-DebugLog -LogIt -ScreenPrint\n            }\n        }\n        \n        # function returned the string &quot;ignore&quot;        \n        if ($DelInfo -eq &quot;ignore&quot;)\n        {\n            # the function fond the ignor sting. This script will not delete this snapshot.\n            (&quot;VM &quot; + $Snap.vm + &quot; with snapshot &quot; + $Snap.name + &quot; has been set to ignore &quot;) | Send-DebugLog -LogIt -ScreenPrint\n        }\n\n      \n    }else{\n        # function returned a null\n        if ($Snap.created.adddays($DeleteAfterDays) -lt (get-date))\n        {\n            (&quot;VM &quot; + $Snap.vm + &quot; with snapshot &quot; + $Snap.name + &quot; unknown, but its older then $DeleteAfterDays days&quot;) | Send-DebugLog -LogIt -ScreenPrint\n            Remove-Snapshot $Snap -WhatIf\n            &quot;Deleting Snapshot on VM &quot; + $Snap.vm + &quot; called &quot; + $Snap.name | Send-DebugLog -Result $? -LogIt -ScreenPrint\n        }else{\n            (&quot;VM &quot; + $Snap.vm + &quot; with snapshot &quot; + $Snap.name + &quot; unknown, but its not going to get deleted for &quot; + &#x5B;int](($Snap.created.adddays($DeleteAfterDays) - (get-date)).TotalDays) + &quot; days&quot;) | Send-DebugLog -LogIt -ScreenPrint\n        }\n    }\n}\n\n\n\n<\/pre><\/div>\n\n\n<p>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. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 &#8220;my snapshot _ DeleteAfter60days&#8221; the script will look for &#8220;DeleteAfter&#8221; or &#8220;DA&#8221;&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[5,43],"tags":[17,12,28],"class_list":["post-515","post","type-post","status-publish","format-standard","hentry","category-powercli","category-vmware","tag-function","tag-powercli","tag-snapshots"],"_links":{"self":[{"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=\/wp\/v2\/posts\/515","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=515"}],"version-history":[{"count":6,"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=\/wp\/v2\/posts\/515\/revisions"}],"predecessor-version":[{"id":693,"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=\/wp\/v2\/posts\/515\/revisions\/693"}],"wp:attachment":[{"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=515"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=515"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=515"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}