PSIISHelper.psm1

#Region IsLocal

<#
.SYNOPSIS
    Test to see if the provided computer name is the local computer.
.DESCRIPTION
    This function tests to see if the provided computer name is the local computer.
.PARAMETER ComputerName
    The computer name to test.
.EXAMPLE
    IsLocal "MyComputer"

    Description
    -----------
    This function tests to see if the provided computer name is the local computer.
.NOTES
    Author: matthewjdegarmo
    GitHub: https://github.com/matthewjdegarmo
    Sponsor: https://github.com/sponsors/matthewjdegarmo
#>

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

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

    Process {
        Try {
            $IsLocal = $LocalValues | Foreach-Object {
                if ($_ -eq $ComputerName) {
                    Write-Output $true
                    break
                }
            }

            [bool]$IsLocal
        } 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.
.PARAMETER Credential
    The credentials to use for the connection. If not specified the connection will use the current user.
    You can provide a PSCredential object, or use `New-PSIISSession` to create a PSCredential object that lives for the current powershell session.

    See `Get-Help New-PSIISSession` for more details.
.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
.EXAMPLE
    Get-WebSiteBinding -ComputerName "MY_SERVER_NAME" -Port 1234 -Credential $PSCredential

    Description
    -----------
    Returns all the web site bindings for the specified server, using the specified credential.

.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 = '*',

        [PSCredential]$Credential = $script:PSIISCredential
    )

    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 {}
        }
        $ComputerName | Foreach-Object {
            Write-Verbose "Retrieving IIS information from $_"
            If (IsLocal $_) {
                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: $_"
                $InvokeCommandSplat = @{
                    ComputerName = $_
                    ScriptBlock = $ScriptBlock
                    ArgumentList = @($Port)
                }
                If ($null -ne $Credential) { $InvokeCommandSplat['Credential'] = $Credential }
                Invoke-Command @InvokeCommandSplat | Select-Object * -ExcludeProperty 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.
.PARAMETER Credential
    The credentials to use for the connection. If not specified the connection will use the current user.
    You can provide a PSCredential object, or use `New-PSIISSession` to create a PSCredential object that lives for the current powershell session.

    See `Get-Help New-PSIISSession` for more details.
.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 = '*',

        [PSCredential]$Credential = $script:PSIISCredential
    )

    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 {
                $InvokeCommandSplat = @{
                    ComputerName = $_
                    ScriptBlock = $ScriptBlock
                    ArgumentList = @($_, $State)
                }
                If ($null -ne $Credential) { $InvokeCommandSplat['Credential'] = $Credential }
                Invoke-Command @InvokeCommandSplat | 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.
.PARAMETER Credential
    The credentials to use for the connection. If not specified the connection will use the current user.
    You can provide a PSCredential object, or use `New-PSIISSession` to create a PSCredential object that lives for the current powershell session.

    See `Get-Help New-PSIISSession` for more details.
.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 = '*',

        [PSCredential]$Credential = $script:PSIISCredential
    )

    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 {
                $InvokeCommandSplat = @{
                    ComputerName = $_
                    ScriptBlock = $ScriptBlock
                    ArgumentList = @($Name, $State)
                }
                If ($null -ne $Credential) { $InvokeCommandSplat['Credential'] = $Credential }
                Invoke-Command @InvokeCommandSplat | Select-Object * -ExcludeProperty RunspaceID
            }
        }
    }

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

<#
.SYNOPSIS
    Create a new PSCredential to be used for the current session.
.DESCRIPTION
    This will store an environment credential for the current session for PSIISHelper commands.
.PARAMETER Credential
    The credentials to use for the connection. If not specified the connection will use the current user.
    You can provide a PSCredential object.
.PARAMETER PassThru
    If true, the credential will be passed through to the pipeline.
.EXAMPLE
    New-PSIISSession

    Description
    -----------
    Creates a new PSCredential object to be used for the current session.
.EXAMPLE
    New-PSIISSession -Credential $cred -PassThru

    Description
    -----------
    Passes a user-provided credential and creates a new PSCredential object to be used for the current session.
    The credential will be passed through to the pipeline using -PassThru.
.NOTES
    Author: matthewjdegarmo
    GitHub: https://github.com/matthewjdegarmo
    Sponsor: https://github.com/sponsors/matthewjdegarmo
#>

Function New-PSIISSession() {
    [CmdletBinding()]
    Param(
        [PSCredential]$Credential = (Get-Credential),
        [switch]$PassThru
    )

    Begin {}

    Process {
        Try {
            $script:PSIISCredential = $Credential
            If ($PassThru) {
                Write-Output $script:PSIISCredential
            }
        } Catch {
            Throw $_
        }
    }

    End {}
}
#EndRegion New-PSIISSession
#Region Remove-PSIISSession

<#
.SYNOPSIS
    This function is used to remove the session credential for PSIISHelper.
.DESCRIPTION
    This function is used to remove stored session credential that the PSIISHelper commands use.
.EXAMPLE
    Remove-PSIISSession

    Description
    -----------
    This function removes the stored session credential that the PSIISHelper commands use.
.NOTES
    Author: matthewjdegarmo
    GitHub: https://github.com/matthewjdegarmo
    Sponsor: https://github.com/sponsors/matthewjdegarmo
#>

Function Remove-PSIISSession() {
    [CmdletBinding()]
    Param()

    Begin {}

    Process {
        Try {
            Get-Variable PSIISCredential -ErrorAction SilentlyContinue | Remove-Variable
        } Catch {
            Throw $_
        }
    }

    End {}
}
#EndRegion Remove-PSIISSession
#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.
.PARAMETER Credential
    The credentials to use for the connection. If not specified the connection will use the current user.
    You can provide a PSCredential object, or use `New-PSIISSession` to create a PSCredential object that lives for the current powershell session.

    See `Get-Help New-PSIISSession` for more details.
.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,

        [PSCredential]$Credential = $script:PSIISCredential
    )

    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 {
                $InvokeCommandSplat = @{
                    ComputerName = $_
                    ScriptBlock = $ScriptBlock
                    ArgumentList = @($Pool, $PassThru)
                }
                If ($null -ne $Credential) { $InvokeCommandSplat['Credential'] = $Credential }
                Invoke-Command @InvokeCommandSplat | Select-Object * -ExcludeProperty RunspaceID
            }
        }
    }
}
#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.
.PARAMETER Credential
    The credentials to use for the connection. If not specified the connection will use the current user.
    You can provide a PSCredential object, or use `New-PSIISSession` to create a PSCredential object that lives for the current powershell session.

    See `Get-Help New-PSIISSession` for more details.
.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,

        [PSCredential]$Credential = $script:PSIISCredential
    )

    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 {
                    $InvokeCommandSplat = @{
                        ComputerName = $_
                        ScriptBlock = $ScriptBlock
                        ArgumentList = @($Site, $PassThru)
                    }
                    If ($null -ne $Credential) { $InvokeCommandSplat['Credential'] = $Credential }
                    Invoke-Command @InvokeCommandSplat | 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.
.PARAMETER Credential
    The credentials to use for the connection. If not specified the connection will use the current user.
    You can provide a PSCredential object, or use `New-PSIISSession` to create a PSCredential object that lives for the current powershell session.

    See `Get-Help New-PSIISSession` for more details.
.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,

        [PSCredential]$Credential = $script:PSIISCredential
    )

    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 {
                $InvokeCommandSplat = @{
                    ComputerName = $_
                    ScriptBlock = $ScriptBlock
                    ArgumentList = @($Pool, $PassThru)
                }
                If ($null -ne $Credential) { $InvokeCommandSplat['Credential'] = $Credential }
                Invoke-Command @InvokeCommandSplat | Select-Object * -ExcludeProperty RunspaceID
            }
        }
    }
}
#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.
.PARAMETER Credential
    The credentials to use for the connection. If not specified the connection will use the current user.
    You can provide a PSCredential object, or use `New-PSIISSession` to create a PSCredential object that lives for the current powershell session.

    See `Get-Help New-PSIISSession` for more details.
.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,

        [PSCredential]$Credential = $script:PSIISCredential
    )

    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 {
                    $InvokeCommandSplat = @{
                        ComputerName = $_
                        ScriptBlock = $ScriptBlock
                        ArgumentList = @($Site, $PassThru)
                    }
                    If ($null -ne $Credential) { $InvokeCommandSplat['Credential'] = $Credential }
                    Invoke-Command @InvokeCommandSplat | 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.
.PARAMETER Credential
    The credentials to use for the connection. If not specified the connection will use the current user.
    You can provide a PSCredential object, or use `New-PSIISSession` to create a PSCredential object that lives for the current powershell session.

    See `Get-Help New-PSIISSession` for more details.
.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,

        [PSCredential]$Credential = $script:PSIISCredential
    )

    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 {
                $InvokeCommandSplat = @{
                    ComputerName = $_
                    ScriptBlock = $ScriptBlock
                    ArgumentList = @($Pool, $PassThru)
                }
                If ($null -ne $Credential) { $InvokeCommandSplat['Credential'] = $Credential }
                Invoke-Command @InvokeCommandSplat | Select-Object * -ExcludeProperty RunspaceID
            }
        }
    }
}
#EndRegion Stop-PSIISPool