IISRFBaseline.psm1

function Invoke-IISRFBaseline
{
    <#
        .SYNOPSIS
         
        Invokes the IIS Request Filtering Baseline module.
 
        .DESCRIPTION
 
        Builds Logparser query files based on input parameters and stores them in the WorkingDirectory as lp_query_RF-Setting.sql. Logparser query files are then launched with the results stored in the WorkingDirectory as lp_results_RF-Setting.csv. Output files are in CSV allowing easy import to spreadsheets, scripting languages, or charting apps. .md files correspond to each setting providing details.
 
        .EXAMPLE
         
        Invoke-IISRFBaseline -WorkingDirectory 'D:\WorkingFolder\' -IISLogPath 'D:\inetpub\Logfiles\logs\' -WebsiteContentPath '\\server.contoso.com\website\' -Sitename 'W3SVC3'
 
        .EXAMPLE
 
        Invoke-IISRFBaseline -LogparserPath 'C:\Program Files (x86)\Log Parser 2.2\' -WorkingDirectory 'D:\WorkingFolder\' -IISLogPath 'D:\inetpub\Logfiles\logs\' -WebsiteContentPath '\\server.contoso.com\website\' -Sitename 'W3SVC3'
 
        .INPUTS
 
        None.
 
        .LINK
 
        https://github.com/phbits/IISRFBaseline
    #>


    [CmdletBinding()]
    [OutputType('System.IO.File')]
    param(
        [parameter(Mandatory=$true)]
        [ValidateScript({Test-Path -LiteralPath $_})]
        [System.String]
        # Path to working directory (Read/Write Access).
        $WorkingDirectory
        ,
        [parameter(Mandatory=$true)]
        [System.String]
        # Path to IIS logs (Read Access).
        $IISLogPath
        ,
        [parameter(Mandatory=$true)]
        [System.String]
        # Path to Website Content (Read Access)
        $WebsiteContentPath
        ,
        [parameter(Mandatory=$true)]
        [System.String]
        # IIS Website Sitename (Example: W3SVC). See FAQ for details.
        $Sitename
        ,
        [parameter(Mandatory=$false)]
        [Switch]
        # Exclude HTTP response codes 300, 301, and 302 from results.
        $ExcludeRedirects
        ,
        [parameter(Mandatory=$false)]
        [System.String]
        # Folder where Logparser.exe and Logparser.dll resides (Read/Exec).
        $LogparserPath
    )

    Write-Verbose -Message 'Begin input validation'

    $InputOK = $false

    if(Validate-WorkingDirectory -WorkingDir $WorkingDirectory)
    {
        if(Validate-LogParser -Path $LogparserPath)
        {
            if(Validate-Sitename -Sitename $Sitename)
            {
                if(Validate-WebsiteContentPath -Path $WebsiteContentPath)
                {
                    $InputOK = Validate-IISLogs -Path $IISLogPath
                }
            }
        }
    }

    if($InputOK)
    {
        Write-Verbose -Message 'Input validation succeeded.'

        Write-Verbose -Message 'Variables set:'

        if($PSCmdlet.MyInvocation.BoundParameters['Verbose'].IsPresent)
        {
            $GlobalVariables = Get-Variable -Name "IISRFB_*" | Format-Table -AutoSize | Out-String

            [string[]]$GlobalVariablesArray = $GlobalVariables.Split([System.Environment]::NewLine,[System.StringSplitOptions]::RemoveEmptyEntries)

            foreach($item in $GlobalVariablesArray)
            {
                Write-Verbose -Message " $($item.TrimEnd())"
            }
        }
        
        [int]$global:IISRFB_MaxStatusCode = 303

        if($ExcludeRedirects -eq $true){ $global:IISRFB_MaxStatusCode = 300 }

        $n = 0

        do {

            $n++
            
            switch($n)
            {
                1 {
                    Write-Verbose -Message 'Building allowDoubleEscaping query file.'

                    $QueryFile = New-RFLpFileAllowDoubleEscaping -Sitename $global:IISRFB_Sitename -LogDir $global:IISRFB_IISLogPath -OutputDir $global:IISRFB_WorkingDirectory
                    
                    Write-Verbose -Message 'Building allowDoubleEscaping results file.'

                    & $global:IISRFB_LogparserPath -stats:OFF -q:ON -i:IISW3C -o:CSV file:$($QueryFile)
                }

                2 { 
                    Write-Verbose -Message 'Building allowHighBitCharacters-FS query file.'
                
                    $QueryFile = New-RFLpFileAllowHighBitCharactersFS -ContentDir $global:IISRFB_WebsiteContentPath -OutputDir $global:IISRFB_WorkingDirectory

                    Write-Verbose -Message 'Building allowHighBitCharacters-FS results file.'

                    & $global:IISRFB_LogparserPath -stats:OFF -q:ON -i:FS -preserveLastAccTime:ON -o:CSV file:$($QueryFile)
                }

                3 { 
                    Write-Verbose -Message 'Building allowHighBitCharacters-IIS query file.'
                
                    $QueryFile = New-RFLpFileAllowHighBitCharactersIIS -Sitename $global:IISRFB_Sitename -LogDir $global:IISRFB_IISLogPath -OutputDir $global:IISRFB_WorkingDirectory
                
                    Write-Verbose -Message 'Building allowHighBitCharacters-IIS results file.'
                    
                    & $global:IISRFB_LogparserPath -stats:OFF -q:ON -i:IISW3C -o:CSV file:$($QueryFile)
                }

                4 { 
                    Write-Verbose -Message 'Building fileExtensions-FS query file.'

                    $QueryFile = New-RFLpFileFileExtensionsFS -ContentDir $global:IISRFB_WebsiteContentPath -OutputDir $global:IISRFB_WorkingDirectory
                
                    Write-Verbose -Message 'Building fileExtensions-FS results file.'
                    
                    & $global:IISRFB_LogparserPath -stats:OFF -q:ON -i:FS -preserveLastAccTime:ON -o:CSV file:$($QueryFile)
                }

                5 { 
                    Write-Verbose -Message 'Building fileExtensions-IIS query file.'

                    $QueryFile = New-RFLpFileFileExtensionsIIS -Sitename $global:IISRFB_Sitename `
                                                                -LogDir $global:IISRFB_IISLogPath `
                                                                -OutputDir $global:IISRFB_WorkingDirectory `
                                                                -MaxHttp $global:IISRFB_MaxStatusCode
                
                    Write-Verbose -Message 'Building fileExtensions-IIS results file.'
                    
                    & $global:IISRFB_LogparserPath -stats:OFF -q:ON -i:IISW3C -o:CSV file:$($QueryFile)
                }

                6 { 
                    Write-Verbose -Message 'Building maxAllowedContentLength query file.'

                    $QueryFile = New-RFLpFileMaxAllowedContentLength -Sitename $global:IISRFB_Sitename `
                                                                    -LogDir $global:IISRFB_IISLogPath `
                                                                    -OutputDir $global:IISRFB_WorkingDirectory `
                                                                    -MaxHttp $global:IISRFB_MaxStatusCode

                    Write-Verbose -Message 'Building maxAllowedContentLength results file.'
                    
                    & $global:IISRFB_LogparserPath -stats:OFF -q:ON -i:IISW3C -o:CSV file:$($QueryFile)
                }

                7 { 
                    Write-Verbose -Message 'Building maxQueryString query file.'

                    $QueryFile = New-RFLpFileMaxQueryString    -Sitename $global:IISRFB_Sitename `
                                                                -LogDir $global:IISRFB_IISLogPath `
                                                                -OutputDir $global:IISRFB_WorkingDirectory `
                                                                -MaxHttp $global:IISRFB_MaxStatusCode

                    Write-Verbose -Message 'Building maxQueryString results file.'

                    & $global:IISRFB_LogparserPath -stats:OFF -q:ON -i:IISW3C -o:CSV file:$($QueryFile)
                }

                8 { 
                    Write-Verbose -Message 'Building maxUrl-FS query file.'

                    $QueryFile = New-RFLpFileMaxUrlFS -ContentDir $global:IISRFB_WebsiteContentPath -OutputDir $global:IISRFB_WorkingDirectory
                
                    Write-Verbose -Message 'Building maxUrl-FS results file.'

                    & $global:IISRFB_LogparserPath -stats:OFF -q:ON -i:FS -preserveLastAccTime:ON -o:CSV file:$($QueryFile)
                }

                9 { 
                    Write-Verbose -Message 'Building maxUrl-IIS query file.'

                    $QueryFile = New-RFLpFileMaxUrlIIS -Sitename $global:IISRFB_Sitename `
                                                        -LogDir $global:IISRFB_IISLogPath `
                                                        -OutputDir $global:IISRFB_WorkingDirectory `
                                                        -MaxHttp $global:IISRFB_MaxStatusCode

                    Write-Verbose -Message 'Building maxUrl-IIS results file.'

                    & $global:IISRFB_LogparserPath -stats:OFF -q:ON -i:IISW3C -o:CSV file:$($QueryFile)
                }

                10 {
                    Write-Verbose -Message 'Building verbs query file.'

                    $QueryFile = New-RFLpFileVerbs -Sitename $global:IISRFB_Sitename `
                                                    -LogDir $global:IISRFB_IISLogPath `
                                                    -OutputDir $global:IISRFB_WorkingDirectory `
                                                    -MaxHttp $global:IISRFB_MaxStatusCode

                    Write-Verbose -Message 'Building verbs results file.'

                    & $global:IISRFB_LogparserPath -stats:OFF -q:ON -i:IISW3C -o:CSV file:$($QueryFile)
                }

                default { 
                    Write-Verbose -Message "Writing output files to console."
                    
                    $lpFiles = Get-ChildItem -LiteralPath $global:IISRFB_WorkingDirectory -Filter "lp_*" -File

                    Write-Host "LogParser Query Files:" -fore Green
                    $lpFiles | ?{ $_.Name.ToString().StartsWith('lp_query_') -eq $true } | %{ Write-Host "`t$($_.FullName)"}

                    Write-Host "Result Files:" -fore Green
                    $lpFiles | ?{ $_.Name.ToString().StartsWith('lp_results_') -eq $true } | %{ Write-Host "`t$($_.FullName)"}

                    Write-Host "IISRFBaseline finished." -fore Green

                    $n = 0 
                }
            }

        } while($n -ne 0)

    } else {
    
        Write-Verbose -Message 'Input validation failed.'
    }

} # End function Invoke-IISRFBaseline

function Validate-Sitename
{
    <#
        .SYNOPSIS
        Validates IIS Website Sitename.
    #>


    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true)]
        [System.String]
        # IIS Website Sitename
        $Sitename
    )

    Write-Verbose -Message "Begin function Validate-Sitename."

    if($Sitename -notmatch "^(?i)(w3svc)[0-9]{1,12}$")
    {
        Write-Error -Message "Invalid Sitename." `
                    -RecommendedAction 'Example: W3SVC99' -Category NotSpecified -ErrorId 120
        return $false
    
    } else {

        Write-Verbose -Message "$Sitename is a valid sitename."

        $global:IISRFB_Sitename = $Sitename.ToUpper()

        return $true
    }

} # End function Validate-Sitename

function Validate-WebsiteContentPath
{
    <#
        .SYNOPSIS
        Validates Website Content Path input parameter.
        .EXAMPLE
        Validate-WebsiteContentPath -Path D:\WorkingFolder\
    #>


    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true)]
        [System.String]
        # Path to Website Content Path
        $Path
    )

    Write-Verbose -Message 'Begin function Validate-WebsiteContentPath'

    $HasErrors = $false

    $global:IISRFB_WebsiteContentPath = [System.IO.Path]::GetDirectoryName($Path) + '\*'
    
    $lpResult = ''

    try {

        $lpQuery = "SELECT COUNT(*) FROM $global:IISRFB_WebsiteContentPath"

        Write-Verbose -Message "Using Logparser query: $lpQuery"

        [string]$lpResult = & $global:IISRFB_LogparserPath -e:-1 -recurse:-1 -preserveLastAccTime:ON -iw:ON -headers:OFF -q:ON -i:FS -o:NAT $lpQuery

        if($lpResult.Trim() -eq 'Task aborted.')
        {
            Write-Error -Message "Failed to query Website Content Path with Logparser.exe" `
                        -RecommendedAction "Manually run this query: & $global:IISRFB_LogparserPath -i:FS -o:NAT $lpQuery" `
                        -Category NotSpecified -ErrorId 150
            $HasErrors = $true

        } else {

            [int]$Records = 0

            if([System.Int32]::TryParse($lpResult.Trim(), [ref]$Records) -eq $false)
            {
                Write-Error -Message "Failed to query Website Content Path with Logparser.exe" `
                        -RecommendedAction "Manually run this query: & $global:IISRFB_LogparserPath -i:FS -o:NAT $lpQuery" `
                        -Category NotSpecified -ErrorId 151
                $HasErrors = $true

            } else {

                if($Records -gt 0)
                {
                    Write-Verbose -Message "$Records files/folders found in $global:IISRFB_WebsiteContentPath"

                } else {

                    Write-Error -Message "Failed to query files/folders from Website Content Path with Logparser.exe" `
                            -RecommendedAction "Manually run this query: & $global:IISRFB_LogparserPath -i:FS -o:NAT $LpQuery" `
                            -Category NotSpecified -ErrorId 152
                    return $false
                }
            }
        }

    } catch {

        $e = $_

        Write-Error -Message "Failed to query Website Content Path with Logparser.exe. $e.Exception.Message" `
                    -RecommendedAction 'Review exception.' -Category $e.CategoryInfo.Category -ErrorId 153
        $HasErrors = $true
    }

    if($HasErrors)
    {
        return $false

    } else {
    
        return $true
    }

} # End function Validate-WebsiteContentPath

function Validate-IisLogs
{
    <#
        .SYNOPSIS
        Validates IIS logs input parameter.
    #>


    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true)]
        [System.String]
        # Path to IIS logs
        $Path
    )

    Write-Verbose -Message 'Begin function Validate-IisLogs'

    $i = 0

    $HasErrors = $false

    $global:IISRFB_IISLogPath = $Path
    
    $lpResult = ''

    do{

        $i++

        switch($i)
        {
            1 { # add asterisk if not present.

                if($global:IISRFB_IISLogPath.ToString().Contains('*') -eq $false)
                {
                    Write-Verbose -Message "Adding suffix to path: $global:IISRFB_IISLogPath"

                    try {

                        $global:IISRFB_IISLogPath = [System.IO.Path]::GetDirectoryName($Path) + '\*.log'

                    } catch {

                        $e = $_

                        Write-Error -Message "Failed to update IIS log path. $e.Exception.Message" `
                                    -RecommendedAction 'Review exception.' -Category $e.CategoryInfo.Category -ErrorId 140
                        $HasErrors = $true
                    }
                }
            }

            2 { # run logparser against

                Write-Verbose -Message "Running Logparser.exe against IIS log path: $global:IISRFB_IISLogPath"

                try {

                    $LpQuery = "SELECT COUNT(*) FROM $global:IISRFB_IISLogPath WHERE s-sitename LIKE `'$global:IISRFB_Sitename`'"

                    Write-Verbose -Message "Using Logparser query: $LpQuery"

                    [string]$lpResult = & $global:IISRFB_LogparserPath -e:-1 -iw:ON -headers:OFF -q:ON -i:IISW3C -o:NAT $lpQuery

                    if($lpResult.Trim() -eq 'Task aborted.')
                    {
                        Write-Error -Message "Failed to query IIS logs with Logparser.exe" `
                                    -RecommendedAction "Manually run this query: & $global:IISRFB_LogparserPath -i:IISW3C -o:NAT $LpQuery" `
                                    -Category NotSpecified -ErrorId 141
                        return $false

                    } else {

                        [int]$IisRecords = 0

                        if([System.Int32]::TryParse($lpResult.Trim(), [ref]$IisRecords) -eq $false)
                        {
                            Write-Error -Message "Failed to query IIS logs with Logparser.exe" `
                                    -RecommendedAction "Manually run this query: & $global:IISRFB_LogparserPath -i:IISW3C -o:NAT $LpQuery" `
                                    -Category NotSpecified -ErrorId 142
                            return $false

                        } else {

                            if($IisRecords -gt 0)
                            {
                                Write-Verbose -Message "$IisRecords IIS log records found for $global:IISRFB_Sitename"

                            } else {

                                Write-Error -Message "Failed to return IIS logs with Logparser.exe" `
                                        -RecommendedAction "Manually run this query: & $global:IISRFB_LogparserPath -i:IISW3C -o:NAT $LpQuery" `
                                        -Category NotSpecified -ErrorId 143
                                return $false
                            }
                        }
                    }

                } catch {
                
                    $e = $_

                    Write-Error -Message "Failed to get file details. $e.Exception.Message" `
                                -RecommendedAction 'Review exception.' -Category $e.CategoryInfo.Category -ErrorId 144
                    $HasErrors = $true
                }
            }

            default { return $true }
        }

        if($HasErrors -eq $true)
        {
            return $false
        }

    }while($i -lt 10)

} # End function Validate-IisLogs

function Validate-Logparser
{
    <#
        .SYNOPSIS
        Validates Logparser version and that it can be launched.
        .EXAMPLE
        Validate-Logparser -Path D:\WorkingFolder\logparser.exe
    #>


    [CmdletBinding()]
    param(
        [parameter(Mandatory=$false)]
        [System.String]
        # Path to Logparser
        $Path
    )

    Write-Verbose -Message 'Begin function Validate-Logparser'
    
    $i = 0

    $global:IISRFB_LogparserPath = $Path

    $LpExeObj = ''

    $HasErrors = $false
 
    do{

        $i++

        switch($i)
        {
            1 { # Search for Logparser.exe if not provided.

                if([System.String]::IsNullOrEmpty($global:IISRFB_LogparserPath))
                {
                    Write-Verbose -Message 'Searching for Logparser.exe'

                    [string]$SearchFolders = $env:Path + ';' + $pwd.Path

                    $lpProgramFilesx86 = Join-Path -Path ${env:ProgramFiles(x86)} -ChildPath 'Log Parser 2.2'

                    if(Test-Path -LiteralPath $lpProgramFilesx86)
                    {
                        $SearchFolders = $lpProgramFilesx86 + ';' + $SearchFolders
                    }

                    $lpProgramFiles = Join-Path -Path $env:ProgramFiles -ChildPath 'Log Parser 2.2'

                    if(Test-Path -LiteralPath $lpProgramFiles)
                    {
                        $SearchFolders = $lpProgramFiles + ';' + $SearchFolders
                    }

                    if([System.String]::IsNullOrEmpty($global:IISRFB_WorkingDirectory) -eq $false)
                    {
                        $SearchFolders = $global:IISRFB_WorkingDirectory + ';' + $SearchFolders
                    }

                    [string[]]$SearchFoldersArray = $SearchFolders.Split(';')

                    for($n=0; $n -lt $SearchFoldersArray.Count; $n++)
                    {
                        if([System.String]::IsNullOrEmpty($SearchFoldersArray[$n]) -eq $false)
                        {
                            $TestLpLocation = Join-Path -Path $SearchFoldersArray[$n] -ChildPath 'Logparser.exe'

                            Write-Verbose -Message "Checking $TestLpLocation"

                            if(Test-Path -LiteralPath $TestLpLocation)
                            {
                                $global:IISRFB_LogparserPath = $TestLpLocation

                                $n = $SearchFoldersArray.Count

                                Write-Verbose -Message "Found $TestLpLocation"
                            }
                        }
                    }
                }

                if([System.String]::IsNullOrEmpty($global:IISRFB_LogparserPath))
                {
                    Write-Error -Message 'Cannot find Logparser.exe.' -RecommendedAction 'Use -LogparserPath switch.' `
                                -Category ObjectNotFound -ErrorId 130
                    $HasErrors = $true
                }
            }

            2 { # get file details of Logparser.exe

                Write-Verbose -Message "Getting file details for $global:IISRFB_LogparserPath"

                try {

                    $LpObj = Get-Item -LiteralPath $global:IISRFB_LogparserPath -ErrorAction Stop

                    if($LpObj.PSIsContainer)
                    {
                        $LpPath = Join-Path -Path $global:IISRFB_LogparserPath -ChildPath 'LogParser.exe'

                        $LpExeObj = Get-Item -LiteralPath $LpPath -ErrorAction Stop

                        $global:IISRFB_LogparserPath = $LpExeObj.FullName

                    } else {

                        $LpExeObj = $LpObj
                    }

                    if([System.String]::IsNullOrEmpty($LpExeObj) -eq $true)
                    {
                        Write-Error -Message 'Failed to get file details for Logparser.exe.' `
                                    -RecommendedAction 'Use -LogparserPath switch.' -Category ObjectNotFound -ErrorId 131
                        $HasErrors = $true
                    }

                } catch {
                                
                    $e = $_

                    Write-Error -Message $('Failed to get file details. {0}' -f $e.Exception.Message) `
                                -RecommendedAction 'Use -LogparserPath switch.' -Category $e.CategoryInfo.Category -ErrorId 132
                    $HasErrors = $true
                }
            }

            3 { # verify file details of Logparser.exe

                Write-Verbose -Message "Verifying file details of $global:IISRFB_LogparserPath"

                try {

                    if($LpExeObj.VersionInfo.FileVersion -ne '2.2.10.0')
                    {
                        Write-Error -Message $('Invalid Log Parser Version {0}' -f $LpExeObj.VersionInfo.FileVersion) `
                                    -RecommendedAction 'Get Log Parser Version 2.2.10' -Category InvalidResult -ErrorId 133
                        $HasErrors = $true
                    
                    } else {

                        $global:IISRFB_LogparserPath = $LpExeObj.FullName
                    }

                } catch {
                                
                    $e = $_

                    Write-Error -Message $('Error getting Logparser.exe file details. {0}' -f $e.Exception.Message) `
                                -RecommendedAction 'Check exception message.' -Category $e.CategoryInfo.Category -ErrorId 134
                    $HasErrors = $true
                }
            }

            4 { # test launch of Logparser.exe

                Write-Verbose -Message "Test launch of $global:IISRFB_LogparserPath"

                try{

                    $lpQuery = "`"SELECT FileVersion FROM `'$($global:IISRFB_LogparserPath)`'`""

                    Write-Verbose -Message "Using Logparser query: $lpQuery"

                    [string]$lpFileVersion = & $global:IISRFB_LogparserPath -e:-1 -iw:ON -headers:OFF -q:ON -i:FS -o:NAT $lpQuery

                    if([System.String]::IsNullOrEmpty($lpFileVersion) -eq $false)
                    {                    
                        if($lpFileVersion.Trim() -eq 'Task aborted.')
                        {
                            Write-Error -Message "Failed to launch $global:IISRFB_LogparserPath" `
                                        -RecommendedAction "Manually run this query: & $global:IISRFB_LogparserPath -i:FS -o:NAT $lpQuery" `
                                        -Category NotSpecified -ErrorId 135
                            $HasErrors = $true

                        } elseif($lpFileVersion.Trim().StartsWith('2.2.10') -eq $false){

                            Write-Error -Message $('Invalid Log Parser Version {0}' -f $lpFileVersion) `
                                        -RecommendedAction 'Install Log Parser Version 2.2.10' -Category InvalidResult -ErrorId 136
                            $HasErrors = $true
                        }

                    } else {

                        Write-Error -Message "Failed to launch $global:IISRFB_LogparserPath" `
                                    -RecommendedAction "Manually run this query: & $global:IISRFB_LogparserPath -i:FS -o:NAT $lpQuery" `
                                    -Category NotSpecified -ErrorId 137
                        $HasErrors = $true
                    }

                } catch {

                    $e = $_

                    Write-Error -Message $('Failed to launch Logparser.exe. {0}' -f $e.Exception.Message) `
                                -RecommendedAction 'Check exception message.' -Category $e.CategoryInfo.Category -ErrorId 138
                    $HasErrors = $true
                }
            }

            default { return $true }
        }

        if($HasErrors -eq $true)
        {
            return $false
        }

    }while($i -lt 10)

} # End function Validate-Logparser

function Get-IISRFBaselineHelp
{
    <#
        .SYNOPSIS
         
        Outputs markdown help file for the specified setting.
 
        .DESCRIPTION
 
        Best viewed on GitHub. Acceptable values are:
 
        allowDoubleEscaping
        allowHighBitCharacters-FS
        allowHighBitCharacters-IIS
        FAQ
        fileExtensions-FS
        fileExtensions-IIS
        maxAllowedContentLength
        maxQueryString
        maxUrl-FS
        maxUrl-IIS
        OtherSettings
        README
        verbs
 
        .INPUTS
 
        None
 
        .OUTPUTS
 
        System.String[]
 
        .EXAMPLE
         
        PS> Get-IISRFBaselineHelp -Setting allowHighBitCharacters-IIS
 
        .EXAMPLE
 
        PS> Get-IISRFBaselineHelp -Setting maxAllowedContentLength
 
        .LINK
 
        https://github.com/phbits/IISRFBaseline
    #>


    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true)]
        [ValidateSet('allowDoubleEscaping','allowHighBitCharacters-FS','allowHighBitCharacters-IIS','fileExtensions-FS','fileExtensions-IIS','maxAllowedContentLength','maxQueryString','maxUrl-FS','maxUrl-IIS','verbs','FAQ','OtherSettings','README')]
        [System.String]
        # IISRFBaseline Help File
        $HelpFile
    )
        $HelpFileLocation = ''

        switch ($Setting.ToUpper())
        {
            'ALLOWDOUBLEESCAPING'        { $HelpFileLocation = Join-Path -Path $global:IISRFB_BasePath -ChildPath 'IISRFBaseline-allowDoubleEscaping.md' }
            'ALLOWHIGHBITCHARACTERS-FS'  { $HelpFileLocation = Join-Path -Path $global:IISRFB_BasePath -ChildPath 'IISRFBaseline-allowHighBitCharacters-FS.md' }
            'ALLOWHIGHBITCHARACTERS-IIS' { $HelpFileLocation = Join-Path -Path $global:IISRFB_BasePath -ChildPath 'IISRFBaseline-allowHighBitCharacters-IIS.md' }
            'FAQ'                        { $HelpFileLocation = Join-Path -Path $global:IISRFB_BasePath -ChildPath 'FAQ.md' }
            'FILEEXTENSIONS-FS'          { $HelpFileLocation = Join-Path -Path $global:IISRFB_BasePath -ChildPath 'IISRFBaseline-fileExtensions-FS.md' }
            'FILEEXTENSIONS-IIS'         { $HelpFileLocation = Join-Path -Path $global:IISRFB_BasePath -ChildPath 'IISRFBaseline-fileExtensions-IIS.md' }
            'MAXALLOWEDCONTENTLENGTH'    { $HelpFileLocation = Join-Path -Path $global:IISRFB_BasePath -ChildPath 'IISRFBaseline-maxAllowedContentLength.md' }
            'MAXQUERYSTRING'             { $HelpFileLocation = Join-Path -Path $global:IISRFB_BasePath -ChildPath 'IISRFBaseline-maxQueryString.md' }
            'MAXURL-FS'                  { $HelpFileLocation = Join-Path -Path $global:IISRFB_BasePath -ChildPath 'IISRFBaseline-maxUrl-FS.md' }
            'MAXURL-IIS'                 { $HelpFileLocation = Join-Path -Path $global:IISRFB_BasePath -ChildPath 'IISRFBaseline-maxUrl-IIS.md' }
            'OTHERSETTINGS'              { $HelpFileLocation = Join-Path -Path $global:IISRFB_BasePath -ChildPath 'OtherSettings.md' }
            'README'                     { $HelpFileLocation = Join-Path -Path $global:IISRFB_BasePath -ChildPath 'README.md' }
            'VERBS'                      { $HelpFileLocation = Join-Path -Path $global:IISRFB_BasePath -ChildPath 'IISRFBaseline-verbs.md' }
            default                      { Write-Error 'Unknown `"-Setting`" parameter. Try, Get-Help Get-IISRFBaselineHelp -Full' }
        }
    
    if([System.String]::IsNullOrEmpty($HelpFileLocation) -eq $false)
    {
        try 
        {        
            [string[]]$HelpFileContent = Get-Content -LiteralPath $HelpFileLocation

            return $HelpFileContent

        } catch {
            
            $e = $_

            Write-Error -Message "$($e.Exception.Message)"
        }
    }

} # End function Get-IISRFBaselineHelp

function Validate-WorkingDirectory
{
    <#
        .SYNOPSIS
        Validates Read/Write Access to Working Directory
    #>


    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true)]
        [System.String]
        # Path to Working Directory
        $WorkingDir
    )

    Write-Verbose -Message 'Begin function Validate-WorkingDirectory'

    Write-Verbose -Message "Validating IsFolder: $WorkingDir"

    $WorkingDirItem = Get-Item -LiteralPath $WorkingDir

    if($WorkingDirItem.PSIsContainer -eq $false)
    {
        Write-Error -Message "`"$WorkingDir`" is not a valid Working Directory." `
                                    -RecommendedAction "Specify a folder for the `'-WorkingDirectory`' switch." `
                                    -Category NotSpecified -ErrorId 161
        return $false
    }

    Write-Verbose -Message "Validating Read/Write access to $WorkingDir"

    $TestFilePath = Join-Path -Path $WorkingDir -ChildPath 'tmp-test-write.txt'

    if(Test-Path -LiteralPath $TestFilePath)
    {
        Write-Error -Message "$TestFilePath already exists. Must delete before proceeding." `
                    -Category NotSpecified -ErrorId 162
        return $false
    }

    try {

        Write-Verbose -Message "Writing temp file to $TestFilePath"

        $TestFile = New-Item -Path $WorkingDir -Name 'tmp-test-write.txt' -ItemType File -Value 'TestWrite' -ErrorAction Stop

        Write-Verbose -Message "Reading temp file at $TestFilePath"

        $TestFileContent = Get-Content -LiteralPath $TestFilePath -ErrorAction Stop

        if($TestFileContent -ne 'TestWrite')
        {
            Write-Error -Message "Failed to read temp file $TestFilePath" `
                                    -RecommendedAction "Verify a minimum of Read/Write permission on $WorkingDir" `
                                    -Category NotSpecified -ErrorId 163
            return $false
        }

    } catch {
    
        $e = $_

        Write-Error -Message "$($e.Exception.Message)"

        return $false
    }

    Write-Verbose -Message "Validated Read/Write access to $WorkingDir"

    if(Test-Path -LiteralPath $TestFilePath)
    {
        try {

            Write-Verbose -Message "Removing temp file $TestFilePath"

            Remove-Item -LiteralPath $TestFilePath

        } catch {

            Write-Verbose -Message "Failed to remove temp file $TestFilePath. Not critical. Continuing."
        }
    }

    Write-Verbose -Message "Valid working directory $WorkingDir"

    $global:IISRFB_WorkingDirectory = [System.IO.Path]::GetDirectoryName($WorkingDir) + '\'

    return $true

} # End function Validate-WorkingDirectory

# set module root to variable
$global:IISRFB_BasePath = [System.IO.Path]::GetDirectoryName($psscriptroot) + '\'