Public/Remove-DailyBackup.ps1
function Remove-DailyBackup { <# .SYNOPSIS Removes daily backup directories and files based on specified criteria. .DESCRIPTION Removes daily backup directories and files from a backup location. Supports multiple removal modes including date-based cleanup, retention policies, and specific backup removal. The function can clean up old backups while preserving recent ones, or remove specific backup dates. Only directories matching the yyyy-MM-dd date pattern are considered for removal operations. .PARAMETER Path The root directory path where daily backup folders are stored, or the specific backup directory to remove when using -Date parameter. This should be the parent directory containing date-named subdirectories (e.g., '2025-08-24'). .PARAMETER Keep The minimum number of backup directories to retain when using retention-based cleanup. Older backups beyond this number will be deleted, sorted by date with oldest removed first. Set to 0 to remove all backups. Cannot be used with -Date parameter. Note: BackupsToKeep is an alias for this parameter. .PARAMETER Date Specific backup date to remove (yyyy-MM-dd format). When specified, only the backup directory for this date will be removed. Cannot be used with -Keep parameter. .PARAMETER Force Bypass confirmation prompts and remove backups without user interaction. Use with caution as this will permanently delete backup data. .INPUTS [String] Backup root path can be piped to this function. .OUTPUTS None. This function does not return any objects but may display verbose information about the removal process. .NOTES - Only directories matching the yyyy-MM-dd date pattern are processed - Supports ShouldProcess for WhatIf and Confirm functionality - Uses cross-platform compatible removal methods - Continues operation even if individual directory deletions fail - Verbose output provides detailed information about removal operations .EXAMPLE PS > Remove-DailyBackup -Path 'C:\Backups' -Keep 7 Keeps the 7 most recent daily backup folders, removes older ones .EXAMPLE PS > Remove-DailyBackup -Path 'C:\Backups' -Date '2025-09-01' Removes only the backup directory for September 1, 2025 .EXAMPLE PS > Remove-DailyBackup -Path '/home/user/backups' -Keep 3 -WhatIf Shows which backup directories would be deleted without actually removing them .EXAMPLE PS > Remove-DailyBackup -Path 'D:\Backups' -Keep 0 -Force Removes all backup directories without confirmation prompts .EXAMPLE PS > 'C:\MyBackups' | Remove-DailyBackup -Keep 14 Pipeline input: maintains a 2-week retention policy (14 days) for backup directories #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Retention')] param ( [Parameter( Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'The root directory path where daily backup folders are stored.' )] [ValidateNotNullOrEmpty()] [Alias('BackupRoot', 'DestinationPath')] [string] $Path, [Parameter( ParameterSetName = 'Retention', HelpMessage = 'The number of daily backup directories to retain.' )] [ValidateRange(0, [int]::MaxValue)] [Alias('BackupsToKeep')] [int] $Keep = 7, [Parameter( Mandatory = $true, ParameterSetName = 'SpecificDate', HelpMessage = 'Specific backup date to remove (yyyy-MM-dd format).' )] [ValidatePattern('^\d{4}-\d{2}-\d{2}$')] [string] $Date, [Parameter( HelpMessage = 'Bypass confirmation prompts and remove backups without user interaction.' )] [switch] $Force ) begin { $verboseEnabled = $VerbosePreference -eq 'Continue' Write-Verbose 'Remove-DailyBackup:Begin> Starting backup removal operation' -Verbose:$verboseEnabled } process { # Validate input path first if ([string]::IsNullOrWhiteSpace($Path)) { Write-Error "Remove-DailyBackup:Process> Path parameter cannot be null or empty" return } Write-Verbose "Remove-DailyBackup:Process> Input path: '$Path'" -Verbose:$verboseEnabled # Resolve the path to ensure it exists try { $resolvedPath = Resolve-Path -LiteralPath $Path -ErrorAction Stop $backupRoot = $resolvedPath.Path } catch { Write-Error "Remove-DailyBackup:Process> Cannot access path '$Path': $($_.Exception.Message)" return } Write-Verbose "Remove-DailyBackup:Process> Processing backup root: $backupRoot" -Verbose:$verboseEnabled # Get qualified backup directories (matching yyyy-MM-dd pattern) $qualifiedBackupDirs = @(Get-ChildItem -LiteralPath $backupRoot -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -match '^\d{4}-\d{2}-\d{2}$' }) if ($qualifiedBackupDirs.Length -eq 0) { Write-Verbose "Remove-DailyBackup:Process> No qualified backup directories found in: $backupRoot" -Verbose:$verboseEnabled return } Write-Verbose "Remove-DailyBackup:Process> Found $($qualifiedBackupDirs.Length) qualified backup directories" -Verbose:$verboseEnabled if ($PSCmdlet.ParameterSetName -eq 'SpecificDate') { # Remove specific date $targetDir = $qualifiedBackupDirs | Where-Object { $_.Name -eq $Date } if (-not $targetDir) { Write-Warning "Remove-DailyBackup:Process> No backup found for date: $Date" return } $confirmMessage = "Remove backup directory for date $Date" if ($Force -or $PSCmdlet.ShouldProcess($targetDir.FullName, $confirmMessage)) { Write-Verbose "Remove-DailyBackup:Process> Removing backup directory: $($targetDir.FullName)" -Verbose:$verboseEnabled try { Remove-ItemAlternative -LiteralPath $targetDir.FullName -WhatIf:$WhatIfPreference -Verbose:$verboseEnabled Write-Verbose "Remove-DailyBackup:Process> Successfully removed: $($targetDir.FullName)" -Verbose:$verboseEnabled } catch { Write-Error "Remove-DailyBackup:Process> Failed to remove directory '$($targetDir.FullName)': $($_.Exception.Message)" } } } else { # Retention-based cleanup if ($qualifiedBackupDirs.Length -le $Keep) { Write-Verbose "Remove-DailyBackup:Process> Current backup count ($($qualifiedBackupDirs.Length)) does not exceed retention limit ($Keep)" -Verbose:$verboseEnabled return } # Create hashtable to sort backup directories by date $backups = @{ } foreach ($backupDir in $qualifiedBackupDirs) { try { $backups.Add($backupDir.FullName, [System.DateTime]::ParseExact($backupDir.Name, 'yyyy-MM-dd', $null)) } catch { Write-Warning "Remove-DailyBackup:Process> Skipping directory with invalid date format: $($backupDir.Name)" } } # Sort by date and remove oldest backups $sortedBackupPaths = ($backups.GetEnumerator() | Sort-Object -Property Value | ForEach-Object { $_.Key }) $backupsToRemove = $sortedBackupPaths.Count - $Keep Write-Verbose "Remove-DailyBackup:Process> Will remove $backupsToRemove old backup directories (keeping $Keep)" -Verbose:$verboseEnabled for ($i = 0; $i -lt $backupsToRemove; $i++) { $backupPath = $sortedBackupPaths[$i] $backupDate = Split-Path -Leaf $backupPath $confirmMessage = "Remove old backup directory for date $backupDate" if ($Force -or $PSCmdlet.ShouldProcess($backupPath, $confirmMessage)) { Write-Verbose "Remove-DailyBackup:Process> Removing old backup directory: $backupPath" -Verbose:$verboseEnabled try { Remove-ItemAlternative -LiteralPath $backupPath -WhatIf:$WhatIfPreference -Verbose:$verboseEnabled Write-Verbose "Remove-DailyBackup:Process> Successfully removed: $backupPath" -Verbose:$verboseEnabled } catch { Write-Error "Remove-DailyBackup:Process> Failed to remove directory '$backupPath': $($_.Exception.Message)" } } } } } end { Write-Verbose 'Remove-DailyBackup:End> Backup removal operation completed' -Verbose:$verboseEnabled } } |