Helpers/Functions.ps1

#region DSC Strings
# Create a variable so we can detect conflicts in the Configuration.
New-Variable -Name GlobalConflictEngine -Value @{} -Option AllScope -Scope Script -Force
$GlobalConflictEngine = @{}

# Create a variable to track every resource processed for Summary Data.
New-Variable -Name ProcessingHistory -Value @{} -Option AllScope -Scope Script -Force
$ProcessingHistory = @{}

Function Add-ProcessingHistory {
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$Name,

        [Parameter(Mandatory = $true)]
        [string]$Type,

        [switch]$Conflict,
        [switch]$Disabled,
        [switch]$ParsingError,
        [switch]$ResourceNotFound
    )

    # If we do not have a processing history entry for this type, set it to a blank array.
    # Have to do this here, because they may have forgotten do import the module so we cannot assume only Resources specified in the Import-Module.
    if (!$ProcessingHistory.ContainsKey($Type)) {
        $ProcessingHistory[$Type] = @()
    }

    # Add this resource to the processing history.
    $ProcessingHistory[$Type] += @{Name = $Name; Conflict = $Conflict; Disabled = $Disabled; ResourceNotFound = $ResourceNotFound; ParsingError = $ParsingError }
}

Function Test-Conflicts {
    [OutputType([bool])]
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$Type,

        [Parameter(Mandatory = $true)]
        [string]$Name,

        [Parameter(Mandatory = $true)]
        [switch]$CommentOut,

        [Parameter(Mandatory = $true)]
        [System.Collections.Hashtable]$Resource
    )

    # Set our conflict variables up.
    $GlobalConflict = $false
    $ResourceNotFound = $false

    $Conflict = @()
    $ResourceKeys = @()

    # Determine if we have already processed a Resource Block of this type.
    if ($Script:GlobalConflictEngine.ContainsKey($Type)) {
        # Loop through every Resource definition of this type.
        foreach ($hashtable in $Script:GlobalConflictEngine[$Type]) {
            $Conflict = @()
            $ResourceKeys = @()

            # Loop through every Key/Value Pair in the Resource definition to see if they match the current one.
            foreach ($KeyPair in $hashtable.GetEnumerator()) {
                # Add the result to our Conflict Array.
                $Conflict += $KeyPair.Value -eq $Resource[$KeyPair.Name]
                # Need to store which Key/Value Pairs we checked.
                $ResourceKeys += $KeyPair.Name
            }

            # If we found a conflict.
            if ($ResourceKeys.Count -gt 0 -and $Conflict -notcontains $false) {
                Write-Verbose "Detected Potential Conflict in $Name. Commenting Out Block"
                $GlobalConflict = $true
                break
            }
        }
    }
    else {
        Write-Verbose "Write-DSCString: DSC resource ($Type) not found on System. Please run the conversion again when the resource is available."
        $ResourceNotFound = $true
        $GlobalConflict = $true
    }

    if (!$GlobalConflict) { # Add this Resources Key/Value pairs to the collective.
        $tmpHash = @{}
        foreach ($Key in $ResourceKeys) {
            $tmpHash[$key] = $Resource[$key]
        }

        $Script:GlobalConflictEngine[$Type] += $tmpHash
    }

    # Add this resource to the processing history.
    Add-ProcessingHistory -Type $Type -Name $Name -Conflict:$GlobalConflict -Disabled:$CommentOut -ResourceNotFound:$ResourceNotFound

    return ($GlobalConflict -or $ResourceNotFound -or $CommentOut)
}

Function Get-Tabs {
    [OutputType([string])]
    param
    (
        [Parameter(Mandatory = $true)]
        [int]$Tabs
    )
    (1..$Tabs) | ForEach-Object { " " }
}

Function Write-DSCStringKeyPair {
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$Key,

        [Parameter(Mandatory = $true)]
        [int]$Tabs,

        [Parameter(Mandatory = $true)]
        [AllowNull()]
        $Value
    )

    $DSCString = ""

    if ($Value -eq $null) {
        <# Allowing Null values for now
            return "# `n$(Get-Tabs $Tabs)$($key) = $null"
        #>

        return "`n$(Get-Tabs $Tabs)$($key) = `$null"
    }


    # Start the Resource Key/Value Pair.
    $DSCString += "`n$(Get-Tabs $Tabs)$($key) = "
    $Separator = ", "
    # If the Value is an array, increase the tab stops and add the array operators.
    if ($Value -is [Array]) {
        $DSCString += "@("
        $Tabs++
    }

    # Treat the value like an array, even if it's an array of 1. It simplifies the parsing.
    for ($i = 0; $i -lt @($Value).Count; $i++) {
        $tmpValue = @($Value)[$i]
        switch ($tmpValue) {
            { $_ -is [System.String] } {
                Try {
                    Invoke-Expression $("`$variable = '" + $_.TrimStart("'").TrimEnd("'").TrimStart('"').TrimEnd('"') + "'") | Out-Null
                    $DSCString += "'$([string]::new($_.TrimStart("'").TrimEnd("'").TrimStart('"').TrimEnd('"').Trim(' ')))'"
                }
                Catch {
                    # Parsing Error
                    $DSCString += "@'`n$($_.Trim("'").TrimStart("'").TrimEnd("'").TrimStart('"').TrimEnd('"'))`n'@"
                }
            }

            { $_ -is [System.Boolean] } {
                $DSCString += "`$$([string]([bool]$_))"
            }

            { $_ -is [System.Collections.Hashtable] } {
                $identifier = "@"
                if ($_.ContainsKey("EmbeddedInstance")) {
                    $identifier = $_.EmbeddedInstance
                    $Separator = ";"
                    $_.Remove("EmbeddedInstance") | Out-Null
                }

                if ($Value -is [Array]) {
                    $DSCString += "`n$(Get-Tabs $Tabs)"
                }

                $DSCString += "$identifier"
                $Tabs += 2
                $DSCString += "`n$(Get-Tabs $Tabs){"
                $Tabs++
                foreach ($keypair in $_.GetEnumerator()) {
                    $DSCString += Write-DSCStringKeyPair -Key $Keypair.Name -Value $Keypair.Value -Tabs $Tabs
                }
                $Tabs--
                $DSCString += "`n$(Get-Tabs $Tabs)}"
                $Tabs -= 2
            }

            Default {
                $DSCString += "$($_)"
            }
        }

        if ((@($Value).Count - $i) -gt 1) {
            $DSCString += $Separator
        }
    }

    # If the value was an array, close up the array and decrement the tab stops.
    if ($Value -is [Array]) {
        $Tabs--
        $DSCString += ")"
    }

    return $DSCString
}


<#
This is the function that makes it all go. It has a variety of parameter sets
which can be tricky to use. Each of the Switches tell the function what type of
Code block it is creating. The additional parameters to the set are what
determine the contents of the block. This Function takes the input and properly
formats it with Tabs and Newlines so it looks properly formatted.
 
It also has a built in Conflict detection engine. If it detects that a Resource
with the Same REQUIRED values was already processed it will COMMENT OUT
subsequent resource blocks with the same values.
 
The function has logic to arbitrarily comment out a resource block if
necessary (like disabled resources). The function also provides functionality to
comment Code Blocks if comments are available.
#>

Function Write-DSCString {
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        # Configuration Block
        [Parameter(Mandatory = $true, ParameterSetName = "Configuration")]
        [switch]$Configuration,

        # Resource Block
        [Parameter(Mandatory = $true, ParameterSetName = "Resource")]
        [switch]$Resource,

        # Import-Module line.
        [Parameter(Mandatory = $true, ParameterSetName = "ModuleImport")]
        [switch]$ModuleImport,

        # Node Block.
        [Parameter(Mandatory = $true, ParameterSetName = "Node")]
        [switch]$Node,

        # Close out the configuration block.
        [Parameter(ParameterSetName = "CloseConfigurationBlock")]
        [switch]$CloseConfigurationBlock,

        # Close the Node Block.
        [Parameter(ParameterSetName = "CloseNodeBlock")]
        [switch]$CloseNodeBlock,

        # Invoke the Configuration (to create the MOF).
        [Parameter(Mandatory = $true, ParameterSetName = "InvokeConfiguration")]
        [switch]$InvokeConfiguration,

        # This will be the Name of the Configuration Block or Node block or Resource Block.
        [Parameter(Mandatory = $true, ParameterSetName = "Configuration")]
        [Parameter(Mandatory = $true, ParameterSetName = "InvokeConfiguration")]
        [Parameter(Mandatory = $true, ParameterSetName = "Node")]
        [Parameter(Mandatory = $true, ParameterSetName = "Resource")]
        [string]$Name,

        # This is the TYPE to be used with Resource Blocks (Regisry, Service, etc.).
        [Parameter(Mandatory = $true, ParameterSetName = "Resource")]
        [string]$Type,

        # This is an Array of ModuleNames to import.
        [Parameter(Mandatory = $true, ParameterSetName = "ModuleImport")]
        [string[]]$ModuleName,

        # This is a hashtable of Keys/Values for a Resource Block.
        [Parameter(Mandatory = $true, ParameterSetName = "Resource")]
        [hashtable]$Parameters,

        # This determines whether or not to comment out a resource block.
        [switch]$CommentOUT = $false,

        # This allows comments to be added to various code blocks.
        [string]$Comment,

        # This allows conditional resource blocks
        [Parameter(ParameterSetName = "Resource")]
        [scriptblock]$Condition,

        # This is meant to fix a bug in the ASC Baselines themselves, and will be removed when the bug is fixed.
        # This will use DoubleQuotes on Resource Names instead of Single thus solving any parsing errors where stray quotes are present.
        [Parameter(ParameterSetName = "Resource")]
        [switch]$DoubleQuoted,

        # This Output Path is for the Configuration Block, not this function.
        [Parameter(ParameterSetName = "InvokeConfiguration")]
        [string]$OutputPath = $(Join-Path -Path $PSScriptRoot -ChildPath "Output")
    )

    $DSCString = ""
    switch ($PSCmdlet.ParameterSetName) {
        "Configuration" {
            # Add comments if there are comments.
            if ($PSBoundParameters.ContainsKey("Comment")) {
                $Comment = "`n<#`n$Comment`n#>"
            }
            else {
                $Comment = ""
            }

            # Output the DSC String.
            $DSCString = @"
$Comment
Configuration $Name`n{`n`n`t
"@

        }
        "ModuleImport" {
            Write-Verbose "Write-DSCString: ModuleImport"
            # Use this block to reset our Conflict Engine.
            $Script:GlobalConflictEngine = @{}
            $ModuleNotFound = @()
            # Loop through each module.
            foreach ($m in $ModuleName) {
                if (!(Get-command Get-DscResource -ErrorAction SilentlyContinue)) {
                    Import-module PSDesiredStateConfiguration -Force
                }

                Write-Verbose "Write-DSCString: Checking module $m."
                # Use Get-DSCResource to determine REQUIRED parameters to resource.
                $resources = Get-DscResource -Module $m
                if ($null -ne $resources) {
                    # Loop through every resource in the module.
                    foreach ($r in $resources) {
                        # Add a blank entry for the Resource Block with Required Parameters.
                        $Script:GlobalConflictEngine[$r.Name] = @()
                        $tmpHash = @{}
                        $r.Properties.Where( { $_.IsMandatory }) | ForEach-Object {
                            Write-Verbose "Write-DSCString: Resource $($r.Name) added to tmpHash."
                            $tmpHash[$_.Name] = ""
                        }
                        $Script:GlobalConflictEngine[$r.Name] += $tmpHash
                    }
                }
                else {
                    Write-Verbose "Write-DSCString: Module ($m) not found on System. Please run conversion again when the module is available."
                    $ModuleNotFound += $m
                }
            }

            $ModuleName = $ModuleName | Where-Object { $_ -notin $ModuleNotFound }

            # Output our Import-Module string.
            foreach ($m in $ModuleName) {
                $DSCString += "Import-DSCResource -ModuleName '$m'`n`t"
            }

            foreach ($m in $ModuleNotFound) {
                $DSCString += "# Module Not Found: Import-DSCResource -ModuleName '$m'`n`t"
            }
        }
        "Node" { $DSCString = "Node $Name`n`t{`n" }
        "InvokeConfiguration" { $DSCString = "$Name -OutputPath '$($OutputPath)'" }
        "CloseNodeBlock" {
            $DSCString = @"
         RefreshRegistryPolicy 'ActivateClientSideExtension'
         {
             IsSingleInstance = 'Yes'
         }
     }
"@

        }
        "CloseConfigurationBlock" { $DSCString = "`n}`n" }
        "Resource" {
            $Tabs = 2
            $DSCString = ""

            # A Condition was specified for this resource block.
            if ($PSBoundParameters.ContainsKey("Condition")) {
                $DSCString += "$(Get-Tabs $Tabs)if ($($Condition.ToString()))`n $(Get-Tabs $Tabs){`n"
                $Tabs++
            }

            # Variables to be used for commenting out resource if necessary.
            $CommentStart = ""
            $CommentEnd = ""

            $CommentOUT = Test-Conflicts -Type $Type -Name $Name -Resource $Parameters -CommentOut:$CommentOUT

            # If we are commenting this block out, then set up our comment characters.
            if ($CommentOut) {
                $CommentStart = "<#"
                $CommentEnd = "#>"
            }

            # If they passed a comment FOR the block, add it above the block.
            if ($PSBoundParameters.ContainsKey("Comment") -and ![string]::IsNullOrEmpty($Comment)) {
                $tmpComment = "<#`n"
                # Changed from ForEach-Object { $tmpComment += "`t`t$_`n"}
                $tmpComment += $Comment -split "`n" | ForEach-Object { "$(Get-Tabs $Tabs)$_`n" }
                $tmpComment += "$(Get-Tabs $Tabs)#>`n$(Get-Tabs $Tabs)"
                $Comment = $tmpComment
            }
            else {
                $Comment = ""
            }

            # Start the Resource Block with Comment and CommentOut characters if necessary.
            if ($DoubleQuoted) {
                $DSCString += "$(Get-Tabs $Tabs)$Comment$($CommentStart)$Type `"$($Name)`"`n$(Get-Tabs $Tabs){"
            }
            else {
                $DSCString += "$(Get-Tabs $Tabs)$Comment$($CommentStart)$Type '$($Name)'`n$(Get-Tabs $Tabs){"
            }
            $Tabs++
            foreach ($key in $Parameters.Keys) {
                if ($Parameters[$key] -eq $null) {
                    "WTF"  | out-null
                }

                $DSCString += Write-DSCStringKeyPair -Key $key -Value $Parameters[$key] -Tabs $Tabs
            }
            $Tabs--
            $DSCString += "`n$(Get-Tabs $Tabs)}$CommentEnd"

            if ($PSBoundParameters.ContainsKey("Condition")) {
                $DSCString += "$(Get-Tabs $Tabs)if ($($Condition.ToString()))`n $(Get-Tabs $Tabs){`n"
                $Tabs++
            }
            $Tabs--
            if ($PSBoundParameters.ContainsKey("Condition")) {
                $DSCString += "`n`n$(Get-Tabs $Tabs)}"
            }

            $DSCString += "`n`n"
        }
    }

    Write-Verbose $DSCString

    # Return our DSCstring.
    return $DSCString
}

function Get-IniContent {
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [System.String]$Path
    )

    $ini = @{}
    switch -regex -file $Path {
        "^\[(.+)\]" { # Section
            $section = $matches[1]
            $ini[$section] = @{}
            $CommentCount = 0
        }
        "^(;.*)$" { # Comment
            $value = $matches[1]
            $CommentCount = $CommentCount + 1
            $name = "Comment" + $CommentCount
            $ini[$section][$name] = $value.Trim()
            continue
        }
        "(.+)\s*=(.*)" { # Key
            $name, $value = $matches[1..2]
            $ini[$section][$name.Trim()] = $value.Trim()
            # Need to replace double quotes with `"
            continue
        }
        "\`"(.*)`",(.*)$" {
            $name, $value = $matches[1..2]
            $ini[$section][$name.Trim()] = $value.Trim()
            continue
        }
    }
    return $ini
}

Function Complete-Configuration {
    [CmdletBinding()]
    [OutputType([bool])]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$ConfigString,

        [Parameter()]
        [string]$OutputPath = $(Join-Path -Path $pwd.path -ChildPath "Output")
    )

    if ($ConfigString -match "(?m)Configuration (?<Name>.*)$") {
        $CallingFunction = $Matches.Name
    }
    else {
        $CallingFunction = (Get-PSCallStack)[1].Command
    }

    if (!(Test-Path $OutputPath)) {
        mkdir $OutputPath
    }

    $scriptblock = [scriptblock]::Create($ConfigString)

    if (!$?) {
        # Somehow CallingFunction, defined above, is not useable right here. Not sure why
        $Path = $(Join-Path -Path $OutputPath -ChildPath "$($CallingFunction).ps1.error")
        $ConfigString > $Path

        Write-Error "Could not CREATE ScriptBlock from configuration. Creating PS1 Error File: $Path. Please rename error file to PS1 and open for further inspection. Errors are listed below"
        return $false
    }

    $output = Invoke-Command -ScriptBlock $scriptblock

    if (!$?) {
        $Path = $(Join-Path -Path $OutputPath -ChildPath "$($CallingFunction).ps1.error")
        $scriptblock.ToString() > $Path

        Write-Error "Could not COMPILE cofiguration. Storing ScriptBlock: $Path. Please rename error file to PS1 and open for further inspection. Errors are listed below"
        foreach ($e in $output) {
            Write-Error $e
        }
        return $false
    }

    return $true
}

# Clear our processing history on each Configuration Creation.
Function Clear-ProcessingHistory {
    $ProcessingHistory.Clear()
}

# Write out summary data and output proper files based on success/failure.
Function Write-ProcessingHistory {
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [bool]$Pass,

        [Parameter()]
        [string]$OutputPath = $(Join-Path -Path $pwd.path -ChildPath "Output")
    )

    if (!(Test-Path $OutputPath)) {
        mkdir $OutputPath | Out-Null
    }

    if (((Get-Module -ListAvailable).name -contains "Pester")) {
        Import-Module Pester
    }
    elseif (!((Get-Module).name -contains "Pester")) {
        Write-ProcessingHistory_NonPester -Pass $Pass
        Write-ProcessingHistory_NonPester -Pass $Pass *> $(Join-Path -Path $OutputPath -ChildPath "Summary.log")
        return
    }

    New-Variable -Name successes -Option AllScope -Force
    New-Variable -Name disabled -Option AllScope -Force
    New-Variable -Name conflict -Option AllScope -Force
    New-Variable -Name resourcenotfound -Option AllScope -Force
    New-Variable -Name parsingerror -Option AllScope -Force
    $successes = $disabled = $conflict = $parsingerror = $resourcenotfound = 0
    $History = Get-Variable ProcessingHistory
    foreach ($KeyPair in $History.Value.GetEnumerator()) {
        $old_success = $successes
        $old_disabled = $disabled
        $old_conflict = $conflict
        $old_resourcenotfound = $resourcenotfound
        $old_parsingerror = $parsingerror

        $Describe = "Parsing Summary: $((Get-PsCallStack)[1].Command) - $(@("FAILED", "SUCCEEDED")[[int]$Pass])`n`t$($KeyPair.Key.ToUpper()) Resources"
        Describe $Describe {

            foreach ($Resource in $KeyPair.Value.Where( { ($_.Disabled -eq $false) -and ($_.Conflict -eq $false) -and ($_.ResourceNotFound -eq $false) -and ($_.ParsingError -eq $false) })) {
                It "Parsed: $($Resource.Name)" {
                    $Resource.Disabled | Should Be $false
                }
                $successes++
            }

            foreach ($Resource in $KeyPair.Value.Where( { $_.Disabled })) {
                It "Disabled: $($Resource.Name)" {
                    $Resource.Disabled | Should Be $true
                }
                $disabled++
            }

            foreach ($Resource in $KeyPair.Value.Where( { $_.Conflict })) {
                It "Found Conflicts: $($Resource.Name)" {
                    $Resource.Conflict | Should Be $true
                } -Pending
                $conflict++
            }

            foreach ($Resource in $KeyPair.Value.Where( { $_.ResourceNotFound })) {
                It "Had Missing Resources: $($Resource.Name)" {
                    $Resource.ResourceNotFound | Should Be $true
                } -Skip
                $resourcenotfound++
            }

            foreach ($Resource in $KeyPair.Value.Where( { $_.ParsingError })) {
                It "Had No Parsing Errors: $($Resource.Name)" {
                    $Resource.ParsingError | Should Be $false
                }
                $parsingerror++
            }
        } *>> $(Join-Path -Path $OutputPath -ChildPath "Summary.log")

        $successes = $old_success
        $disabled = $old_disabled
        $conflict = $old_conflict
        $resourcenotfound = $old_resourcenotfound
        $parsingerror = $old_parsingerror

        Describe $Describe {

            foreach ($Resource in $KeyPair.Value.Where( { ($_.Disabled -eq $false) -and ($_.Conflict -eq $false) -and ($_.ResourceNotFound -eq $false) -and (($_.ParsingError -eq $false)) })) {
                It "Parsed: $($Resource.Name)" {
                    $Resource.Disabled | Should Be $false
                }
                $successes++
            }

            foreach ($Resource in $KeyPair.Value.Where( { $_.Disabled })) {
                It "Disabled: $($Resource.Name)" {
                    $Resource.Disabled | Should Be $true
                } -Skip
                $disabled++
            }

            foreach ($Resource in $KeyPair.Value.Where( { $_.Conflict })) {
                It "Found Conflicts: $($Resource.Name)" {
                    $Resource.Conflict | Should Be $true
                } -Pending
                $conflict++
            }

            foreach ($Resource in $KeyPair.Value.Where( { $_.ResourceNotFound })) {
                It "Had Missing Resources: $($Resource.Name)" {
                    $Resource.ResourceNotFound | Should Be $true
                } -Skip
                $resourcenotfound++
            }

            foreach ($Resource in $KeyPair.Value.Where( { $_.ParsingError })) {
                It "Had No Parsing Errors: $($Resource.Name)" {
                    $Resource.ParsingError | Should Be $false
                }
                $parsingerror++
            }
        }
    }

    $tmpBlock = {
        Write-Host "TOTALS" -ForegroundColor White
        Write-Host "------" -ForegroundColor White
        Write-Host "SUCCESSES: $successes" -ForegroundColor Green
        Write-Host "DISABLED: $disabled" -ForegroundColor Gray
        Write-Host "MISSING RESOURCES: $resourcenotfound" -ForegroundColor Yellow
        Write-Host "CONFLICTS: $conflict" -ForegroundColor Cyan
        Write-Host "PARSING ERROR: $resourcenotfound" -ForegroundColor Red
        Write-Host "______________________" -ForegroundColor White
        Write-Host "TOTAL: $($successes + $disabled + $conflict + $resourcenotfound + $parsingerror)" -ForegroundColor White
    }

    $tmpBlock.Invoke() *>> $(Join-Path -Path $OutputPath -ChildPath "Summary.log")
    $tmpBlock.Invoke()
}

Function Write-ProcessingHistory_NonPester {
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [bool]$Pass
    )

    Write-Host "Parsing Summary: $((Get-PsCallStack)[1].Command) - $(@("FAILED", "SUCCEEDED")[[int]$Pass])" -ForegroundColor @("RED", "GREEN")[[int]$Pass]
    Write-Host "---------------" -ForegroundColor White
    $History = Get-Variable ProcessingHistory
    $successes = $disabled = $conflict = $resourcenotfound = $parsingerror = 0
    foreach ($KeyPair in $History.Value.GetEnumerator()) {
        foreach ($Resource in $KeyPair.Value.Where( { ($_.Disabled -eq $false) -and ($_.Conflict -eq $false) })) {
            Write-Host "Parsed: $($Resource.Name)" -ForegroundColor Green
            $successes++
        }

        foreach ($Resource in $KeyPair.Value.Where( { $_.Disabled })) {
            Write-Host "Disabled: $($Resource.Name)" -ForegroundColor Gray
            $disabled++
        }

        foreach ($Resource in $KeyPair.Value.Where( { $_.Conflict })) {
            Write-Host "Found Conflicts: $($Resource.Name)" -ForegroundColor Cyan
            $conflict++
        }

        foreach ($Resource in $KeyPair.Value.Where( { $_.ResourceNotFound })) {
            Write-Host "Missing Resources: $($Resource.Name)" -ForegroundColor Yellow
            $resourcenotfound++
        }

        foreach ($Resource in $KeyPair.Value.Where( { $_.ParsingError })) {
            Write-Host "Parsing Error: $($Resource.Name)" -ForegroundColor Red
            $parsingerror++
        }
    }

    Write-Host "TOTALS" -ForegroundColor White
    Write-Host "------" -ForegroundColor White
    Write-Host "SUCCESSES: $successes" -ForegroundColor Green
    Write-Host "DISABLED: $disabled" -ForegroundColor Gray
    Write-Host "MISSING RESOURCES: $resourcenotfound" -ForegroundColor Yellow
    Write-Host "CONFLICTS: $conflict" -ForegroundColor Cyan
    Write-Host "PARSING ERROR: $resourcenotfound" -ForegroundColor Red
    Write-Host "______________________" -ForegroundColor White
    Write-Host "TOTAL: $($successes + $disabled + $conflict + $resourcenotfound + $parsingerror)" -ForegroundColor White
}

# SIG # Begin signature block
# MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCt0Hfkc36t5nPR
# Waf2GumuS0D9IOl0QENCv2lUZ5pEjqCCDYEwggX/MIID56ADAgECAhMzAAABh3IX
# chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB
# znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH
# sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d
# weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ
# itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV
# Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw
# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu
# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy
# S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K
# NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV
# BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr
# qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx
# zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe
# yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g
# yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf
# AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI
# 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5
# GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea
# jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS
# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla
# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT
# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG
# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S
# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz
# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7
# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u
# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33
# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl
# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP
# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB
# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF
# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM
# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ
# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud
# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO
# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0
# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p
# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw
# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA
# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY
# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj
# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd
# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ
# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf
# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ
# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j
# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN
# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgFiBtPDRp
# 0QGeiJsSI/sreXYqOdZtZt1nhsA5zFbQXhwwQgYKKwYBBAGCNwIBDDE0MDKgFIAS
# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN
# BgkqhkiG9w0BAQEFAASCAQDCA9qwQhbZ1YtzcjeEipoEO0p4jZxqjU+w1oSvb9r0
# dBgO1aG2XzUI81bpoyKvLeFGWXFGZjff7WZo4jRzuf6hP3CELzbRWijF2vM19W1V
# fRz5PYPfuJ0w1DJoR3ztbpBe8+CU896qcGumFj+hn2rt+P6ENSSVmcgqOnfX0E5Y
# HyvKKG1MXuAXDK6uI9xSSP/CnHGdkjAGG5v1Ct89/1zmkLtYpuxWn+82tyauNf8x
# hufKKoL1GTMNIwT4/ZzOds7l+p0mLmwtze2BZ/1OhrygXadYSuxIzsoT7IJ1PICn
# m8xWCBDDJmRDYRg6tk+b0inOBy2Hh/dnKB09Lp2HpodXoYIS8TCCEu0GCisGAQQB
# gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME
# AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB
# MDEwDQYJYIZIAWUDBAIBBQAEIB2ikt3Qp5NXOWbFbERlKpYAjrQLCsezM5DTAwak
# 6ASBAgZf24oIOJwYEzIwMjEwMTA0MTQwMTE5LjcwNVowBIACAfSggdSkgdEwgc4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p
# Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg
# VFNTIEVTTjowQTU2LUUzMjktNEQ0RDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABJy9uo++RqBmoAAAA
# AAEnMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
# MB4XDTE5MTIxOTAxMTQ1OVoXDTIxMDMxNzAxMTQ1OVowgc4xCzAJBgNVBAYTAlVT
# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy
# YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjowQTU2
# LUUzMjktNEQ0RDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj
# ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPgB3nERnk6fS40vvWeD
# 3HCgM9Ep4xTIQiPnJXE9E+HkZVtTsPemoOyhfNAyF95E/rUvXOVTUcJFL7Xb16jT
# KPXONsCWY8DCixSDIiid6xa30TiEWVcIZRwiDlcx29D467OTav5rA1G6TwAEY5rQ
# jhUHLrOoJgfJfakZq6IHjd+slI0/qlys7QIGakFk2OB6mh/ln/nS8G4kNRK6Do4g
# xDtnBSFLNfhsSZlRSMDJwFvrZ2FCkaoexd7rKlUNOAAScY411IEqQeI1PwfRm3aW
# bS8IvAfJPC2Ah2LrtP8sKn5faaU8epexje7vZfcZif/cbxgUKStJzqbdvTBNc93n
# /Z8CAwEAAaOCARswggEXMB0GA1UdDgQWBBTl9JZVgF85MSRbYlOJXbhY022V8jAf
# BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH
# hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU
# aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF
# BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0
# YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG
# AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQAKyo180VXHBqVnjZwQy7NlzXbo2+W5
# qfHxR7ANV5RBkRkdGamkwUcDNL+DpHObFPJHa0oTeYKE0Zbl1MvvfS8RtGGdhGYG
# CJf+BPd/gBCs4+dkZdjvOzNyuVuDPGlqQ5f7HS7iuQ/cCyGHcHYJ0nXVewF2Lk+J
# lrWykHpTlLwPXmCpNR+gieItPi/UMF2RYTGwojW+yIVwNyMYnjFGUxEX5/DtJjRZ
# mg7PBHMrENN2DgO6wBelp4ptyH2KK2EsWT+8jFCuoKv+eJby0QD55LN5f8SrUPRn
# K86fh7aVOfCglQofo5ABZIGiDIrg4JsV4k6p0oBSIFOAcqRAhiH+1spCMIIGcTCC
# BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv
# b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN
# MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
# aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw
# DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0
# VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw
# RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe
# dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx
# Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G
# kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA
# AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7
# fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC
# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX
# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v
# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI
# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g
# AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93
# d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB
# BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA
# bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh
# IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS
# +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK
# kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon
# /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi
# PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/
# fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII
# YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0
# cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a
# KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ
# cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+
# NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT
# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP
# cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjow
# QTU2LUUzMjktNEQ0RDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy
# dmljZaIjCgEBMAcGBSsOAwIaAxUAs5W4TmyDHMRM7iz6mgGojqvXHzOggYMwgYCk
# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF
# AOOdGiswIhgPMjAyMTAxMDQwODM4MDNaGA8yMDIxMDEwNTA4MzgwM1owdzA9Bgor
# BgEEAYRZCgQBMS8wLTAKAgUA450aKwIBADAKAgEAAgIYLgIB/zAHAgEAAgIRyDAK
# AgUA455rqwIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB
# AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAJ4PKChsXitPFQIR
# 8lAGCG5ZZp7WPLbNzuQPiOwXRQFSghdEnMuwDWmJcchuHuKgJtG4SrVZkdeFSOSY
# ssTpTXkBKjeKmwBSvQsSF5YW30d2m8p/TB/WtlyItDYwEv2vqEz5dofOrT7qasSJ
# KaSe6yMb+K0mJLgZUqs1sctbnSgZMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTACEzMAAAEnL26j75GoGagAAAAAAScwDQYJYIZIAWUD
# BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B
# CQQxIgQgHuPjy6BJO/vUxs37Dlfc0I4g7H98T2nZuNbW/evERV0wgfoGCyqGSIb3
# DQEJEAIvMYHqMIHnMIHkMIG9BCAbkuhLEoYdahb/BUyVszO2VDi6kB3MSaof/+8u
# 7SM+IjCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB
# Jy9uo++RqBmoAAAAAAEnMCIEIFnWEFa2d4NiKHOQp50WXXR4qCccxNzQOrNDwNUP
# CgrOMA0GCSqGSIb3DQEBCwUABIIBAFZ2G9oP52EFw5jn5x3aCH3yNM+EXfkV1/+8
# Yy6A7Qmt7JP8hdlz553FxF/SzCRL4Z1cBkpsLHJg4vOqNtIPv62GpRP8PHvvC+Ys
# b00szIeY6QHyb+kfh0zyqo3ZeZVnCRZwBlZCjv3gLAvDhVaYusKkV99Dp99shfcx
# qvkX2/woPABPAVQSqX4OKRDUgavqR6M84OJyufY/ehXfQ+/jObCmUgpjLli8KWhE
# lwnUSrw8n24hhMk46ZK4K5h9yRA168hlv3WCw3rZPCMNLZpjX0iQ2qM0UoC33dKS
# 4SRrN0kxFi4KcX+rS2ZZqiZZVjLgfOnUqJFizgEgVjz9YmFpbaI=
# SIG # End signature block