Delete-FilesByAge.ps1
<#PSScriptInfo .VERSION 1.3.7 .GUID f03ddea5-f6e3-498a-b249-1ac6b7ec8f01 .AUTHOR June Castillote .COMPANYNAME www.lazyexchangeadmin.com .COPYRIGHT june.castillote@gmail.com .TAGS PowerShell Script Delete Housekeeping Logs CleanUp .LICENSEURI .PROJECTURI https://github.com/junecastillote/Delete-FilesByAge .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES 1.3.3 (June 16, 2019) - Added Teams Notification Option 1.3.4 (June 17, 2019) - Additional fact (Source:) for MS Teams notification 1.3.5 (June 18, 2019) - Fixed CSS formatting of report - Fixed MS Teams JSON notification format - Code cleanup 1.3.6 (June 18, 2019) - Added timezone format 1.3.7 (June 18,2019) - Fixed files to delete collection .PRIVATEDATA #> <# .DESCRIPTION Delete files from specified paths based on age, with email summary reporting. #> Param( #paths to clean up (eg. "c:\Folder1","c:\folder2") [Parameter(Mandatory=$true)] [string[]]$Paths, #list of files or extension to INCLUDE (eg. *.blg,*.txt) [Parameter(Mandatory=$true)] [string[]]$Include, #list of files or extension to EXCLUDE (eg. *.blg,*.txt) [Parameter(Mandatory=$false)] [string[]]$Exclude, #switch to indicate recursive action [Parameter()] [switch]$Recurse, [Parameter(Mandatory=$true)] [int]$daysToKeep, #path to the output/Report directory (eg. c:\scripts\output) [Parameter(Mandatory=$true)] [string]$outputDirectory, #path to the log directory (eg. c:\scripts\logs) [Parameter()] [string]$logDirectory, #prefix string for the report (ex. COMPANY) [Parameter()] [string]$headerPrefix, #Switch to enable email report [Parameter()] [switch]$sendEmail, #Sender Email Address [Parameter()] [string]$sender, #Recipient Email Addresses - separate with comma [Parameter()] [string[]]$recipients, #smtpServer [Parameter()] [string]$smtpServer, #smtpPort [Parameter()] [string]$smtpPort, #credential for SMTP server (if applicable) [Parameter()] [pscredential]$smtpCredential, #switch to indicate if SSL will be used for SMTP relay [Parameter()] [switch]$smtpSSL, #accepts Teams WebHook URI [Parameter()] [string[]]$notifyTeams ) #................................... #Region FUNCTION #................................... #Function to Stop Transaction Logging Function Stop-TxnLogging { $txnLog="" Do { try { Stop-Transcript | Out-Null } catch [System.InvalidOperationException] { $txnLog="stopped" } } While ($txnLog -ne "stopped") } #Function to Start Transaction Logging Function Start-TxnLogging { param ( [Parameter(Mandatory=$true,Position=0)] [string]$logDirectory ) Stop-TxnLogging Start-Transcript $logDirectory -Append } #Function to get Script Version and ProjectURI for PSv4 Function Get-ScriptInfo { param ( [Parameter(Mandatory=$true,Position=0)] [string]$Path ) $scriptFile = Get-Content $Path $props = @{ Version = "" ProjectURI = "" } $scriptInfo = New-Object PSObject -Property $props # Get Version foreach ($line in $scriptFile) { if ($line -like ".VERSION*") { $scriptInfo.Version = $line.Split(" ")[1] BREAK } } # Get ProjectURI foreach ($line in $scriptFile) { if ($line -like ".PROJECTURI*") { $scriptInfo.ProjectURI = $line.Split(" ")[1] BREAK } } Remove-Variable scriptFile Return $scriptInfo } #Function to get current system timezone (for PS versions below 5) Function Get-TimeZoneInfo { $tzName = ([System.TimeZone]::CurrentTimeZone).StandardName $tzInfo = [System.TimeZoneInfo]::FindSystemTimeZoneById($tzName) Return $tzInfo } #................................... #EndRegion FUNCTION #................................... #................................... #Region SCRIPT INFO #................................... if ($PSVersionTable.psversion.Major -lt 5) { $scriptInfo = Get-ScriptInfo -Path $MyInvocation.MyCommand.Definition } else { $scriptInfo = Test-ScriptFileInfo -Path $MyInvocation.MyCommand.Definition } #................................... #Get TimeZone Information $timeZoneInfo = Get-TimeZoneInfo #EndRegion SCRIPT INFO #................................... #................................... #Region PARAMETER CHECK #................................... $isAllGood = $true if ($sendEmail) { if (!$sender) { Write-Host (get-date -Format "dd-MMM-yyyy hh:mm:ss tt") ": ERROR: A valid sender email address is not specified." -ForegroundColor Yellow $isAllGood = $false } if (!$recipients) { Write-Host (get-date -Format "dd-MMM-yyyy hh:mm:ss tt") ": ERROR: No recipients specified." -ForegroundColor Yellow $isAllGood = $false } if (!$smtpServer ) { Write-Host (get-date -Format "dd-MMM-yyyy hh:mm:ss tt") ": ERROR: No SMTP Server specified." -ForegroundColor Yellow $isAllGood = $false } if (!$smtpPort ) { Write-Host (get-date -Format "dd-MMM-yyyy hh:mm:ss tt") ": ERROR: No SMTP Port specified." -ForegroundColor Yellow $isAllGood = $false } } if ($isAllGood -eq $false) { Write-Host (get-date -Format "dd-MMM-yyyy hh:mm:ss tt") ": ERROR: Exiting Script." -ForegroundColor Yellow EXIT } #................................... #EndRegion PARAMETER CHECK #................................... #................................... #Region CSS #................................... $css_string = @' <style type="text/css"> #HeadingInfo { font-family:"Segoe UI"; width:100%; border-collapse:collapse; } #HeadingInfo td, #HeadingInfo th { font-size:0.8em; padding:3px 7px 2px 7px; } #HeadingInfo th { font-size:2.0em; font-weight:normal; text-align:left; padding-top:5px; padding-bottom:4px; background-color:#604767; color:#fff; } #SectionLabels { font-family:"Segoe UI"; width:100%; border-collapse:collapse; } #SectionLabels th.data { font-size:2.0em; text-align:left; padding-top:5px; padding-bottom:4px; background-color:#fff; color:#000; } #data { font-family:"Segoe UI"; width:100%; border-collapse:collapse; } #data td, #data th { font-size:0.8em; border:1px solid #DDD; padding:3px 7px 2px 7px; vertical-align:top; } #data th { font-size:0.8em; padding-top:5px; padding-bottom:4px; background-color:#00B388; color:#fff; text-align:left; } #data td { font-size:0.8em; padding-top:5px; padding-bottom:4px; text-align:left; } #data td.bad { font-size:0.8em; font-weight: bold; padding-top:5px; padding-bottom:4px; color:#f04953; } #data td.good { font-size:0.8em; font-weight: bold; padding-top:5px; padding-bottom:4px; color:#01a982; } .status { width: 10px; height: 10px; margin-right: 7px; margin-bottom: 0px; background-color: #CCC; background-position: center; opacity: 0.8; display: inline-block; } .green { background: #01a982; } .purple { background: #604767; } .orange { background: #ffd144; } .red { background: #f04953; } </style> '@ #................................... #EndRegion CSS #................................... #................................... #Region PATHS #................................... $today = Get-Date [string]$fileSuffix = '{0:dd-MMM-yyyy_hh-mm_tt}' -f $today $today = $today.ToString("F") $today = "$($today) $($timeZoneInfo.DisplayName.ToString().Split(" ")[0])" $logFile = "$($logDirectory)\Log_$($fileSuffix).log" $outputCSV = "$($outputDirectory)\delete-Summary_$($fileSuffix).csv" $outputHTML = "$($outputDirectory)\delete-Summary_$($fileSuffix).html" #Create folders if not found if ($logDirectory) { if (!(Test-Path $logDirectory)) { New-Item -ItemType Directory -Path $logDirectory | Out-Null #start transcribing---------------------------------------------------------------------------------- Start-TxnLogging $logFile #---------------------------------------------------------------------------------------------------- } else { Start-TxnLogging $logFile } } if (!(Test-Path $outputDirectory)) { New-Item -ItemType Directory -Path $outputDirectory | Out-Null } #................................... #EndRegion PARAMETER CHECK #................................... #................................... #Region GENERATE FILES LIST #................................... $fileParams = @{ Path = $Paths } if ($Recurse){$fileParams+=@{Recurse=$true}} if ($Exclude){$fileParams+=@{Exclude=$Exclude}} $fileParams Write-Host "" [datetime]$oldDate = (Get-Date).AddDays(-$daysToKeep) #$filesToDelete = Get-ChildItem @fileParams | Where-Object {$_.LastWriteTime -lt $oldDate -and !$_.PSIsContainer} $filesToDelete = @() foreach ($fInclude in $Include){ $temp = Get-ChildItem @fileParams -Filter $fInclude | Where-Object {$_.LastWriteTime -lt $oldDate -and !$_.PSIsContainer} $filesToDelete += $temp } #................................... #EndRegion GENERATE FILES LIST #................................... #................................... #Region FILE DELETION #................................... if ($filesToDelete) { Write-Host (get-date -Format "dd-MMM-yyyy hh:mm:ss tt") ": Found Total of $($filesToDelete.Count) files" -ForegroundColor Green $resultLog = @() $successful = 0 $failed = 0 [int64]$deletedSize = 0 [int64]$failedSize = 0 foreach ($file in $filesToDelete) { $temp = "" | Select-Object FileName,FileSize,Status $temp.FileName = $file.FullName $temp.FileSize = $file.Length try { Remove-Item -Path ($file.FullName) -Force -Confirm:$false -ErrorAction Stop $temp.Status = "Success" $successful = $successful+1 $deletedSize = $deletedSize + $file.Length Write-Host (get-date -Format "dd-MMM-yyyy hh:mm:ss tt") ": Delete $($file.FullName) - Success " -ForegroundColor Green } catch { $temp.Status = "Failed" $failed = $failed+1 $failedSize = $failedSize + $file.Length Write-Host (get-date -Format "dd-MMM-yyyy hh:mm:ss tt") ": Delete $($file.FullName) - Failed " -ForegroundColor Red } $resultLog += $temp } $resultLog | Export-Csv -NoTypeInformation $outputCSV $summary = "" | Select-Object Paths,TotalNumberOfFiles,TotalSizeOfAllFiles,SuccessfulDeletions,FailedDeletions,TotalSuccessfulDeletionSize,TotalFailedDeletionSize $summary.Paths = $Paths $summary.TotalNumberOfFiles = "{0:N0}" -f ($filesToDelete).Count $summary.TotalSizeOfAllFiles = "{0:N0}" -f ($filesToDelete | Measure-Object -Property Length -Sum).Sum $summary.SuccessfulDeletions = "{0:N0}" -f $successful $summary.FailedDeletions = "{0:N0}" -f $failed $summary.TotalSuccessfulDeletionSize = "{0:N0}" -f $deletedSize $summary.TotalFailedDeletionSize = "{0:N0}" -f $failedSize Write-Host "" Write-Host (get-date -Format "dd-MMM-yyyy hh:mm:ss tt") ": SUMMARY:" $summary #................................... #Region HTML #................................... if ($headerPrefix) { $mailSubject = "[" + $headerPrefix + "] File Deletion Task Summary" } else { $mailSubject = "File Deletion Task Summary" } $htmlBody += "<html><head><title>$($mailSubject) - $($Today)</title><meta http-equiv=""Content-Type"" content=""text/html; charset=ISO-8859-1"" />" $htmlBody += $css_string $htmlBody += '</head><body><p><font size="2" face="Tahoma">' $htmlBody += '<table id="HeadingInfo">' if ($headerPrefix) { $htmlBody += '<tr><th>'+ $headerPrefix + '<br />Delete Files Older Than ' + $daysToKeep + ' Days<br / >'+ $today +'</th></tr>' } else { $htmlBody += '<tr><th>Delete Files Older Than ' + $daysToKeep + ' Days<br / >'+ $today +'</th></tr>' } $htmlBody += '</table><hr />' $htmlBody += '<table id="SectionLabels">' $htmlBody += '<tr><th class="data">Summary</th></tr></table>' $htmlBody += '<table id="data">' $htmlBody += '<tr><th width="15%">Computer</th><td>'+ $env:COMPUTERNAME +'</td></tr>' $htmlBody += '<tr><th>Paths</th><td>'+ ($Paths -join "<br />") +'</td></tr>' $htmlBody += '<tr><th>Successful Deletion</th><td class="good">' + ($summary.SuccessfulDeletions) + ' files (' + ($summary.TotalSuccessfulDeletionSize) + ' bytes)</td></tr>' $htmlBody += '<tr><th>Failed Deletion</th><td class="bad">' + ($summary.FailedDeletions) + ' files (' + ($summary.TotalFailedDeletionSize) + ' bytes)</td></tr>' $htmlBody += '<tr><th>Total Files</th><td>' + ($summary.TotalNumberOfFiles) + ' files (' + ($summary.TotalSizeOfAllFiles) + ' bytes)</td></tr>' $htmlBody += '</table><hr />' #start table SETTINGS $htmlBody += '<table id="SectionLabels">' $htmlBody += '<tr><th class="data">Settings</th></tr></table>' $htmlBody += '<table id="data">' $htmlBody += '<tr><th width="15%">Included</th><td>'+ ($Include -join ";") +'</td></tr>' $htmlBody += '<tr><th width="15%">Excluded</th><td>'+ ($Exclude -join ";") +'</td></tr>' $htmlBody += '<tr><th width="15%">Recursive</th><td>'+ (invoke-command {if ($Recurse) {return "Yes"} else {return "No"}}) +'</td></tr>' $htmlBody += '<tr><th width="15%">Send Email Report</th><td>'+ (invoke-command {if ($sendEmail) {return "Yes"} else {return "No"}}) +'</td></tr>' $htmlBody += '<tr><th width="15%">SMTP Server Name or IP</th><td>'+ $smtpServer +'</td></tr>' $htmlBody += '<tr><th width="15%">SMTP Server Port</th><td>'+ $smtpPort +'</td></tr>' $htmlBody += '<tr><th width="15%">SMTP SSL in Use</th><td>'+ (invoke-command {if ($smtpSSL) {return "Yes"} else {return "No"}}) +'</td></tr>' $htmlBody += '<tr><th width="15%">SMTP Login Required</th><td>' + (invoke-command {if ($smtpCredential) {return "Yes"} else {return "No"}}) + '</td></tr>' $htmlBody += '<tr><th width="15%">Script File</th><td>' + $MyInvocation.MyCommand.Definition + '</td></tr>' $htmlBody += '<tr><th width="15%">Csv Report File</th><td>' + $outputCSV + '</td></tr>' $htmlBody += '<tr><th width="15%">Html Report File</th><td>' + $outputHTML + '</td></tr>' $htmlBody += '<tr><th width="15%">Script Version</th><td>' + "<a href=""$($scriptInfo.ProjectURI)"">$($MyInvocation.MyCommand.Definition.ToString().Split("\")[-1].Split(".")[0]) $($scriptInfo.version)</a>" + '</td></tr>' $htmlBody += '</table><hr />' #end table SETTINGS $htmlBody += "</body></html>" #Export HTML Report $htmlBody | Out-File $outputHTML #................................... #EndRegion HTML #................................... #................................... #Region EMAIL #................................... if ($sendEmail) { $mailParams = @{ From = $sender To = $recipients Subject = $mailSubject + ": " + ('{0:dd-MMM-yyyy hh:mm tt}' -f $Today) Body = $htmlBody BodyAsHTML = $true smtpServer = $smtpServer Port = $smtpPort useSSL = $smtpSSL attachments = $outputCSV } #SMTP Authentication if ($smtpCredential){ $mailParams += @{credential = $smtpCredential} } Write-Host (get-date -Format "dd-MMM-yyyy hh:mm:ss tt") ": Sending email to" ($recipients -join ",") -ForegroundColor Green #Send message Send-MailMessage @mailParams } #................................... #EndRegion EMAIL #................................... #................................... #Region TEAMS #................................... if ($notifyTeams) { $teamsMessage = ConvertTo-Json -Depth 4 @{ title = $mailSubject text = ('{0:dd-MMM-yyyy hh:mm tt}' -f $Today) sections = @( @{ activityTitle = "Delete Files Older Than $($daysToKeep) Days" activityImage = "https://raw.githubusercontent.com/junecastillote/Delete-FilesByAge/master/res/deleteFBAIcon.png" activityText = "" }, @{ title = "<h4>Summary</h4>" facts = @( @{ name = "Computer:" value = "$($env:COMPUTERNAME)" }, @{ name = "Paths:" value = ($Paths -join ";<br />") }, @{ name = "Total Number of Files: " value = "$($summary.TotalNumberOfFiles) files ($($summary.TotalSizeOfAllFiles)) bytes)" }, @{ name = "Successful Deletion:" value = "<font color=""Green"">$($summary.SuccessfulDeletions) files ($($summary.TotalSuccessfulDeletionSize) bytes)</font>" }, @{ name = "Failed Deletion:" value = "<font color=""Red"">$($summary.FailedDeletions) files ($($summary.TotalFailedDeletionSize) bytes)</font>" } ) }, @{ title = "<h4>Settings</h4>" facts = @( @{ name = "Include:" value = ($Include -join ";") }, @{ name = "Exclude:" value = ($Exclude -join ";") }, @{ name = "Recurse:" value = "$($Recurse)" } @{ name = "Script File:" value = $MyInvocation.MyCommand.Definition } @{ name = "Csv Report File:" value = $outputCSV } @{ name = "Html Report File:" value = $outputHTML }, @{ name = "Script Version:" value = "<a href=""$($scriptInfo.ProjectURI)"">$($MyInvocation.MyCommand.Definition.ToString().Split("\")[-1].Split(".")[0]) $($scriptInfo.version)</a>" } ) } ) } Write-Host (get-date -Format "dd-MMM-yyyy hh:mm:ss tt") ": Sending Teams Notification" -ForegroundColor Green foreach ($uri in $notifyTeams) { try { Invoke-RestMethod -uri $uri -Method Post -body $teamsMessage -ContentType 'application/json' -ErrorAction Stop Write-Host "SUCCESS: $($uri)" -ForegroundColor Green } catch { Write-Host "FAILED: $($_.exception.message)" -ForegroundColor RED } } } #................................... #EndRegion TEAMS #................................... Write-Host (get-date -Format "dd-MMM-yyyy hh:mm:ss tt") ": HTML Summary Report saved in $outputHTML " -ForegroundColor Cyan Write-Host (get-date -Format "dd-MMM-yyyy hh:mm:ss tt") ": CSV Summary Report saved in $outputCSV " -ForegroundColor Cyan } else { Write-Host (get-date -Format "dd-MMM-yyyy hh:mm:ss tt") ": No files to delete. Exiting script" -ForegroundColor Green } #................................... #EndRegion FILE DELETION #................................... if ($logDirectory) {Write-Host (get-date -Format "dd-MMM-yyyy hh:mm:ss tt") ": Transcript Log saved in $logfile " -ForegroundColor Cyan} Stop-TxnLogging |