Public/Invoke-LogRotation.ps1

function Invoke-LogRotation {
    <#
    .SYNOPSIS
        Compresses log files by month.
    .DESCRIPTION
        The Invoke-LogRotation cmdlet retrieves a list of log file in the specified locations and compressed them into a ZIP archive by month. Once the contents of the archive are verified the original log files are deleted.
    .EXAMPLE
        Invoke-LogRotation -Path C:\Inetpub\Logs\LogFiles\W3SVC1
        Archives the log files for the IIS 'Default Website' using the default 5 day retention
    .EXAMPLE
        Invoke-LogRotation -Path C:\Inetpub\Logs\LogFiles\W3SVC1 -KeepRaw 10
        Archives the log files for the IIS 'Default Website' using the specified 10 day retention
    .LINK
        http://psservermanagement.readthedocs.io/en/latest/functions/Invoke-LogRotation
    .NOTES
        Author: Trent Willingham
        Check out my other projects on GitHub https://github.com/twillin912
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        # Specifies a path to one or more locations. Invoke-LogRotation processes the log files in the specified locations.
        [Parameter(Mandatory = $true, Position = 0)]
        [string[]]$Path,

        # Specifies the number of days to keep uncompressed log files. If you do not specify this parameter, the cmdlet will retain 5 days.
        [Parameter(Position = 1)]
        [Alias('CompressDays')]
        [int] $KeepRaw = 5,

        # Specifies a wildcard selection string of files to include.
        [Parameter()]
        [string]$Include = '*.log',

        # Specifies a wildcard selection string of files to exclude.
        [Parameter()]
        [string]$Exclude
    )

    begin {
        $DateDisplayFormat = 'MM/dd/yyyy'
        $DateFileFormat = 'yyyy-MM'
        $CurrentDate = Get-Date -Hour 0 -Minute 0 -Second 0

        if ($KeepRaw) {
            $CompressBefore = (Get-Date -Date $CurrentDate).AddDays( - $KeepRaw)
        }

        $AdditionalParams = @{ 'Include' = $Include }
        if ($Exclude) { $AdditionalParams.Add('Exclude', $Exclude) }

        $null = [Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem")

    }

    process {
        foreach ($LogPath in $Path) {
            if (!(Test-Path -Path $LogPath)) {
                Write-Error -Message "Cannot find path '$LogPath' because it does not exist."
                break
            }

            $LogFolder = Split-Path -Path $($LogPath) -Leaf

            if ($KeepRaw) {
                $LogsToCompress = Get-ChildItem -Path $LogPath @AdditionalParams -Recurse |
                    Where-Object { $PSItem.PSIsContainer -eq $false -and $PSItem.LastWriteTime -lt $CompressBefore }
                Write-Verbose -Message "Compressing $($LogsToCompress.Count) older than $($CompressBefore.ToString($DateDisplayFormat))"

                $LogHashTable = @{}
                foreach ($File in $LogsToCompress) {
                    $LogHashTable.Add($File.FullName, $File.LastWriteTime.ToString($DateFileFormat))
                }
                $LogHashTable = $LogHashTable.GetEnumerator() | Sort-Object -Property Value, Name
                $MonthsToProcess = @( $LogHashTable | Group-Object -Property Value | Select-Object -Property Name)

                foreach ($Month in $MonthsToProcess) {
                    $ZipFileName = "$($env:ComputerName)-$($LogFolder)-$($Month.Name).zip"
                    $ZipFullName = Join-Path -Path $LogPath -ChildPath $ZipFileName
                    $CurrentMonthLogs = $LogHashTable | Where-Object { $PSItem.Value -eq "$($Month.Name)" }

                    foreach ($LogFile in $CurrentMonthLogs) {
                        $LogName = Split-Path -Path $LogFile.Name -Leaf

                        if ($PSCmdlet.ShouldProcess($ZipFullName, "Create/Update Archive")) {
                            $ZipFile = [System.IO.Compression.ZipFile]::Open($ZipFullName, "Update")
                        }

                        if ($PSCmdlet.ShouldProcess($LogFile.Name, "Get Content")) {
                            $LogContent = Get-Content -LiteralPath $LogFile.Name -Raw
                        }

                        if (!($ZipFile.GetEntry($LogName))) {
                            if ($PSCmdlet.ShouldProcess($LogFile.Name, "Add to Archive")) {
                                $ZipFileEntry = $ZipFile.CreateEntry($LogName)
                                $StreamWriter = [System.IO.StreamWriter]$ZipFileEntry.Open()
                                $StreamWriter.Write($LogContent)
                                $StreamWriter.Dispose()
                                $ZipFileEntry.LastWriteTime = (Get-Item -LiteralPath "$($LogFile.Name)").LastWriteTime
                            }
                        }

                        if ($PSCmdlet.ShouldProcess($ZipFullName, "Save Archive")) {
                            $ZipFile.Dispose()
                        }

                        if ($PSCmdlet.ShouldProcess($LogFile.Name, "Compare to Archive")) {
                            $ZipFile = [System.IO.Compression.ZipFile]::Open($ZipFullName, "Read")
                            $ZipFileEntry = [System.IO.StreamReader]$ZipFile.GetEntry($LogName).Open()
                            $ZipContent = $ZipFileEntry.ReadToEnd()
                            $ZipFile.Dispose()

                            if ($ZipContent -eq $LogContent -or $LogContent.Length -eq 0) {
                                Remove-Item -LiteralPath $LogFile.Name
                            }
                        }
                        [System.GC]::Collect()
                    }
                }
            }
        }
    }

    end {

    }
}