PSIISHelper.psm1

#Region IsLocal

<#
.SYNOPSIS

.DESCRIPTION

.NOTES
    Author: matthewjdegarmo
    GitHub: https://github.com/matthewjdegarmo
#>

Function IsLocal() {
    [CmdletBinding()]
    [OutputType([bool])]
    Param(
        [System.String]$ComputerName
    )

    Begin {
        $LocalValues = @(
            'localhost',
            '.',
            $env:COMPUTERNAME
        )
    }

    Process {
        Try {
            $LocalValues.Contains($ComputerName)
        } Catch {
            Throw $_
        }
    }

    End {}
}
#EndRegion IsLocal
#Region Get-PSIISBinding

<#
.SYNOPSIS
    Returns information about IIS web site bindings
.DESCRIPTION
    Takes a list of IIS servers and returns all the web site bindings

    Requires administrator permissions.
.PARAMETER ComputerName
    A string or string array of server names.
.PARAMETER Port
    The port number to use for the connection.
.EXAMPLE
    Get-WebSiteBinding MY_SERVER_NAME

    Description
    -----------
    Returns all the web site bindings for the specified server.
.EXAMPLE
    Get-WebSiteBinding MY_SERVER_NAME1, MY_SERVER_NAME2

    Description
    -----------
    Returns all the web site bindings for the specified servers.
.EXAMPLE
    "MY_SERVER_NAME" | Get-WebSiteBinding

    Description
    -----------
    Returns all the web site bindings for the specified server.
.EXAMPLE
    @("MY_SERVER_NAME1", "MY_SERVER_NAME2") | Get-WebSiteBinding

    Description
    -----------
    Returns all the web site bindings for the specified servers.
.EXAMPLE
    Get-Content myServerNames.txt | Get-WebSiteBinding

.NOTES
    Author: Matthewjdegarmo
    GitHub: https://github.com/matthewjdegarmo
    Sponsor: https://github.com/sponsors/matthewjdegarmo
#>

function Get-PSIISBinding() {
    [CmdletBinding()]
    Param (
        [Parameter(
            ValueFromPipeline
        )]
        [System.String[]] $ComputerName = $env:COMPUTERNAME,

        [Parameter()]
        [System.String] $Port = '*'
    )

    Begin {

        Write-Verbose -Message "Starting Get-PSIISBinding"
        
        $scriptBlock = {
            [CmdletBinding()]
            Param(
                [System.String] $Port
            )
            Import-Module WebAdministration;
            $sites = Get-ChildItem -path IIS:\Sites
            foreach ($Site in $sites) {
                foreach ($Bind in (Get-WebBinding $Site.Name)) {
                    foreach ($bindinfo in ($Bind | Select-Object -ExpandProperty bindingInformation)) {
                        $bindingInformation = @($bindinfo -split ':')
                        if ('*' -ne $Port) {
                            If ($Port -ne $bindingInformation[1]) {
                                continue
                            }
                        }
                        [pscustomobject]@{
                            Server          = $env:COMPUTERNAME
                            Sitename        = $Site.name
                            Id              = $Site.id
                            State           = $Site.State
                            PhysicalPath    = $Site.physicalPath
                            ApplicationPool = $Site.applicationPool
                            Protocol        = $Bind.Protocol
                            SslFlags        = $Bind.sslFlags
                            IpAddress       = $bindingInformation[0]
                            Port            = $bindingInformation[1]
                            HostName        = $bindingInformation[2]
                        }
                    }
                }
            }
        }
    }

    Process {
        Switch($Port) {
            'HTTP' {$Port = '80'}
            'HTTPS' {$Port = '443'}
            DEFAULT {}
        }
        Write-Verbose "Retrieving IIS information from $ComputerName"
        If (IsLocal $ComputerName) {
            Write-Verbose "$($MyInvocation.MyCommand.Name): Running on local computer: $env:COMPUTERNAME"
            & $scriptBlock -Port $Port -Verbose:$VerbosePreference | Select-Object -ExcludeProperty PSComputerName, RunspaceID, PSShowComputerName
        } Else {
            Write-Verbose "$($MyInvocation.MyCommand.Name): Running on remote computer: $COMPUTERNAME"
            Invoke-Command -ComputerName $ComputerName -ScriptBlock  $scriptBlock -ArgumentList $Port | Select-Object -ExcludeProperty PSComputerName, RunspaceID, PSShowComputerName
        }
    }
}
#EndRegion Get-PSIISBinding

#Region Get-PSIISPool

<#
.SYNOPSIS
    Get application pool information.
.DESCRIPTION
    Query one or multiple remote servers for their application pools and other information.
.PARAMETER ComputerName
    Specify a remote computer to run against.
.PARAMETER Name
    Specify the name of the Application Pool to search for.
.PARAMETER State
    Specify the state of the application pool to query.
.EXAMPLE
    PS> Get-PSIISPool -ComputerName some-remote-pc1

    Description
    -----------
    Query 'some-remote-pc1' for all started app pools.
.EXAMPLE
    PS> Get-PSIISPool -ComputerName WebServer01 -Name SiteAppPool_01

    Description
    -----------
    This will get the single app pool SiteAppPool_01 from WebServer01
.EXAMPLE
    PS> Get-PSIISPool -ComputerName WebServer01 -State Stopped

    Description
    -----------
    Get all stopped app pools from WebServer01
.EXAMPLE
    PS> Get-PSIISPool WebServer01,WebServer02 -State *

    Description
    -----------
    Search for all app pools (all states) from WebServer01 and WebServer02
.OUTPUTS
    [PSCustomObject]@{
        Name # Name of Application Pool
        State # State of Application Pool
        Applications # Site Names that are in this Application Pool
        PSComputerName # ComputerName that the Application Pool lives on.
    }
.NOTES
    Author: Matthew.DeGarmo
    Github: https://github.com/matthewjdegarmo
    Sponsor: https://github.com/sponsors/matthewjdegarmo
#>

Function Get-PSIISPool() {
    [CmdletBinding()]
    Param(
        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias('Server', 'PSComputerName')]
        [System.String[]] $ComputerName = $env:COMPUTERNAME,

        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias('ApplicationPool')]
        [System.String] $Name,

        [Parameter()]
        [ValidateSet('Started','Stopped', '*')]
        [System.String] $State = '*'
    )

    Begin {
        $OriginalFormatEnumerationLimit = $FormatEnumerationLimit
        $global:FormatEnumerationLimit = -1
    }

    Process {
        if ($_ -is [System.Object]) {
            $Pool = @{
                # The below If Else statements are version of these Turnary commands.
                # Windows PowerShell can't handle turnary operators. Leaving these here for reference to the below logic.
                
                # ComputerName = ($_.Server) ? $_.Server : (($_.ComputerName) ? $_.ComputerName : $_.PSComputerName)
                # Name = ($_.ApplicationPool) ? $_.ApplicationPool : $_.Name
            }

            If ($_.Server) {
                $Pool['ComputerName'] = $_.Server
            } Else {
                If ($_.ComputerName) {
                    $Pool['ComputerName'] = $_.ComputerName
                } Else {
                    $Pool['ComputerName'] = $_.PSComputerName
                }
            }

            If ($_.ApplicationPool) {
                $Pool['Name'] = $_.ApplicationPool
            } Else {
                $Pool['Name'] = $_.Name
            }
            
        } else {
            $Pool = @{
                ComputerName = $ComputerName
                Name         = $Name
            }
        }

        $ScriptBlock = {
            Param(
                $InputObject,
                [System.String]$State
            )
            Write-Verbose "$($COMPUTERNAME)`: Retrieving pool information..."
            Import-Module WebAdministration
            $Pools = Get-ChildItem 'IIS:\AppPools' | Where-Object { ($_.State -like $State) -and ($_.Name -match $InputObject.Name) }
            $Sites = Get-ChildItem 'IIS:\Sites'

            $Pools | Foreach-Object {
                $Pool = $_
                [PSCustomObject] @{
                    Name = $Pool.Name
                    State = $Pool.State
                    Applications = ($Sites | Where-Object {$_.applicationPool -eq $Pool.Name}).Name
                    ComputerName = $env:COMPUTERNAME
                }
            }
        }

        $Pool.ComputerName | Foreach-Object {
            If (IsLocal $_) {
                & $ScriptBlock -InputObject $Pool -State $State
            } Else {
                Invoke-Command -ComputerName $_ -ScriptBlock $ScriptBlock -ArgumentList $_, $State | Select-Object * -ExcludeProperty RunspaceID
            }
        }
    }

    End {
        $global:FormatEnumerationLimit = $OriginalFormatEnumerationLimit
    }
}
#EndRegion Get-PSIISPool
#Region Get-PSIISSite

<#
.SYNOPSIS
    Get IIS Site information.
.DESCRIPTION
    Get IIS Site information.
.PARAMETER ComputerName
    Specify a remote computer to run against.
.PARAMETER Name
    Specify the name of the Application Pool to search for.
.PARAMETER State
    Specify the state of the application pool to query.
.EXAMPLE
    Get-PSIISSite -ComputerName "localhost" -Name "DefaultSite"
.NOTES
    Author: matthewjdegarmo
    GitHub: https://github.com/matthewjdegarmo
#>

Function Get-PSIISSite() {
    [CmdletBinding()]
    Param(
        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias('Server', 'PSComputerName')]
        [System.String[]] $ComputerName = $env:COMPUTERNAME,

        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias('Site')]
        [System.String] $Name,

        [Parameter()]
        [ValidateSet('Started','Stopped', '*')]
        [System.String] $State = '*'
    )

    Begin {
        $OriginalFormatEnumerationLimit = $FormatEnumerationLimit
        $global:FormatEnumerationLimit = -1
    }

    Process {

        $ScriptBlock = {
            Param(
                [System.String]$Name,
                [System.String]$State
            )
            Write-Verbose "$($COMPUTERNAME)`: Retrieving Site information..."
            Import-Module WebAdministration
            $WhereList = New-Object System.Collections.ArrayList
            $Where = $null

            If ($Name) {[void]$WhereList.Add('$_.Name -like $Name')}
            If ($State) {[void]$WhereList.Add('$_.State -like $State')}
            $Where = [scriptblock]::Create($WhereList -join " -and ")

            Get-ChildItem 'IIS:\Sites' | Where-Object $Where | Foreach-Object {
                [PSCustomObject] @{
                    Name = $_.Name
                    State = $_.State
                    ApplicationPool = $_.ApplicationPool
                    ComputerName = $env:COMPUTERNAME
                }
            }
        }

        $ComputerName | Foreach-Object {
            If (IsLocal $_) {
                & $ScriptBlock -Name $Name -State $State
            } Else {
                Invoke-Command -ComputerName $_ -ScriptBlock $ScriptBlock -ArgumentList $Name, $State | Select-Object * -ExcludeProperty RunspaceID
            }
        }
    }

    End {
        $global:FormatEnumerationLimit = $OriginalFormatEnumerationLimit
    }
}
#EndRegion Get-PSIISSite
#Region Restart-PSIISPool

<#
.SYNOPSIS
    Restart an application pool.
.DESCRIPTION
    Supply the web server and application pool to recycle.
.PARAMETER ComputerName
    Specify the remote server to run against.
.PARAMETER Name
    Specify the pool name to recycle.
.PARAMETER Sites
    Specify the site names that are tied to this pool. This parameter is meant to support Pipeline values, but is not required to specify manually.
    Example:

    $Pool | Restart-PSIISPool # $Pool will have Sites information and pass it through the pipeline.
    Restart-PSIISPool -ComputerName Server1 -Name Pool1 # No site information will be included..
.PARAMETER PassThru
    If true, the command will return the IIS information.
.EXAMPLE
    PS> Restart-PSIISPool -ComputerName WebServer01 -Name DefaultSitePool

    Description
    -----------
    This will recycle the DefaultSitePool pool on WebServer01.
.EXAMPLE
    PS> Get-AppPool -ComputerName WebServer01 -Name DefaultSitePool | Restart-PSIISPool

    Description
    -----------
    This will recycle the DefaultSitePool pool on WebServer01.
.EXAMPLE
    PS> Get-AppPool -ComputerName WebServer01,WebServer02 | Restart-PSIISPool

    Description
    -----------
    CAUTION: This will recycle ALL app pools on WebServer01 and WebServer02.
.EXAMPLE
    PS> Get-WebsiteInformation url.matthewjdegarmo.com | Restart-PSIISPool

    Description
    -----------
    If url.matthewjdegarmo.com is found, this will prompt to recycle the app pool it is using.
    You should only do this if you KNOW that this is the only site on the found Application Pool.
.NOTES
    Author: Matthew.DeGarmo
    Handle: @matthewjdegarmo
#>

Function Restart-PSIISPool() {
    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = "High"
    )]
    Param(
        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias('Server', 'PSComputerName')]
        [System.String[]] $ComputerName = $env:COMPUTERNAME,

        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias('ApplicationPool')]
        [System.String] $Name,

        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias('Sitename', 'Applications')]
        [System.String[]] $Sites,

        [switch]$PassThru
    )

    Begin {}

    Process {
        #Region Dynamic Pipeline handling
        if ($_ -is [System.Object]) {
            $Pool = @{
                # The below If Else statements are version of these Turnary commands.
                # Windows PowerShell can't handle turnary operators. Leaving these here for reference to the below logic.

                # ComputerName = ($_.Server) ? $_.Server : (($_.ComputerName) ? $_.ComputerName : $_.PSComputerName).ToUpper()
                # Name = ($_.ApplicationPool) ? $_.ApplicationPool : $_.Name
                # Sites = ($_.SiteName) ? $_.SiteName : (($_.Applications) ? $_.Applications : $_.Sites)
            }
            If ($_.Server) {
                $Pool['ComputerName'] = $_.Server
            } Else {
                If ($_.ComputerName) {
                    $Pool['ComputerName'] = $_.ComputerName
                } Else {
                    $Pool['ComputerName'] = $_.PSComputerName
                }
            }
            If ($_.ApplicationPool) {
                $Pool['Name'] = $_.ApplicationPool
            } Else {
                $Pool['Name'] = $_.Name
            }

            If ($_.SiteName) {
                $Pool['Sites'] = $_.SiteName
            } Else {
                If ($_.Applications) {
                    $Pool['Sites'] = $_.Applications
                } Else {
                    $Pool['Sites'] = $_.Sites
                }
            }
        }
        else {
            $Pool = @{
                ComputerName = $ComputerName.ToUpper()
                Name         = $Name
            }
            If ($Sites) {
                $Pool['Sites'] = $Sites.ToUpper()
            }
            else {
                $Pool['Sites'] = (Get-PSIISPool -ComputerName $Pool.ComputerName -Name $Pool.Name).Applications
            }
        }
        #EndRegion Dynamic Pipeline handling

        if ($PSCmdlet.ShouldProcess($Pool.ComputerName, "Recycle $($Pool.Name) pool containing sites: $($Pool.Sites)")) {
            $ScriptBlock = {
                [CmdletBinding()]
                Param(
                    $Pool,
                    [switch]$PassThru
                )
                Write-Verbose "$($Pool.ComputerName): Recycling Pool: $($Pool.Name)"
                Import-Module WebAdministration
                
                Try  {
                    Restart-WebAppPool -Name $Pool.Name
                } Catch {
                    $_
                }
                If ($PassThru) {
                    Get-PSIISPool -Name $Pool.Name -State 'Started'
                }
            }

            If (IsLocal $Pool.ComputerName) {
                & $ScriptBlock -Pool $Pool -PassThru:$PassThru
            } Else {
                Invoke-Command -ComputerName $Pool.ComputerName -ScriptBlock $ScriptBlock -ArgumentList $Pool, $PassThru
            }
        }
    }
}
#EndRegion Restart-PSIISPool
#Region Restart-PSIISSite

<#
.SYNOPSIS
    Restart a Website.
.DESCRIPTION
    Supply the web server and website to restart.
.PARAMETER ComputerName
    Specify the remote server to run against.
.PARAMETER Name
    Specify the website name to restart.
.PARAMETER PassThru
    If true, the command will return the IIS information.
.EXAMPLE
    PS> Restart-PSIISSite -ComputerName WebServer01 -Name DefaultSite

    Description
    -----------
    This will restart the website DefaultSite on WebServer01
.NOTES
    Author: Matthew.DeGarmo
    Github: https://github.com/matthewjdegarmo
    Sponsor: https://github.com/sponsors/matthewjdegarmo
#>

Function Restart-PSIISSite() {
    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact="High"
    )]
    Param(
        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias('Server','PSComputerName')]
        [System.String[]] $ComputerName = $env:COMPUTERNAME,
        
        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias('Sitename')]
        [System.String] $Name,

        [switch]$PassThru
    )

    Begin {}

    Process {
        if ($_ -is [System.Object]) {
            $Site = @{
                # The below If Else statements are version of these Turnary commands.
                # Windows PowerShell can't handle turnary operators. Leaving these here for reference to the below logic.
                
                # ComputerName = ($_.Server) ? $_.Server : (($_.ComputerName) ? $_.ComputerName : $_.PSComputerName)
                # Name = ($_.Sitename) ? $_.Sitename : $_.Name
            }

            If ($_.Server) {
                $Site['ComputerName'] = $_.Server
            } Else {
                If ($_.ComputerName) {
                    $Site['ComputerName'] = $_.ComputerName
                } Else {
                    $Site['ComputerName'] = $_.PSComputerName
                }
            }

            If ($_.Sitename) {
                $Site['Name'] = $_.Sitename
            } Else {
                $Site['Name'] = $_.Name
            }
        } else {
            $Site = @{
                ComputerName = $ComputerName
                Name         = $Name
            }
        }

        if ($PSCmdlet.ShouldProcess($Site.ComputerName, "Restart site: $($Site.Name)")) {
            $ScriptBlock = {
                [CmdletBinding()]
                Param(
                    $Site,
                    [switch]$PassThru
                )
                Import-Module WebAdministration
                Stop-Website -Name $Site.Name -ErrorAction SilentlyContinue
                Start-Website -Name $Site.Name -ErrorAction SilentlyContinue -PassThru:$PassThru
            }
            $Site.ComputerName | FOreach-Object {
                If (IsLocal $_) {
                    & $ScriptBlock -Site $Site -PassThru:$PassThru
                } Else {
                    Invoke-Command -ComputerName $_ -ScriptBlock $ScriptBlock -ArgumentList $Site, $PassThru | Select-Object -ExcludeProperty RunspaceId
                }
            }
        }
    }
}
#EndRegion Restart-PSIISSite
#Region Start-PSIISPool

<#
.SYNOPSIS
    Restart an application pool.
.DESCRIPTION
    Supply the web server and application pool to recycle.
.PARAMETER ComputerName
    Specify the remote server to run against.
.PARAMETER Name
    Specify the pool name to recycle.
.PARAMETER Sites
    Specify the site names that are tied to this pool. This parameter is meant to support Pipeline values, but is not required to specify manually.
    Example:

    $Pool | Restart-PSIISPool # $Pool will have Sites information and pass it through the pipeline.
    Restart-PSIISPool -ComputerName Server1 -Name Pool1 # No site information will be included..
.PARAMETER PassThru
    If true, the command will return the IIS information.
.EXAMPLE
    PS> Restart-PSIISPool -ComputerName WebServer01 -Name DefaultSitePool

    Description
    -----------
    This will recycle the DefaultSitePool pool on WebServer01.
.EXAMPLE
    PS> Get-AppPool -ComputerName WebServer01 -Name DefaultSitePool | Restart-PSIISPool

    Description
    -----------
    This will recycle the DefaultSitePool pool on WebServer01.
.EXAMPLE
    PS> Get-AppPool -ComputerName WebServer01,WebServer02 | Restart-PSIISPool

    Description
    -----------
    CAUTION: This will recycle ALL app pools on WebServer01 and WebServer02.
.EXAMPLE
    PS> Get-WebsiteInformation url.matthewjdegarmo.com | Restart-PSIISPool

    Description
    -----------
    If url.matthewjdegarmo.com is found, this will prompt to recycle the app pool it is using.
    You should only do this if you KNOW that this is the only site on the found Application Pool.
.NOTES
    Author: matthewjdegarmo
    GitHub: https://github.com/matthewjdegarmo
    Sponsor: https://github.com/sponsors/matthewjdegarmo
#>

Function Start-PSIISPool() {
    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = "High"
    )]
    Param(
        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias('Server', 'PSComputerName')]
        [System.String[]] $ComputerName = $env:COMPUTERNAME,

        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias('ApplicationPool')]
        [System.String] $Name,

        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias('Sitename', 'Applications')]
        [System.String[]] $Sites,

        [switch]$PassThru
    )

    Begin {}

    Process {
        #Region Dynamic Pipeline handling
        if ($_ -is [System.Object]) {
            $Pool = @{
                # The below If Else statements are version of these Turnary commands.
                # Windows PowerShell can't handle turnary operators. Leaving these here for reference to the below logic.

                # ComputerName = ($_.Server) ? $_.Server : (($_.ComputerName) ? $_.ComputerName : $_.PSComputerName).ToUpper()
                # Name = ($_.ApplicationPool) ? $_.ApplicationPool : $_.Name
                # Sites = ($_.SiteName) ? $_.SiteName : (($_.Applications) ? $_.Applications : $_.Sites)
            }
            If ($_.Server) {
                $Pool['ComputerName'] = $_.Server
            } Else {
                If ($_.ComputerName) {
                    $Pool['ComputerName'] = $_.ComputerName
                } Else {
                    $Pool['ComputerName'] = $_.PSComputerName
                }
            }
            If ($_.ApplicationPool) {
                $Pool['Name'] = $_.ApplicationPool
            } Else {
                $Pool['Name'] = $_.Name
            }

            If ($_.SiteName) {
                $Pool['Sites'] = $_.SiteName
            } Else {
                If ($_.Applications) {
                    $Pool['Sites'] = $_.Applications
                } Else {
                    $Pool['Sites'] = $_.Sites
                }
            }
        }
        else {
            $Pool = @{
                ComputerName = $ComputerName.ToUpper()
                Name         = $Name
            }
            If ($Sites) {
                $Pool['Sites'] = $Sites.ToUpper()
            }
            else {
                $Pool['Sites'] = (Get-PSIISPool -ComputerName $Pool.ComputerName -Name $Pool.Name).Applications
            }
        }
        #EndRegion Dynamic Pipeline handling

        if ($PSCmdlet.ShouldProcess($Pool.ComputerName, "Start $($Pool.Name) pool containing sites: $($Pool.Sites)")) {
            $ScriptBlock = {
                [CmdletBinding()]
                Param(
                    $Pool,
                    [switch]$PassThru
                )
                Write-Verbose "$($Pool.ComputerName): Starting Pool: $($Pool.Name)"
                Import-Module WebAdministration
                Start-WebAppPool -Name $Pool.Name -ErrorAction SilentlyContinue -PassThru:$PassThru
            }

            If (IsLocal $Pool.ComputerName) {
                & $ScriptBlock -Pool $Pool -PassThru:$PassThru
            } Else {
                Invoke-Command -ComputerName $Pool.ComputerName -ScriptBlock $ScriptBlock -ArgumentList $Pool, $PassThru
            }
        }
    }
}
#EndRegion Start-PSIISPool
#Region Start-PSIISSite

<#
.SYNOPSIS
    Start an IIS Site.
.DESCRIPTION
    Start an IIS Site.
.PARAMETER ComputerName
    Specify a remote computer to run against.
.PARAMETER Name
    Specify the name of the IIS Site to search for.
.PARAMETER PassThru
    If true, the command will return the IIS information.
.EXAMPLE
    Start-PSIISSite -ComputerName localhost -Name MySite
.NOTES
    Author: matthewjdegarmo
    GitHub: https://github.com/matthewjdegarmo
    Sponsor: https://github.com/sponsors/matthewjdegarmo
#>

Function Start-PSIISSite() {
    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact="High"
    )]
    Param(
        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias('Server','PSComputerName')]
        [System.String[]] $ComputerName = $env:COMPUTERNAME,
        
        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias('Sitename')]
        [System.String] $Name,

        [switch]$PassThru
    )

    Begin {}

    Process {
        if ($_ -is [System.Object]) {
            $Site = @{
                # The below If Else statements are version of these Turnary commands.
                # Windows PowerShell can't handle turnary operators. Leaving these here for reference to the below logic.
                
                # ComputerName = ($_.Server) ? $_.Server : (($_.ComputerName) ? $_.ComputerName : $_.PSComputerName)
                # Name = ($_.Sitename) ? $_.Sitename : $_.Name
            }

            If ($_.Server) {
                $Site['ComputerName'] = $_.Server
            } Else {
                If ($_.ComputerName) {
                    $Site['ComputerName'] = $_.ComputerName
                } Else {
                    $Site['ComputerName'] = $_.PSComputerName
                }
            }

            If ($_.Sitename) {
                $Site['Name'] = $_.Sitename
            } Else {
                $Site['Name'] = $_.Name
            }
        } else {
            $Site = @{
                ComputerName = $ComputerName
                Name         = $Name
            }
        }

        if ($PSCmdlet.ShouldProcess($Site.ComputerName, "Start site: $($Site.Name)")) {
            $ScriptBlock = {
                [CmdletBinding()]
                Param(
                    $Site,
                    [switch]$PassThru
                )
                Import-Module WebAdministration
                Start-Website -Name $Site.Name -ErrorAction SilentlyContinue -PassThru:$PassThru
            }
            $Site.ComputerName | Foreach-Object {
                If (IsLocal $_) {
                    & $ScriptBlock -Site $Site -PassThru:$PassThru
                } Else {
                    Invoke-Command -ComputerName $_ -ScriptBlock $ScriptBlock -ArgumentList $Site, $PassThru | Select-Object -ExcludeProperty RunspaceId
                }
            }
        }
    }
}
#EndRegion Start-PSIISSite
#Region Stop-PSIISPool

<#
.SYNOPSIS
    Stop an application pool.
.DESCRIPTION
    Supply the web server and application pool to recycle.
.PARAMETER ComputerName
    Specify the remote server to run against.
.PARAMETER Name
    Specify the pool name to recycle.
.PARAMETER Sites
    Specify the site names that are tied to this pool. This parameter is meant to support Pipeline values, but is not required to specify manually.
    Example:

    $Pool | Stop-PSIISPool # $Pool will have Sites information and pass it through the pipeline.
    Stop-PSIISPool -ComputerName Server1 -Name Pool1 # No site information will be included..
.PARAMETER PassThru
    If true, the command will return the IIS information.
.EXAMPLE
    PS> Stop-PSIISPool -ComputerName WebServer01 -Name DefaultSitePool

    Description
    -----------
    This will recycle the DefaultSitePool pool on WebServer01.
.EXAMPLE
    PS> Get-AppPool -ComputerName WebServer01 -Name DefaultSitePool | Stop-PSIISPool

    Description
    -----------
    This will recycle the DefaultSitePool pool on WebServer01.
.EXAMPLE
    PS> Get-AppPool -ComputerName WebServer01,WebServer02 | Stop-PSIISPool

    Description
    -----------
    CAUTION: This will recycle ALL app pools on WebServer01 and WebServer02.
.EXAMPLE
    PS> Get-WebsiteInformation url.matthewjdegarmo.com | Stop-PSIISPool

    Description
    -----------
    If url.matthewjdegarmo.com is found, this will prompt to recycle the app pool it is using.
    You should only do this if you KNOW that this is the only site on the found Application Pool.
.NOTES
    Author: matthewjdegarmo
    GitHub: https://github.com/matthewjdegarmo
    Sponsor: https://github.com/sponsors/matthewjdegarmo
#>

Function Stop-PSIISPool() {
    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = "High"
    )]
    Param(
        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias('Server', 'PSComputerName')]
        [System.String[]] $ComputerName = $env:COMPUTERNAME,

        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias('ApplicationPool')]
        [System.String] $Name,

        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias('Sitename', 'Applications')]
        [System.String[]] $Sites,

        [switch]$PassThru
    )

    Begin {}

    Process {
        #Region Dynamic Pipeline handling
        if ($_ -is [System.Object]) {
            $Pool = @{
                # The below If Else statements are version of these Turnary commands.
                # Windows PowerShell can't handle turnary operators. Leaving these here for reference to the below logic.

                # ComputerName = ($_.Server) ? $_.Server : (($_.ComputerName) ? $_.ComputerName : $_.PSComputerName).ToUpper()
                # Name = ($_.ApplicationPool) ? $_.ApplicationPool : $_.Name
                # Sites = ($_.SiteName) ? $_.SiteName : (($_.Applications) ? $_.Applications : $_.Sites)
            }
            If ($_.Server) {
                $Pool['ComputerName'] = $_.Server
            } Else {
                If ($_.ComputerName) {
                    $Pool['ComputerName'] = $_.ComputerName
                } Else {
                    $Pool['ComputerName'] = $_.PSComputerName
                }
            }
            If ($_.ApplicationPool) {
                $Pool['Name'] = $_.ApplicationPool
            } Else {
                $Pool['Name'] = $_.Name
            }

            If ($_.SiteName) {
                $Pool['Sites'] = $_.SiteName
            } Else {
                If ($_.Applications) {
                    $Pool['Sites'] = $_.Applications
                } Else {
                    $Pool['Sites'] = $_.Sites
                }
            }
        }
        else {
            $Pool = @{
                ComputerName = $ComputerName.ToUpper()
                Name         = $Name
            }
            If ($Sites) {
                $Pool['Sites'] = $Sites.ToUpper()
            }
            else {
                $Pool['Sites'] = (Get-PSIISPool -ComputerName $Pool.ComputerName -Name $Pool.Name).Applications
            }
        }
        #EndRegion Dynamic Pipeline handling

        if ($PSCmdlet.ShouldProcess($Pool.ComputerName, "Stop $($Pool.Name) pool containing sites: $($Pool.Sites)")) {
            $ScriptBlock = {
                [CmdletBinding()]
                Param(
                    $Pool,
                    [switch]$PassThru
                )
                Write-Verbose "$($Pool.ComputerName): Stopping Pool: $($Pool.Name)"
                Import-Module WebAdministration
                Stop-WebAppPool -Name $Pool.Name -ErrorAction SilentlyContinue -PassThru:$PassThru
                # Get-PSIISPool -Name $Pool.Name -State 'Stopped'
            }

            If (IsLocal $Pool.ComputerName) {
                & $ScriptBlock -Pool $Pool -PassThru:$PassThru
            } Else {
                Invoke-Command -ComputerName $Pool.ComputerName -ScriptBlock $ScriptBlock -ArgumentList $Pool, $PassThru
            }
        }
    }
}
#EndRegion Stop-PSIISPool