PSRotateLog.psm1

function Compress-IISLog
{

    <#
    .SYNOPSIS
    Performs gzip compression of IIS logs
    .DESCRIPTION
    Reads original logs from source directory and saves compressed logs to destination directory. The original files will be deleted after compression. The script won't rotate files which have been modified today.
    .EXAMPLE
    Compress-IISLogs -SourceDirectory D:\Log -DestinationDirectory D:\Archive
    #>

    #region Params
    [CmdletBinding()]
    param(
        # Source directory to read files from
        [Parameter(Position = 0, Mandatory = $true)]
        [ValidateScript( {Test-Path -Path $_ -PathType 'container'})]
        [System.IO.DirectoryInfo]
        $Path,
        # Destination directory to write archives to
        [Parameter(Position = 1, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $Destination
    )
    #endregion

    $SourceDirectory = $Path
    $DestinationDirectory = $Destination

    Get-ChildItem -Path $SourceDirectory -Recurse -Exclude "*.gz"|ForEach-Object {
        if ($($_.Attributes -band [System.IO.FileAttributes]::Directory) -ne [System.IO.FileAttributes]::Directory)
        {
            #Current file
            $curFile = $_

            #Check the file wasn't modified today
            if ($curFile.LastWriteTime.Date -ne [System.DateTime]::Today)
            {

                $containedDir = $curFile.Directory.FullName.Replace($SourceDirectory, $DestinationDirectory)

                #if target directory doesn't exist - create
                if ($(Test-Path -Path "$containedDir") -eq $false)
                {
                    New-Item -Path "$containedDir" -ItemType directory|Out-Null
                }

                try
                {

                    Compress-File -InputFile $curFile -OutputFile "$containedDir\$($curFile.Name).gz"
                    Remove-Item -Path $curFile
                }
                catch {throw $_}
            }
        }
    }
}
function Compress-File
{
    #region Params
    param(
        [Parameter(Position = 0, Mandatory = $true)]
        [ValidateScript( {Test-Path -Path $_ -PathType 'leaf'})]
        [System.String]
        $InputFile,
        [Parameter(Position = 1, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $OutputFile
    )
    #endregion

    try
    {
        #Creating buffer with size 8MB
        $bytesGZipFileBuffer = New-Object -TypeName byte[](8192)

        $streamGZipFileInput = New-Object -TypeName System.IO.FileStream($InputFile, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
        $streamGZipFileOutput = New-Object -TypeName System.IO.FileStream($OutputFile, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write)
        $streamGZipFileArchive = New-Object -TypeName System.IO.Compression.GZipStream($streamGZipFileOutput, [System.IO.Compression.CompressionMode]::Compress)

        for ($iBytes = $streamGZipFileInput.Read($bytesGZipFileBuffer, 0, $bytesGZipFileBuffer.Count);
            $iBytes -gt 0;
            $iBytes = $streamGZipFileInput.Read($bytesGZipFileBuffer, 0, $bytesGZipFileBuffer.Count))
        {

            $streamGZipFileArchive.Write($bytesGZipFileBuffer, 0, $iBytes)
        }

        $streamGZipFileArchive.Dispose()
        $streamGZipFileInput.Close()
        $streamGZipFileOutput.Close()

        Get-Item $OutputFile
    }
    catch { throw $_ }
}
function Get-FileList
{
    [OutputType([System.IO.FileInfo])]
    #region Param
    param(
        [Parameter(Position = 0, Mandatory = $true)]
        [ValidateScript( {Test-Path -Path $_ -PathType 'Container'})]
        [System.IO.DirectoryInfo]
        $Path,
        [Parameter(Position = 1, Mandatory = $false)]
        [bool]
        $ListEmptyFolders = $false
    )
    #endregion
    process
    {
        $excludeItems = New-Object -TypeName System.Collections.ArrayList

        Get-ChildItem -Recurse -Force $Path|ForEach-Object {
            $fileCurrent = $_
            $itemIsNotExcluded = $true
            $itemIsFolder = $fileCurrent.Attributes -band [System.IO.FileAttributes]::Directory
            $folderIsEmpty = $itemIsFolder -and $ListEmptyFolders -and @(Get-ChildItem -Path $fileCurrent.FullName|Select-Object -First 1).Count -eq 0
            $itemIsReparsePoint = $fileCurrent.Attributes -band [System.IO.FileAttributes]::ReparsePoint
            $itemToBeRemoved = (!$itemIsFolder -or ($itemIsFolder -and $folderIsEmpty)) -and !$itemIsReparsePoint

            if ($itemIsReparsePoint)
            {
                $itemIsNotExcluded = $false
                $excludeItems.Add($fileCurrent)
            }
            else
            {
                $itemIsNotExcluded = $null -eq ($excludeItems | Where-Object {
                        $fileCurrent.FullName -match [regex]::Escape($_.FullName )
                    })
            }

            if ($itemToBeRemoved -and $itemIsNotExcluded)
            {
                Write-Output $fileCurrent
            }
        }
    }
}

function Remove-IISLogSize
{
    [OutputType([System.IO.FileInfo])]
    #region Param
    Param(
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $false,
            Position = 0)]
        [ValidateScript( {{Test-Path -Path $_ -PathType 'container'}})]
        [System.IO.DirectoryInfo]
        $Path,
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $false,
            Position = 1)]
        [ValidateNotNullOrEmpty()]
        [System.Int64]
        $Size
    )
    #endregion

    #Converting to List, otherwise array is fixed size
    $sortedList = [System.Collections.Generic.List`1[[System.Object, mscorlib, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089]]](Get-FileList -Path $Path|Sort-Object CreationTime)
    $folderSize = ($sortedList| Measure-Object -property length -sum).Sum

    try
    {
        while ($folderSize -gt $Size)
        {

            $item = $sortedList[0]

            Remove-Item -Path $item.FullName
            $sortedList.RemoveAt(0)

            $folderSize -= $item.Length
            Write-Output $item
        }
    }
    catch { throw $_ }
}

function Remove-IISLogCount
{
    #region Param
    Param(
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $false,
            Position = 0)]
        [ValidateScript( {{Test-Path -Path $_ -PathType 'container'}})]
        [System.IO.DirectoryInfo]
        $Path,
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $false,
            Position = 1)]
        [ValidateNotNullOrEmpty()]
        [System.Int64]
        $KeepFiles
    )
    #endregion

    $sortedList = Get-FileList -Path $Path|Sort-Object CreationTime
    #Number of files for removing
    $removeCount = $sortedList.Count - $KeepFiles

    try
    {
        if ($removeCount -gt 0)
        {
            $sortedList|Select-Object -First $removeCount|ForEach-Object {
                Remove-Item -Path $_.FullName
                Write-Output $_
            }
        }
    }
    catch { throw $_ }
}

function Remove-IISLogOlder
{
    #region Param
    Param(
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $false,
            Position = 0)]
        [ValidateScript( {{Test-Path -Path $_ -PathType 'container'}})]
        [System.IO.DirectoryInfo]
        $Path,
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $false,
            Position = 1)]
        [ValidateNotNullOrEmpty()]
        [System.DateTime]
        $Older,
        [Parameter(Position = 1, Mandatory = $false)]
        [bool]
        $RemoveEmptyFolders = $false
    )
    #endregion

    try
    {
        Get-FileList -Path $Path -ListEmptyFolders $RemoveEmptyFolders|Where-Object {$_.LastWriteTime -lt $Older}|ForEach-Object {
            Remove-Item -Path $_.FullName
            Write-Output $_
        }
    }
    catch { throw $_ }
}
function Remove-IISLog
{
    <#
    .SYNOPSIS
        Removes logs
    .DESCRIPTION
        Log removing based on directory size, files count, files age
    .EXAMPLE
        C:\PS> Remove-IISLog -Path C:\Inetpub\Logs -FolderSize 100MB
        Removes old files and keeps only 100MB of the newest files
    .EXAMPLE
        C:\PS> Remove-IISLog -Path C:\Inetpub\Logs -KeepFiles 10
        Keeps 10 newest files
    .EXAMPLE
        C:\PS> Remove-IISLog -Path D:\Logs2 -Older ([datetime]::now.AddDays(-10))
        Removes files older 10 days
    #>

    [CmdletBinding(DefaultParameterSetName = 'FolderSize')]
    [OutputType([System.IO.FileInfo])]
    #region Param
    Param(
        # Path to log directory
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $false,
            Position = 0)]
        [ValidateScript( {{Test-Path -Path $_ -PathType 'container'}})]
        [System.IO.DirectoryInfo]
        $Path,
        # Folder size to keep
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $false,
            Position = 1,
            ParameterSetName = 'FolderSize')]
        [ValidateNotNullOrEmpty()]
        [System.Int64]
        $FolderSize,
        # Number of files to keep
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $false,
            Position = 1,
            ParameterSetName = 'FilesNumber')]
        [ValidateNotNullOrEmpty()]
        [System.Int64]
        $KeepFiles,
        # Remove files older than specified
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $false,
            Position = 1,
            ParameterSetName = 'FilesAge')]
        [ValidateNotNullOrEmpty()]
        [System.DateTime]
        $Older,
        [Parameter(Position = 1, Mandatory = $false)]
        [switch]
        $RemoveEmptyFolders
    )
    switch ($PSCmdlet.ParameterSetName)
    {
        'FolderSize'
        {
            Remove-IISLogSize -Path $Path -Size $FolderSize
            break
        }
        'FilesNumber'
        {
            Remove-IISLogCount -Path $Path -KeepFiles $KeepFiles
            break
        }
        'FilesAge'
        {
            Remove-IISLogOlder -Path $Path -Older $Older -RemoveEmptyFolders $RemoveEmptyFolders
            break
        }
    }
    #endregion
}