CPolydorou.IIS.psm1

#region Functions
#region Get-WebConnections
function Get-WebConnections
{
    Param
    (
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, Position = 0)]
        [string]$WebSite,

        [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 1)]
        [string[]]$ComputerName = $env:COMPUTERNAME
    )

    Begin{}

    Process
    {

        foreach($c in $ComputerName)
        {
            $results = @()
            $perfmon = new-object System.Diagnostics.PerformanceCounter
            $perfmon.CategoryName = "Web Service"
            $perfmon.CounterName = "Current Connections"
            $perfmon.MachineName = $c

            $cat = new-object System.Diagnostics.PerformanceCounterCategory("Web Service")
            $instances = $cat.GetInstanceNames()

            foreach ($instance in $instances)
            {
                if(-not ([String]::IsNullOrEmpty($WebSite)))
                {
                    if($WebSite -ne $instance)
                    {
                        continue
                    }
                }

                $perfmon.InstanceName = $instance
                $obj = New-Object psobject
                $obj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $c
                $obj | Add-Member -MemberType NoteProperty -Name WebSite -Value $instance
                
                $connections  = -1
                try
                {
                    $connections = $perfmon.NextValue()
                }
                catch
                {
                }

                $obj | Add-Member -MemberType NoteProperty -Name Connections -Value $connections
                $results += $obj
            }

            write-output $results
        }
    }
        
    End{}
}
#endregion
#region Restart-IISApplicationPool
Function Restart-IISApplicationPool
{
    [cmdletBinding()]

    Param
    (
        [Parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias("Name")]
        [string[]]$ComputerName = $env:COMPUTERNAME,

        [Parameter(Mandatory = $true, Position = 1)]
        [string[]]$AppPool
    )

    Begin {}

    Process
    {
        foreach($s in $ComputerName)
        {
            Write-Verbose ("Processing server: " + $s)
            Invoke-Command -ComputerName $s `
                           -ScriptBlock {
                                [cmdletBinding()]
                                
                                Param
                                (
                                    [string[]]$ApplicationPoolNames
                                )

                                # Import the web administration module
                                Import-Module WebAdministration

                                # Restart the application pool
                                $VerbosePreference = $using:VerbosePreference
                                foreach($ap in $ApplicationPoolNames)
                                {
                                    Write-Verbose ("`tRestarting application pool: " + $ap)
                                    Restart-WebAppPool -Name $ap
                                }
                            } -ArgumentList (,$AppPool) -Verbose
        }
    }

    End {}
}
#endregion
#region Get-IISApplicationPool
function Get-IISApplicationPool
{
    [cmdletBinding()]

    Param
    (
        [Parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias("Name")]
        [string[]]$ComputerName = $env:COMPUTERNAME,

        [Parameter(Mandatory = $false, Position = 1)]
        [string[]]$AppPool
    )

    Begin {}

    Process
    {
        foreach($s in $ComputerName)
        {
            Write-Verbose ("Processing server: " + $s)
            $pools = Invoke-Command -ComputerName $s `
                           -ScriptBlock {
                                [cmdletBinding()]
                                
                                Param
                                (
                                    [string[]]$ApplicationPoolNames
                                )

                                # Import the web administration module
                                Import-Module WebAdministration

                                # Restart the application pool
                                $VerbosePreference = $using:VerbosePreference
                                Get-ChildItem -Path IIS:\AppPools
                            } -Verbose |
                Select-Object -Property @{Name="Name"; Expression={$_.Name}}, `
                                        @{Name="State"; Expression={$_.State}}, `
                                        @{Name="ComputerName"; Expression = {$_.PSComputerName}}, `
                                        @{Name="WorkerProcesses"; Expression={$_.WorkerProcesses.Collection.ProcessID -join ","}}
            if($AppPool)
            {
                foreach($ap in $AppPool)
                {
                    $pools |
                        Where-Object {$_.Name -like $ap}
                }
            }
            else
            {
                $pools
            }
        }
    }

    End {}
}
#endregion
#region Get-IISApplicationPoolWorkerProcesses
function Get-IISApplicationPoolWorkerProcesses
{
    [cmdletBinding()]

    Param
    (
        [Parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias("Name")]
        [string[]]$ComputerName = $env:COMPUTERNAME,

        [Parameter(Mandatory = $false, Position = 1)]
        [string[]]$AppPool
    )

    Begin {}

    Process
    {
        foreach($s in $ComputerName)
        {
            Write-Verbose ("Processing server: " + $s)
            $workerProcesses = Invoke-Command -ComputerName $s `
                           -ScriptBlock {
                                [cmdletBinding()]
                                
                                Param
                                (
                                    [string[]]$ApplicationPoolNames
                                )

                                # Import the web administration module
                                Import-Module WebAdministration

                                # Restart the application pool
                                $VerbosePreference = $using:VerbosePreference
                                Get-ChildItem -Path IIS:\AppPools |
                                    %{
                                        $appPool = $_.Name
                                        Get-ChildItem -Path ("IIS:\AppPools\" + $_.Name + "\WorkerProcesses") |
                                            Select-Object -Property @{Name="AppPool"; Expression={$appPool}},ProcessID,State,Handles,StartTime
                                    }
                            } -Verbose |
                Select-Object -Property @{Name="AppPool"; Expression={$_.AppPool}}, `
                                        @{Name="ComputerName"; Expression = {$_.PSComputerName}}, `
                                        @{Name="ProcessID"; Expression={$_.processid}}, `
                                        @{Name="State"; Expression={$_.state}}, `
                                        @{Name="Handles"; Expression={$_.handles}}, `
                                        @{Name="StartTime"; Expression={$_.starttime}}
            if($AppPool)
            {
                foreach($ap in $AppPool)
                {
                    $workerProcesses |
                        Where-Object {$_.AppPool -like $ap}
                }
            }
            else
            {
                $workerProcesses
            }
        }
    }

    End {}
}
#endregion
#region Search-IISSiteLog
workflow Search-IISSiteLog
{
    <#
    .Synopsis
       Parse log files.
    .DESCRIPTION
       Parse log files in folder or using the web site name.
    .EXAMPLE
        Search the log files for site "Default Web Site" of local server for pattern "401"
 
        Search-IISSiteLog -WebSite "Default Web Site" -Pattern "401"
    .EXAMPLE
        Search the files under the "c:\inetpub\logs\LogFiles\W3SVC1" of the local server for lines that match the pattern "404"
 
        Search-IISSiteLog -Path "c:\inetpub\logs\LogFiles\W3SVC1" -Pattern "404"
    .EXAMPLE
        Search the files in directory "c:\inetpub\logs\LogFiles\W3SVC1" that are last changed between 12/31/2017 and 7/1/2018 on webservers "webserver1" and "webserver2" for the regex pattern "40"
        
        Search-IISSiteLog -ComputerName webserver1,webserver2 -Path "c:\inetpub\logs\LogFiles\W3SVC1" -Pattern "40" -End "12/31/2017" -Start "7/1/2017"
    .NOTES
       This workflow requires remote PowerShell access to the servers and in case the website name is used, the WebAdministration module has to be available on the servers.
    .FUNCTIONALITY
       Log file parser.
    #>


    #region Parameters
    [CmdletBinding(DefaultParameterSetName='Parameter Set Website',
                  ConfirmImpact='Low')]

    Param
    (
        # The web server
        [Parameter(Mandatory=$false, 
                   Position=0,
                   ParameterSetName='Parameter Set Website')]
        [Parameter(Mandatory=$false, 
                   Position=0,
                   ParameterSetName='Parameter Set Folder')]
        [Alias("Server")]
        [string[]] 
        $ComputerName,

        # The web site
        [Parameter(Mandatory=$false, 
                   Position=1,
                   ParameterSetName='Parameter Set Website')]
        [string[]] 
        $WebSite,

        # The folder
        [Parameter(Mandatory=$false, 
                   Position=1,
                   ParameterSetName='Parameter Set Folder')]
        [string] 
        $Path,

        # The expression to match against
        [Parameter(Mandatory=$false, 
                   Position=2,
                   ParameterSetName='Parameter Set Website')]
        [Parameter(Mandatory=$false, 
                   Position=2,
                   ParameterSetName='Parameter Set Folder')]
        [string] 
        $Pattern,

        # The start time
        [Parameter(Mandatory=$false, 
                   Position=3,
                   ParameterSetName='Parameter Set Website')]
        [Parameter(Mandatory=$false, 
                   Position=3,
                   ParameterSetName='Parameter Set Folder')]
        [DateTime] 
        $Start,

        # The end time
        [Parameter(Mandatory=$false, 
                   Position=4,
                   ParameterSetName='Parameter Set Website')]
        [Parameter(Mandatory=$false, 
                   Position=4,
                   ParameterSetName='Parameter Set Folder')]
        [DateTime] 
        $End
    )
    #endregion

    #region configuration
    # Set the default value for the computername
    if(-not $ComputerName)
    {
        $ComputerName = $env:COMPUTERNAME
    }
    #endregion

    #region Permission Level

    # Check if the workflow was executed under administrator level permissions
    if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(`
        [Security.Principal.WindowsBuiltInRole] "Administrator"))
    {
        Write-Error "This cmdlet has to be executed with administrator rights."
        return
    }
    #endregion

    #region Set the mode of operation
    if($Path)
    {
        $mode = "Path"
        $location = $Path
    }
    else
    {
        $mode = "Site"
        $location = $WebSite
    }
    #endregion

    #region Create the configuration

    # A hashtable is created in order to be passed as a variable to the scriptblock
    $configuration = @{
                        Mode     = $mode 
                        Location = $location
                        Match    = $Pattern
                        Start    = $Start
                        End      = $End
                     }
    #endregion

    #region Process servers
    foreach -parallel ($c in $ComputerName)
    {
        Write-Verbose "Processing logs on server $c"
        inlineScript
        {
            Invoke-Command -ComputerName $using:c `
                           -ScriptBlock {

                                # The scriptblock paramters
                                Param($config)

                                # Create a variable to hold the files to be processed
                                $files = New-Object -TypeName System.Collections.ArrayList

                                #region Get the files to process
                                # Deferentiate uppon mode
                                if($config.Mode -eq "Path")
                                {
                                    # Get the files in the directory and add them to the collection
                                    Get-ChildItem -Path $config.Location -Force -File |
                                        %{
                                            $files.Add($_) |
                                                Out-Null
                                        }
                                }

                                if($config.Mode -eq "Site")
                                {
                                    # Load the IIS module
                                    Import-Module webadministration

                                    # Get the website
                                    Write-Warning "afsdf"
                                    $site = Get-Website |
                                                Where-Object {$_.Name -eq $config.Location}

                                    # Get the path to the log files
                                    if($site)
                                    {
                                        # Get the path
                                        $logPath = [System.Environment]::ExpandEnvironmentVariables($site.logFile.directory) + "\w3svc" + $site.id

                                        # Get the files
                                        Get-ChildItem -Path $logPath -Force -File |
                                            %{
                                                $files.Add($_) |
                                                    Out-Null
                                            }
                                    }
                                    else
                                    {
                                        continue
                                    }
                                }
                                #endregion

                                # Create the RegEx object
                                $regex = New-Object -TypeName system.text.regularexpressions.regex $config.Match

                                #region Process the files
                                foreach($f in $files)
                                {
                                    
                                    # Exclude files older than start time
                                    if($config.Start -ne [DateTime]::MinValue)
                                    {
                                        if($f.LastWriteTime -le $config.Start)
                                        {
                                            continue
                                        }
                                    }
                                    
                                    # Exclude files newer than end date
                                    if($config.End -ne [DateTime]::MinValue)
                                    {
                                        if($f.LastWriteTime -ge [DateTime]$config.End)
                                        {
                                            continue
                                        }
                                    }
                                    
                                    # Get the content of each file
                                    $content = Get-Content -Path $f.Fullname

                                    foreach($line in $content)
                                    {
                                        #region Pattern

                                        # Check if pattern is specified
                                        if($Config.Match -ne $null)
                                        {
                                            # Move to the next line if not matched
                                            if( -not $regex.IsMatch($line))
                                            {
                                                continue
                                            }
                                        }
                                        #endregion

                                        # Create the custom object
                                        $props = [ordered]@{
                                                            ComputerName = $env:COMPUTERNAME
                                                            File = $f
                                                            Entry = $line
                                                         }
                                        New-Object -TypeName PSObject -Property $props
                                    }
                                }
                                #endregion
                            } `
                        -ArgumentList $using:configuration
        }     
    }
    #endregion
}
#endregion
#endregion