PruneVSS.ps1 Remove Expired DPM Recovery Points.

By Steven Jordan on April 15th, 2014.

There are times when DPM fails to remove expired recovery points.  PruneVSS.ps1 can remove excessive disk (i.e., not tape) based recovery points.


#Author : Ruud Baars
#Date : 11/09/2008 #Edited : 11/15/2012 By: Wilson S.
#edited : 11:27:2012 By: Mike J.
#Edited : 3/25/2014 By: Steven J.
# $VerbosePreference = "Continue"
# NOTE: Update script to only remove recovery points on Disk. Recovery points removed will be from the oldest one up to the date
# entered by the user while the script is running
#deletes all recovery points before 'now' on selected data source. $version="V4.7"
$ErrorActionPreference = "silentlycontinue"
add-pssnapin sqlservercmdletsnapin100
Add-PSSnapin -Name Microsoft.DataProtectionManager.PowerShell
#display RP's to delete and ask to continue.
#Check & wait data source to be idle else removal may fail (in Mojito filter on 'intent' to see the error)
#Fixed prune default and logfile name and some logging lines (concatenate question + answer)
#Check dependent recovery points do not pass BEFORE date and adjust selection to not select those ($reselect)
#--- Fixed reselect logic to keep adjusting reselect for as long as older than BEFORE date
#--- Fixed post removal rechecking logic to match what is done so far (was still geared to old logic)
#--- Modified to remove making RP and ask for pruning, fixed logic for removal rechecking logic
$MB=1024*1024
$logfile="DPMdeleteRP.LOG"
$wait=10
#seconds $confirmpreference = "None"
function Show_help { cls $l="=" * 79 write-host $l -foregroundcolor magenta write-host -nonewline "`t<<<" -foregroundcolor white write-host -nonewline " DANGEROUS :: MAY DELETE MANY RECOVERY POINTS " -foregroundcolor red write-host ">>>" -foregroundcolor white write-host $l -foregroundcolor magenta write-host "Version: $version" -foregroundcolor cyan write-host "A: User Selects data source to remove recovery points for" -foregroundcolor green write-host "B: User enters date / time (using 24hr clock) to Delete recovery points" -foregroundcolor green write-host "C: User Confirms deletion after list of recovery points to be deleted is displayed." -foregroundcolor green write-host "Appending to log file $logfile`n" -foregroundcolor white write-host "User Accepts all responsibilities by entering a data source or just pressing [Enter] " -foregroundcolor white -backgroundcolor blue } "**********************************" >> $logfile "Version $version" >> $logfile get-date >> $logfile show_help $DPMservername=&"hostname" "Selected DPM server = $DPMservername" >> $logfile write-host "`nConnnecting to DPM server retrieving data source list...`n" -foregroundcolor green $pglist = @(Get-ProtectionGroup $DPMservername) # WILSON - Created PGlist as array in case we have a single protection group. $ds=@() $tapes=$null $count = 0 $dscount = 0 foreach ($count in 0..($pglist.count - 1)) { # write-host $pglist[$count].friendlyname $ds += @(get-datasource $pglist[$count]) # WILSON - Created DS as array in case we have a single protection group. # write-host $ds # write-host $count -foreground yellow } if ( Get-Datasource $DPMservername -inactive) {$ds += Get-Datasource $DPMservername -inactive} $i=0 write-host "Index Protection Group Computer Path" write-host "---------------------------------------------------------------------------------" foreach ($l in $ds) { "[{0,3}] {1,-20} {2,-20} {3}" -f $i, $l.ProtectionGroupName, $l.psinfo.netbiosname, $l.logicalpath $i++ } $DSname=read-host "`nEnter a data source index from list above - Note co-located datasources on same replica will be effected" if (!$DSname) { write-host "No datasource selected `n" -foregroundcolor yellow "Aborted on Datasource name" >> $logfile exit 0 } $DSselected=$ds[$DSname] if (!$DSselected) { write-host "No datasource selected `n" -foregroundcolor yellow "Aborted on Datasource name" >> $logfile exit 0 } $rp=get-recoverypoint $DS[$dsname] $rp # $DoTape=read-host "`nDo you want to remove when recovery points are on tape ? [y/N]" # "Remove tape recovery point = $DoTape" >> $logfile write-host "`nCollecting recoverypoint information for datasource $DSselected.name" -foregroundcolor green if ($DSselected.ShadowCopyUsedspace -gt 0) { while ($DSSelected.TotalRecoveryPoints -eq 0) { # "still 0" } #this is on disk $oldShadowUsage=[math]::round($DSselected.ShadowCopyUsedspace/$MB,1) $line=("Total recoverypoint usage {0} MB on DISK in {1} recovery points" -f $oldShadowUsage ,$DSselected.TotalRecoveryPoints ) $line >> $logfile write-host $line`n -foregroundcolor white } #this is on tape #$trptot=0 #$tp= Get-RecoveryPoint($dsselected) | where {($_.Datalocation -eq "Media")} #foreach ($trp in $tp) {$trptot += $trp.size } #if ($trptot -gt 0 ) #{ # $line=("Total recoverypoint usage {0} MB on TAPE in {1} recovery points" -f ($trptot/$MB) ,$DSselected.TotalRecoveryPoints ) # $line >> $logfile # write-host $line`n -foregroundcolor white #} [datetime]$afterdate="1/1/1980" #$answer=read-host "`nDo you want to delete recovery points from the beginning [Y/n]" #if ($answer -eq "n" ) #{ # [datetime]$afterdate=read-host "Delete recovery points AFTER date [MM/DD/YYYY hh:mm]" #} [datetime]$enddate=read-host "Delete ALL Disk based recovery points BEFORE and Including date/time entered [MM/DD/YYYY hh:mm]" "Deleting recovery points until $enddate" >>$logfile write-host "Deleting recovery points until and $enddate" -foregroundcolor yellow $rp=get-recoverypoint $DSselected if ($DoTape -ne "y" ) { $RPselected=$rp | where {($_.representedpointintime -le $enddate) -and ($_.Isincremental -eq $FALSE)-and ($_.DataLocation -eq "Disk")} } else { $RPselected=$rp | where {($_.representedpointintime -le $enddate) -and ($_.Isincremental -eq $FALSE)} } if (!$RPselected) { write-host "No recovery points found!" -foregroundcolor yellow "No recovery points found, aborting...!" >> $logfile exit 0 } $reselect = $enddate $adjustflag = $false foreach ($onerp in $RPselected) { $rtime=[string]$onerp.representedpointintime $rsize=[math]::round(($onerp.size/$MB),1) $line= "Found {0}, RP size= {1} MB (If 0 MB, co-located datasource cannot be computed), Incremental={2} "-f $rtime, $rsize,$onerp.Isincremental $line >> $logfile write-host "$line" -foregroundcolor yellow # #Get dependent rp's for data source # $allRPtbd=$DSselected.GetAllRecoveryPointsToBeDeleted($onerp) foreach ($oneDrp in $allRPtbd) { if ($oneDrp.IsIncremental -eq $FALSE) {continue} $rtime=[string]$oneDrp.representedpointintime $rsize=[math]::round(($oneDrp.size/$MB),1) $line= ("`t...is dependancy for {0} size {1} `tIncremental={2}" -f $rtime, $rsize, $oneDrp.Isincremental) $line >> $logfile if ($oneDrp.representedpointintime -ge $enddate) { #stick to latest full ($oneDrp = dependents, $onerp = full) $adjustflag = $true $reselect = $onerp.representedpointintime "<< Dependents newer than BEFORE date >>>" >> $logfile Write-Host -nonewline "`t <<< later than BEFORE date >>>" -foregroundcolor white -backgroundcolor red write-host "$line" -foregroundcolor yellow } else { #Ok, include current latest incremental $reselect = $oneDrp.representedpointintime write-host "$line" -foregroundcolor yellow } } } if ($reselect -lt $oneDrp.representedpointintime) { #we adjusted further backward than latest incremental within selection $reselect = $rtime $line = "Adjusted BEFORE date to be $reselect to include dependents to $enddate" $line >> $logfile Write-Host $line -foregroundcolor white -backgroundcolor blue } $line="`n<<< SECOND TO LAST CHANCE TO ABORT - ONE MORE PROMPT TO CONFIRM. >>>" write-host $line -foregroundcolor white -backgroundcolor blue $line >> $logfile $line="Above recovery points within adjusted range will be permanently deleted !!!" write-host $line -foregroundcolor red $line >> $logfile $line="These RP's include dependent recovery points and may contain co-located datasource(s)" write-host $line -foregroundcolor red $line >> $logfile $line="Data source activity = " + $DSselected.Activity $line >> $logfile write-host $line -foregroundcolor white $DoDelete="" while (($DoDelete -ne "N" ) -and ($DoDelete -ne "Y")) { $line="Continue with deletion (must answer) Y/N? " write-host $line -foregroundcolor white $DoDelete=read-host $line = $line + $DoDelete $line >> $logfile } if (!$DSselected.Activity -eq "Idle") { $line="Data source not idle, do you want to wait Y/N ? " write-host $line -foregroundcolor yellow $Y=read-host $line = $line + $Y $line >> $logfile if ($Y -ieq "Y") { Write-Host "Waiting for data source to become idle..." -foregroundcolor green while ($DSselected.Activity -ne "Idle") { ("Waiting {0} seconds" -f $wait) >>$logfile Write-Host -NoNewline "..." -ForegroundColor blue start-sleep -s $wait } } } if ($DoDelete -eq "Y") { foreach ($onerp in $RPselected) { #reselect is adjusted to safe range relative to what was requested #--- if adjustflag not set then all up to including else only older because we must keep the full if ((($onerp.representedpointintime -le $reselect) -and ($adjustflag -eq $false)) -or ($onerp.representedpointintime -lt $reselect)) { $rtime=[string]$onerp.representedpointintime write-host `n$line -foregroundcolor red $line >>$logfile if (($onerp ) -and ($onerp.IsIncremental -eq $FALSE)) { remove-recoverypoint -RecoveryPoint $onerp -recurse -force -confirm:$False} # >> $logfile} $line =("---`nDeleting recoverypoint -> " + $rtime) $line >>$logfile } } } "All Done!" >> $logfile write-host "`nAll Done!`n`n" -foregroundcolor white $line="Do you want to View DPMdeleteRP.LOG file Y/N ? " write-host $line -foregroundcolor white $Y=read-host $line = $line + $Y $line >> $logfile if ($Y -ieq "Y") { Notepad DPMdeleteRP.LOG}

2 Comments

  1. Hello Steven,
    I believe this script is what I'm looking for as I moved our Exchange databases to new underlying storage and didn't copy the data in the System Volume Information folder, therefore now I have stranded VSS snapshots in DPM that will not purge.

    I tried running the script and get the following error:

    PS C:\users\rparlee\desktop> .\prunevss.ps1
    Unexpected token 'Version $version' in expression or statement.
    At C:\users\rparlee\desktop\prunevss.ps1:23 char:958
    + function Show_help { cls $l="=" * 79 write-host $l -foregroundcolor magenta w
    rite-host -nonewline "`t<<<" -foregroundcolor white write-host -nonewline " DAN
    GEROUS :: MAY DELETE MANY RECOVERY POINTS " -foregroundcolor red write-host ">>
    >" -foregroundcolor white write-host $l -foregroundcolor magenta write-host "Ve
    rsion: $version" -foregroundcolor cyan write-host "A: User Selects data source
    to remove recovery points for" -foregroundcolor green write-host "B: User enter


    and it goes on to list out the entire rest of the script.

    I copied the code from your post, pasted into notepad and saved is a prunevss.ps1 and executed it.

    I'm running DPM 2010 with all the latest updates/rollups.

    Thank you,
    Ryan

    ReplyDelete
  2. I think copying and pasting loses a lot of the original line breaks - any chance of the original file?

    ReplyDelete

My Instagram