Parse-OSOTConfig.ps1

<#PSScriptInfo
 
.VERSION 0.7.1
 
.GUID 9665fc5d-be92-46e9-acac-cfd3637a3b0a
 
.AUTHOR Evgenij Smirnov
 
.COMPANYNAME metaBPA.org
 
.COPYRIGHT 2020 metaBPA.org
 
.TAGS
 
.LICENSEURI
 
.PROJECTURI https://metabpa.org/projects/various-scripts/osot-parser/
 
.ICONURI https://metabpa.org/wp-content/uploads/2019/11/metaBPA-150x150.ico
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
 
#>


<#
 
.SYNOPSIS
Parses an OSOT configuration file and generates actual executable scripts (.cmd and .ps1).
 
.DESCRIPTION
Parses an OSOT (VMware OS Optimization Tool) configuration file and generates actual executable scripts. These can then be invoked without using the actual OSOT Tool, e.g. as part of a deployment task sequence. The commands are split into classical batch shell (.cmd) and PowerShell (.ps1) commands, with the choice of generating either batch or PS commands for a. disabling services, and b. disabling scheduled tasks.
See project site linked under "Info" for more details.
 
.PARAMETER OSOTFIle
Full path to XML file generated by OSOT.
 
.PARAMETER OutPath
Full path to folder where generated files will be created.
 
.PARAMETER UsePowerShellForServiceCommands
Output 'Set-Service' and 'Stop-Service' instead of 'sc config' and 'sc stop'.
 
.PARAMETER UsePowerShellForSchTasksCommands
Output 'Enable-ScheduledTask' and 'Disable-ScheduledTask' instead of 'schtasks.exe'.
 
.PARAMETER AddCommentsToOutput
Incorporate commets from the XML file into the script
 
.PARAMETER PassThru
Output the actual commands to the pipeline instead of statistics.
 
.PARAMETER MaskExtensions
Make the output files non-executable by adding.txt to the full path.
 
.PARAMETER SuppressFileOutput
Suppress the creation of output files.
 
.INPUTS
 
None. You cannot pipe objects to Parse-OSOTConfig.ps1.
 
.OUTPUTS
 
If -PassThru is specified, an array of PSCustomObjects each representing a command to run on an image.
If -PassThru is not specified, an PSCustomObject that contains some statistics about the processed config.
  
.EXAMPLE
 
./Parse-OSOTConfig.ps1 -OSOTFile c:\temp\Win10Config.xml -OutPath c:\temp\osotcommands -PassThru
 
.EXAMPLE
 
./Parse-OSOTConfig.ps1 -OSOTFile c:\temp\Win10Config.xml -OutPath c:\temp\osotcommands -UsePowerShellForServiceCommands -AddCommentsToOutput
 
.LINK
 
https://metabpa.org/projects/various-scripts/osot-parser/
 
.LINK
 
https://flings.vmware.com/vmware-os-optimization-tool
 
#>
 
[CmdletBinding()]
Param(
    [Parameter(Mandatory=$true)][System.IO.FileInfo]$OSOTFile,
    [Parameter(Mandatory=$true)][System.IO.FileInfo]$OutPath,
    [Parameter(Mandatory=$false)][switch]$UsePowerShellForServiceCommands,
    [Parameter(Mandatory=$false)][switch]$UsePowerShellForSchTasksCommands,
    [Parameter(Mandatory=$false)][switch]$AddCommentsToOutput,
    [Parameter(Mandatory=$false)][switch]$PassThru,
    [Parameter(Mandatory=$false)][switch]$MaskExtensions,
    [Parameter(Mandatory=$false)][switch]$SuppressFileOutput
)
#region functions
function Make-Content {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)][string]$OutFile,
        [Parameter(Mandatory=$false)][string]$FileComment,
        [Parameter(Mandatory=$true)][PSCustomObject[]]$ContentData
    )
    if ($ContentData.Where({$_.lang -eq 'powershell'}).Count -gt 0) {
        $ps_out_file = "$OutPath\$($OutFile).ps1"
        if ($MaskExtensions) { $ps_out_file = $ps_out_file + ".txt" }
        "<#`r`n`t$FileComment from OST File $($OSOTFile.FullName)`r`n#>`r`n" | Set-Content -Path $ps_out_file -Encoding UTF8 -Force
    }
    if ($ContentData.Where({$_.lang -eq 'cmd'}).Count -gt 0) {
        $cmd_out_file = "$OutPath\$($OutFile).cmd"
        if ($MaskExtensions) { $cmd_out_file = $cmd_out_file + ".txt" }
        "@ECHO OFF`r`nREM $FileComment from OST File $($OSOTFile.FullName)`r`n" | Set-Content -Path $cmd_out_file -Encoding UTF8 -Force
    }
    foreach ($action in $ContentData.where({!$_.defuser})) {
        if ($action.lang -eq 'cmd') {
            if ($AddCommentsToOutput) {
                "REM $($action.group) | $($action.comment)" | Add-Content $cmd_out_file -Encoding UTF8
            }
            $action.line | Add-Content $cmd_out_file -Encoding UTF8
        } elseif ($action.lang -eq 'powershell') {
            if ($AddCommentsToOutput) {
                "# $($action.group) | $($action.comment)" | Add-Content $ps_out_file -Encoding UTF8
            }
            $action.line | Add-Content $ps_out_file -Encoding UTF8
        }
    } 
    if ($stats.DefaultUserActions -gt 0) {
        'reg LOAD "HKEY_USERS\temp" C:\Users\Default\NTUSERS.DAT' | Add-Content $cmd_out_file -Encoding UTF8
        foreach ($action in $ContentData.where({$_.defuser})) {
            if ($action.lang -eq 'cmd') {
                if ($AddCommentsToOutput) {
                    "REM $($action.group) | $($action.comment)" | Add-Content $cmd_out_file -Encoding UTF8
                }
                $action.line | Add-Content $cmd_out_file -Encoding UTF8
            } elseif ($action.lang -eq 'powershell') {
                Write-Warning "PowerShell action in default user context: $($action.line)"
            }
        } 
        'reg UNLOAD "HKEY_USERS\temp"' | Add-Content $cmd_out_file -Encoding UTF8
    }

}
#endregion
#region init
if (Test-Path -Path $OSOTFile -PathType Leaf) {
    try {
        $OSOTConfig = [xml](Get-Content -Path $OSOTFile)
    } catch {
        Write-Warning "Error parsing XML: $($_.Exception.Message)"
        return 7
    }
} else {
    Write-Warning "File not found: $OSOTFile"
    return 2
}
if ($SuppressFileOutput -and ($MaskExtensions -or $AddCommentsToOutput)) {
    Write-Warning "You specified -SuppressFileOutput together with one of the file modifiers (-MaskExtensions and -AddCommentsToOutput). No output files will be created."
}
$cmd = @()
$unprocessed = 0
$nline = 0
#endregion
#region parse
foreach ($ActionGroup in $OSOTConfig.sequence.group.ChildNodes) {
    foreach ($ActionStepBlock in $ActionGroup.childNodes) {
        $ActionStep = $ActionStepBlock.action 
        $nline++
        switch ($ActionStep.type) {
            'Registry' {
                $regcmd = $null
                if ($ActionStep.params.keyName -eq $null) {
                    Write-Warning "Null key value encountered!"
                    $unprocessed++
                } else {
                    $xkey = (($ActionStep.params.keyName.Trim() -replace "HKLM","HKEY_LOCAL_MACHINE") -replace "HKU","HKEY_USERS") -replace "HKCU","HKEY_CURRENT_USER"
                    $xval = $null
                    $xtype = $null
                    $xdata = $null
                    switch ($ActionStep.command) {
                        'ADD' {
                            $xval = $ActionStep.params.valueName
                            $xdata = $ActionStep.params.data
                            $xtype = $ActionStep.params.type
                            if ($xval -eq $null) {
                                if ($xtype -ne $null) {
                                    $xval = '(Default)'
                                }
                            } else {
                                $xval = $xval.Trim()
                            }
                            if ($xval) {
                                $xval = ('/v "{0}"' -f $xval)
                                if ($xtype -eq 'REG_SZ') { 
                                    $xdata = ('/d "{0}"' -f $xdata) 
                                } else  { 
                                    $xdata = ('/d {0}' -f $xdata) 
                                }
                                $xtype = ('/t {0}' -f $xtype)
                            }
                            $regcmd = "$(('reg ADD "{0}" {1} {2} {3}' -f $xkey,$xval,$xtype,$xdata).Trim()) /f"
                        }
                        'DELETEVALUE' {
                            $xval = $ActionStep.params.valueName
                            if ($xval) { $xkey = ('{0}\{1}' -f $xkey,$xval) }
                            $regcmd = "$('reg DELETE "{0}"' -f $xkey) /f"
                        }
                        'LOAD' { <# nuthin' to do, will be added automatically #> }
                        'UNLOAD' { <# nuthin' to do, will be added automatically #> }
                        default { 
                            Write-Warning "Unknown registry operation: $($ActionStep.command)" 
                            $unprocessed++
                        }
                    }
                    if ($null -ne $regcmd) {
                        $cmd += [PSCustomObject]@{
                            'nline' = $nline
                            'lang' = 'cmd'
                            'line' = $regcmd
                            'recommended' = ($ActionStepBlock.category -eq 'recommended')
                            'mandatory' = ($ActionStepBlock.category -eq 'mandatory')
                            'defuser' = ($regcmd -match 'HKEY_USERS\\temp\\')
                            'comment' = $ActionStepBlock.description
                            'group' = $ActionGroup.name
                        }
                    }
                }
            }
            'Service' {
                $xname = $ActionStep.params.serviceName
                $xmode = $ActionStep.params.startMode
                if ($null -ne $xname) {
                    if ($null -ne $xmode) {
                        if ($UsePowerShellForServiceCommands) {
                            $cmd += [PSCustomObject]@{
                                'nline' = $nline
                                'lang' = 'powershell'
                                'line' = 'Set-Service "{0}" -StartupType {1}' -f $xname,$xmode
                                'recommended' = ($ActionStepBlock.category -eq 'recommended')
                                'mandatory' = ($ActionStepBlock.category -eq 'mandatory')
                                'defuser' = $false
                                'comment' = $ActionStepBlock.description
                                'group' = $ActionGroup.name
                            }
                            if ($xmode -ne 'AUTOMATIC') {
                                $cmd += [PSCustomObject]@{
                                    'nline' = $nline
                                    'lang' = 'powershell'
                                    'line' = 'Stop-Service "{0}" -Force' -f $xname
                                    'recommended' = ($ActionStepBlock.category -eq 'recommended')
                                    'mandatory' = ($ActionStepBlock.category -eq 'mandatory')
                                    'defuser' = $false
                                    'comment' = $ActionStepBlock.description
                                    'group' = $ActionGroup.name
                                }
                            }
                        } else {
                            $cmd += [PSCustomObject]@{
                                'nline' = $nline
                                'lang' = 'cmd'
                                'line' = 'sc config "{0}" start={1}' -f $xname,$xmode
                                'recommended' = ($ActionStepBlock.category -eq 'recommended')
                                'mandatory' = ($ActionStepBlock.category -eq 'mandatory')
                                'defuser' = $false
                                'comment' = $ActionStepBlock.description
                                'group' = $ActionGroup.name
                            }
                            if ($xmode -ne 'AUTOMATIC') {
                                $cmd += [PSCustomObject]@{
                                    'nline' = $nline
                                    'lang' = 'cmd'
                                    'line' = 'sc stop "{0}"' -f $xname
                                    'recommended' = ($ActionStepBlock.category -eq 'recommended')
                                    'mandatory' = ($ActionStepBlock.category -eq 'mandatory')
                                    'defuser' = $false
                                    'comment' = $ActionStepBlock.description
                                    'group' = $ActionGroup.name
                                }
                            }
                        }
                    } else {
                        Write-Warning "Service start mode empty at step $($ActionStepBlock.name)"
                        $unprocessed++
                    }
                } else {
                    Write-Warning "Service name empty at step $($ActionStepBlock.name)"
                    $unprocessed++
                }
            }
            'SchTasks' { 
                $xname = $ActionStep.params.TaskName
                $xmode = $ActionStep.params.Status
                if ($null -ne $xname) {
                    if ($null -ne $xmode) {
                        if ($UsePowerShellForServiceCommands) {
                            if ($xmode -eq 'DISABLED') {
                                $xmode = 'Disable'
                            } else {
                                $xmode = 'Enable'                                               
                            }
                            if ($xname -match "\\") {
                                $xpath = "\" + $xname.Substring(0,$xname.LastIndexOf("\") + 1)
                                $xname = $xname.Substring($xname.LastIndexOf("\") + 1)
                            } else {
                                $xpath = "\"
                            }
                            $cmd += [PSCustomObject]@{
                                'nline' = $nline
                                'lang' = 'powershell'
                                'line' = '{1}-ScheduledTask -TaskName "{0}" -TaskPath "{2}"' -f $xname,$xmode,$xpath
                                'recommended' = ($ActionStepBlock.category -eq 'recommended')
                                'mandatory' = ($ActionStepBlock.category -eq 'mandatory')
                                'defuser' = $false
                                'comment' = $ActionStepBlock.description
                                'group' = $ActionGroup.name
                            }
                        } else {
                            if ($xmode -eq 'DISABLED') {
                                $xmode = 'disable'
                            } else {
                                $xmode = 'enable'                                               
                            }
                            $cmd += [PSCustomObject]@{
                                'nline' = $nline
                                'lang' = 'cmd'
                                'line' = 'schtasks /change /tn "{0}" /{1}' -f $xname,$xmode
                                'recommended' = ($ActionStepBlock.category -eq 'recommended')
                                'mandatory' = ($ActionStepBlock.category -eq 'mandatory')
                                'defuser' = $false
                                'comment' = $ActionStepBlock.description
                                'group' = $ActionGroup.name
                            }
                        }
                    } else {
                        Write-Warning "SchTask status empty at step $($ActionStepBlock.name)"
                        $unprocessed++
                    }
                } else {
                    Write-Warning "SchTask name empty at step $($ActionStepBlock.name)"
                    $unprocessed++
                }
            }
            'ShellExecute' {
                $xcmd = $ActionStep.command
                if (($xcmd -match '^Powershell "& {(?<pscmd>[a-zA-Z\d]+\-[a-zA-Z\d]+\s.*)}"$') -or ($xcmd -match '^Powershell (?<pscmd>[a-zA-Z\d]+\-[a-zA-Z\d]+\s.*)$')) {
                    $cmd += [PSCustomObject]@{
                        'nline' = $nline
                        'lang' = 'powershell'
                        'line' = $Matches['pscmd']
                        'recommended' = ($ActionStepBlock.category -eq 'recommended')
                        'mandatory' = ($ActionStepBlock.category -eq 'mandatory')
                        'defuser' = $false
                        'comment' = $ActionStepBlock.description
                        'group' = $ActionGroup.name
                    }
                } elseif ($xcmd -match '^Powershell "& {(?<batcmd>.*)}"$') {
                    $cmd += [PSCustomObject]@{
                        'nline' = $nline
                        'lang' = 'cmd'
                        'line' = $Matches['batcmd']
                        'recommended' = ($ActionStepBlock.category -eq 'recommended')
                        'mandatory' = ($ActionStepBlock.category -eq 'mandatory')
                        'defuser' = $false
                        'comment' = $ActionStepBlock.description
                        'group' = $ActionGroup.name
                    }
                } elseif ($xcmd -match '^Powershell (?<batcmd>.*)$') {
                    $cmd += [PSCustomObject]@{
                        'nline' = $nline
                        'lang' = 'cmd'
                        'line' = $Matches['batcmd']
                        'recommended' = ($ActionStepBlock.category -eq 'recommended')
                        'mandatory' = ($ActionStepBlock.category -eq 'mandatory')
                        'defuser' = $false
                        'comment' = $ActionStepBlock.description
                        'group' = $ActionGroup.name
                    }
                } else {
                    $cmd += [PSCustomObject]@{
                        'nline' = $nline
                        'lang' = 'cmd'
                        'line' = $xcmd
                        'recommended' = ($ActionStepBlock.category -eq 'recommended')
                        'mandatory' = ($ActionStepBlock.category -eq 'mandatory')
                        'defuser' = $false
                        'comment' = $ActionStepBlock.description
                        'group' = $ActionGroup.name
                    }
                }
            }
            default { 
                if ($ActionStep.type -ne $null) {
                    Write-Warning "Unknown action type: $($ActionStep.type) at step $($ActionStepBlock.name)"
                    $unprocessed++
                }
            }
        }
    }
}
#endregion
#region stats
$stats = [PSCustomObject]@{
    'OSOTFile' = $OSOTFile.FullName
    'TotalActions' = $cmd.Count
    'CMDActions' = $cmd.Where({$_.lang -eq 'cmd'}).Count
    'PSActions' = $cmd.Where({$_.lang -eq 'powershell'}).Count
    'DefaultUserActions' = $cmd.Where({$_.defuser}).Count
    'MandatoryActions' = $cmd.Where({$_.mandatory}).Count
    'RecommendedActions' = $cmd.Where({$_.recommended}).Count
    'OptionalActions' = $cmd.Where({!$_.recommended -and !$_.mandatory}).Count
    'UnprocessedSteps' = $unprocessed
}
#endregion
#region output
if (!$SuppressFileOutput) {
    if (!(Test-Path $OutPath -Pathtype Container)) {
        New-Item $OutPath -ItemType Directory -Force
    }
    [ordered]@{'stats' = $stats; 'commands' = $cmd} | ConvertTo-Json -Depth 4 | Set-Content "$OutPath\osot_processed.json" -Force
    Make-Content -OutFile 'all_actions' -FileComment 'All Actions' -ContentData $cmd
    Make-Content -OutFile 'mandatory_actions' -FileComment 'Mandatory Actions' -ContentData ($cmd.Where({$_.mandatory}))
    Make-Content -OutFile 'recommended_actions' -FileComment 'Recommended Actions' -ContentData ($cmd.Where({$_.recommended}))
    Make-Content -OutFile 'optional_actions' -FileComment 'Optional Actions' -ContentData ($cmd.Where({!$_.mandatory -and !$_.recommended}))
}
#endregion
if ($PassThru) {
    return $cmd
} else {
    return $stats
}
# SIG # Begin signature block
# MIIefQYJKoZIhvcNAQcCoIIebjCCHmoCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQURb2EqgZSVisw24ZOfnhfyDKI
# 7CugghnAMIIFQTCCAymgAwIBAgITXwAAAAIjZCsPuYSflAAAAAAAAjANBgkqhkiG
# 9w0BAQsFADBcMQswCQYDVQQGEwJERTEPMA0GA1UEBxMGQmVybGluMRQwEgYDVQQK
# EwttZXRhQlBBLm9yZzEMMAoGA1UECxMDRGV2MRgwFgYDVQQDEw9tZXRhQlBBIFJv
# b3QgQ0EwHhcNMTkxMTA4MjEwNzI4WhcNMjAxMTA4MjExNzI4WjBcMQswCQYDVQQG
# EwJERTEPMA0GA1UEBxMGQmVybGluMRQwEgYDVQQKEwttZXRhQlBBLm9yZzEMMAoG
# A1UECxMDRGV2MRgwFgYDVQQDEw9FdmdlbmlqIFNtaXJub3YwggEiMA0GCSqGSIb3
# DQEBAQUAA4IBDwAwggEKAoIBAQDBL4oFbzMU7zkuwMFsG8Ckbk73VtcvE/gz5CFd
# Rf3f/WVMe27r6VQLUfeaLYwjavfe9MaSHngXhC0Ivsbf6IlKtY1oXJFKXuEnNvzC
# s3njl5LXVoWf29wGmOOAbPWBlI7+nBwvcNRHYnZ4ZaLSO9cdhre9gARTvMptUUL9
# 3Njf3jw9iwdLRg7wMk1TB3yB7nlHHd0G8YiPzlltOV9sesfpnFuorR2IPwkfaEfu
# Z6t/nHsuWtvliaT++PbDv9d0tnKiQVJ+h9cwMoTO8kIbg7Q52pT+paMrcEn7hyN+
# XjyG/oJUb6IyDOMgv2Fa+MJ9SWnT9z4Ueap4/JJhkiv8qaf5AgMBAAGjgfswgfgw
# DgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBRm
# y7quPiRE14SnwXiztcO0WxBDYzAfBgNVHSMEGDAWgBT5ujnudnaVxT34rTRrGEb0
# f87i+DA7BgNVHR8ENDAyMDCgLqAshipodHRwOi8vcGtpLm1ldGFicGEub3JnL21l
# dGFCUEFfUm9vdF9DQS5jcmwwRgYIKwYBBQUHAQEEOjA4MDYGCCsGAQUFBzAChipo
# dHRwOi8vcGtpLm1ldGFicGEub3JnL21ldGFCUEFfUm9vdF9DQS5jcnQwDAYDVR0T
# AQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAc05mB0AuoLmHwh5aPGw+VYSlXzDg
# ugs1HwjI3iNe6FQZ1a+E1yupvdLHFrjs+7nBtRzQNdbf6Gs2ulQgaxKzC+qtrgiu
# vkiO7q9mPza1WkkXCDtVatyMnRt6i9aK/Qy0B+lQxVL3gZhHKNiBlZd9lZbps699
# SHuT2lyytJEIQdXWKEm137+3FHFKIso9cQ820Lzh+251VukxuqRHR2A5BhiHRPww
# phRzMyfxGwUZWx1WSqgefEyHcqft562LnYluDXPxfEUNRPtTzAMbR8MH6zuom6QE
# pA3UzrJvwgYGnKkGyfj7SQijvvukfwHbcTRbbq73Jv6YKnFlwG8A6BKDYWYpQdgT
# 7ENlXhJ1TEZe+8O4XOUZokYQoFJbOn9dz2AyKJCQDYev3+uDqNhlVavcMG8YXBjv
# W/Y6NbUb1W2W+kDFtdCG74sXgTZyvqsDsEb57UniGxo0+/uYqxRnFexruJ/Il3OO
# kLv2Up4aov018yowmSYriJY2u9B7NyzPqNTh8KYo/RCm9INUn/SA3JNJ0g9udssR
# tkdbPCpq+e1j9Xjf6V5qkXQcNMuuZLf6RswS/tsuaqlhqRrHDdk4I+EcDj5o0n+C
# ur9horiACMCK5fjpgogRaqkuzCshtO1qc20F9tk0tUr3iD8N63MSbQBmxeyP16Et
# 7ZEHLtu+fmM06ckwggZqMIIFUqADAgECAhADAZoCOv9YsWvW1ermF/BmMA0GCSqG
# SIb3DQEBBQUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFz
# c3VyZWQgSUQgQ0EtMTAeFw0xNDEwMjIwMDAwMDBaFw0yNDEwMjIwMDAwMDBaMEcx
# CzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDElMCMGA1UEAxMcRGlnaUNl
# cnQgVGltZXN0YW1wIFJlc3BvbmRlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
# AQoCggEBAKNkXfx8s+CCNeDg9sYq5kl1O8xu4FOpnx9kWeZ8a39rjJ1V+JLjntVa
# Y1sCSVDZg85vZu7dy4XpX6X51Id0iEQ7Gcnl9ZGfxhQ5rCTqqEsskYnMXij0ZLZQ
# t/USs3OWCmejvmGfrvP9Enh1DqZbFP1FI46GRFV9GIYFjFWHeUhG98oOjafeTl/i
# qLYtWQJhiGFyGGi5uHzu5uc0LzF3gTAfuzYBje8n4/ea8EwxZI3j6/oZh6h+z+yM
# DDZbesF6uHjHyQYuRhDIjegEYNu8c3T6Ttj+qkDxss5wRoPp2kChWTrZFQlXmVYw
# k/PJYczQCMxr7GJCkawCwO+k8IkRj3cCAwEAAaOCAzUwggMxMA4GA1UdDwEB/wQE
# AwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMIIBvwYD
# VR0gBIIBtjCCAbIwggGhBglghkgBhv1sBwEwggGSMCgGCCsGAQUFBwIBFhxodHRw
# czovL3d3dy5kaWdpY2VydC5jb20vQ1BTMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBB
# AG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBh
# AHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBj
# AGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABT
# ACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABB
# AGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBh
# AGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBh
# AHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAu
# MAsGCWCGSAGG/WwDFTAfBgNVHSMEGDAWgBQVABIrE5iymQftHt+ivlcNK2cCzTAd
# BgNVHQ4EFgQUYVpNJLZJMp1KKnkag0v0HonByn0wfQYDVR0fBHYwdDA4oDagNIYy
# aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5j
# cmwwOKA2oDSGMmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3Vy
# ZWRJRENBLTEuY3JsMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDov
# L29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5k
# aWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURDQS0xLmNydDANBgkqhkiG9w0B
# AQUFAAOCAQEAnSV+GzNNsiaBXJuGziMgD4CH5Yj//7HUaiwx7ToXGXEXzakbvFoW
# OQCd42yE5FpA+94GAYw3+puxnSR+/iCkV61bt5qwYCbqaVchXTQvH3Gwg5QZBWs1
# kBCge5fH9j/n4hFBpr1i2fAnPTgdKG86Ugnw7HBi02JLsOBzppLA044x2C/jbRcT
# Bu7kA7YUq/OPQ6dxnSHdFMoVXZJB2vkPgdGZdA0mxA5/G7X1oPHGdwYoFenYk+VV
# FvC7Cqsc21xIJ2bIo4sKHOWV2q7ELlmgYd3a822iYemKC23sEhi991VUQAOSK2vC
# UcIKSK+w1G7g9BQKOhvjjz3Kr2qNe9zYRDCCBs0wggW1oAMCAQICEAb9+QOWA63q
# AArrPye7uhswDQYJKoZIhvcNAQEFBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoT
# DERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UE
# AxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTA2MTExMDAwMDAwMFoX
# DTIxMTExMDAwMDAwMFowYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0
# IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNl
# cnQgQXNzdXJlZCBJRCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
# AQEA6IItmfnKwkKVpYBzQHDSnlZUXKnE0kEGj8kz/E1FkVyBn+0snPgWWd+etSQV
# wpi5tHdJ3InECtqvy15r7a2wcTHrzzpADEZNk+yLejYIA6sMNP4YSYL+x8cxSIB8
# HqIPkg5QycaH6zY/2DDD/6b3+6LNb3Mj/qxWBZDwMiEWicZwiPkFl32jx0PdAug7
# Pe2xQaPtP77blUjE7h6z8rwMK5nQxl0SQoHhg26Ccz8mSxSQrllmCsSNvtLOBq6t
# hG9IhJtPQLnxTPKvmPv2zkBdXPao8S+v7Iki8msYZbHBc63X8djPHgp0XEK4aH63
# 1XcKJ1Z8D2KkPzIUYJX9BwSiCQIDAQABo4IDejCCA3YwDgYDVR0PAQH/BAQDAgGG
# MDsGA1UdJQQ0MDIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUF
# BwMEBggrBgEFBQcDCDCCAdIGA1UdIASCAckwggHFMIIBtAYKYIZIAYb9bAABBDCC
# AaQwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMt
# cmVwb3NpdG9yeS5odG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBz
# AGUAIABvAGYAIAB0AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBv
# AG4AcwB0AGkAdAB1AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAg
# AHQAaABlACAARABpAGcAaQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAg
# AHQAaABlACAAUgBlAGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBt
# AGUAbgB0ACAAdwBoAGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0
# AHkAIABhAG4AZAAgAGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABo
# AGUAcgBlAGkAbgAgAGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9
# bAMVMBIGA1UdEwEB/wQIMAYBAf8CAQAweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUF
# BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6
# Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5j
# cnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9E
# aWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmw0LmRp
# Z2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwHQYDVR0OBBYE
# FBUAEisTmLKZB+0e36K+Vw0rZwLNMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6en
# IZ3zbcgPMA0GCSqGSIb3DQEBBQUAA4IBAQBGUD7Jtygkpzgdtlspr1LPUukxR6tW
# XHvVDQtBs+/sdR90OPKyXGGinJXDUOSCuSPRujqGcq04eKx1XRcXNHJHhZRW0eu7
# NoR3zCSl8wQZVann4+erYs37iy2QwsDStZS9Xk+xBdIOPRqpFFumhjFiqKgz5Js5
# p8T1zh14dpQlc+Qqq8+cdkvtX8JLFuRLcEwAiR78xXm8TBJX/l/hHrwCXaj++wc4
# Tw3GXZG5D2dFzdaD7eeSDY2xaYxP+1ngIw/Sqq4AfO6cQg7PkdcntxbuD8O9fAqg
# 7iwIVYUiuOsYGk38KiGtSTGDR5V3cdyxG0tLHBCcdxTBnU8vWpUIKRAmMIIHODCC
# BSCgAwIBAgIQEw12KjF18pBAJDt0qQwq6jANBgkqhkiG9w0BAQsFADBcMQswCQYD
# VQQGEwJERTEPMA0GA1UEBxMGQmVybGluMRQwEgYDVQQKEwttZXRhQlBBLm9yZzEM
# MAoGA1UECxMDRGV2MRgwFgYDVQQDEw9tZXRhQlBBIFJvb3QgQ0EwHhcNMTkxMTA4
# MjAwMDU5WhcNMzkxMTA4MjAxMDU0WjBcMQswCQYDVQQGEwJERTEPMA0GA1UEBxMG
# QmVybGluMRQwEgYDVQQKEwttZXRhQlBBLm9yZzEMMAoGA1UECxMDRGV2MRgwFgYD
# VQQDEw9tZXRhQlBBIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
# AoICAQCw2DkS2wXYAPJTQVkEccotAn/ba+ZxjYqvZOnOO1QbHk8XDSZCFrTADHjU
# BvYZPVZDg/OPg082TOmln7uVfipsHt29WSWCVco6WTGbb2gHfTFpAoltfdRnsTWt
# +J0l69tdPWVVxMpvsNv1S/FrfkEVLBW6k9glHSjmvz6uJNvF/UKtkkkElpPtbLsS
# jNCwIqXJ7maZTNEEUGnmDYHhj2Bi7Ex5w2fKw5k5IvCr4BO6LLIim9HialiT0pMK
# JMkWSQmQqDlC/WUjh5cmOD4fRq2cL8QiiJjz1mpbALd9zz7F9KO/7F9WZXQaiEK/
# vgBsB8LnjWlE/qh9eoWSw1ib+Pp2Nxs2Wmk433Di4pgkad4eZHC0ENF8/8VU/Via
# a2PQJdwoK/ouAvUJz+JQ/q7hNClpoNa7dbgrtw63W0DeC7NbSHE7469ftrkkHsfy
# S7y2YM68Nw/0s1kgQSneeqbOAMYIW1d634QJniOqzn1kb2wSHj+f/g90HTa+8vmh
# wBC/AGWPZB28qxVz7CjQ6uRfNpQnP3WvEXaJbmKldhvzyTzAWxfhXRzNlRx8Ije1
# BsditmsB1W3V93AU+EH/FXs4r4toQCyAQL4ldXCEmcVxHO2qk3ZDjVIrDddk3wP3
# 9VZP9UTm/laSs/zhWIDYHdZe6Kh7kX95pt10hZqS73O4gcfWmQIDAQABo4IB9DCC
# AfAwCwYDVR0PBAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFPm6
# Oe52dpXFPfitNGsYRvR/zuL4MBAGCSsGAQQBgjcVAQQDAgEAMIIBmgYDVR0gBIIB
# kTCCAY0wggGJBgwrBgEEAYOHcQEBAQEwggF3MIIBRgYIKwYBBQUHAgIwggE4HoIB
# NABUAGgAaQBzACAAUABLAEkAIABpAHMAIABpAG4AdABlAG4AZABlAGQAIABmAG8A
# cgAgAGMAZQByAHQAaQBmAGkAYwBhAHQAaQBvAG4AIABiAHkAIABtAGUAdABhAEIA
# UABBAC4AbwByAGcALAAgAEUAdgBnAGUAbgBpAGoAIABTAG0AaQByAG4AbwB2ACwA
# IABCAGUAcgBsAGkAbgAgAG8AbgBsAHkALgAgAEYAbwByACAAZABlAHQAYQBpAGwA
# cwAsACAAYwBvAG4AcwB1AGwAdAAgAHQAaABlACAAQwBlAHIAdABpAGYAaQBjAGEA
# dABpAG8AbgAgAFAAbwBsAGkAYwB5ACAAUwB0AGEAdABlAG0AZQBuAHQAIABsAGkA
# bgBrAGUAZAAgAGIAZQBsAG8AdwAuMCsGCCsGAQUFBwIBFh9odHRwOi8vcGtpLm1l
# dGFicGEub3JnL2Nwcy5odG1sMA0GCSqGSIb3DQEBCwUAA4ICAQBuwVs984+DElFQ
# BzO7r96a2/QZosjcZst3/KLCJQXRNjfrtHzGNDFRCj5MSqet9jXfC0Q0XQnus28S
# NG75qpzdkdKFruAjffOP1qOWEjnsljK+KkNafHXywlNUDI4Y3LzO44jF33gpTPjM
# 0KjWUGPRmwkOyapyEh629S7AlXikuYJLFDddsEVmzf9jVMjEQi37BzyaERwokkwg
# bW4hxIVycoPyfPhiAVChPj/IHFVaUDYb6vvL5/LD6fS9qd7VbJgOtkrpZhBzV3n4
# Gi7cOHVvGylta/gp4sdpB62QBNTjUS8JOkU1ECLDpO+QfKdKn1jYqpv9vIVYvj52
# 4yPx92T/dp2zZqJd9/3/M35b9pk0J3ACf77Lb2sM98/G4HK5NI8LqdM8iEKQkPQ4
# 9gkujnnQrmm1CMYRND498GfcgxQo2h+TFrxEWfZRHrPmoPtp90E9lW/vgwsgvtGN
# iBR6+wcEeBGm3TnwDUZsXhSUO0wyWSGhPWzxnM+ADC0iMpeBeTn22v+NA2S2mNOF
# f0K83O+r6LhjcnoD6kehQ30hHdvplxUs/D+IiOVkwyhznAmEpLP0Wv3e+ggN0qDP
# zYbKRjgJYcJBFU0us1+UADDg2mxxuJbNWmT7QXnh6eOlFPrYnwIdjmhJwkeaY7/N
# bSbexF0k2hTNI6d526AFcHAVSjJqBDGCBCcwggQjAgEBMHMwXDELMAkGA1UEBhMC
# REUxDzANBgNVBAcTBkJlcmxpbjEUMBIGA1UEChMLbWV0YUJQQS5vcmcxDDAKBgNV
# BAsTA0RldjEYMBYGA1UEAxMPbWV0YUJQQSBSb290IENBAhNfAAAAAiNkKw+5hJ+U
# AAAAAAACMAkGBSsOAwIaBQCgeDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkG
# CSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEE
# AYI3AgEVMCMGCSqGSIb3DQEJBDEWBBRRnGhZNHueNwMnuu4Vf6hdhrPndzANBgkq
# hkiG9w0BAQEFAASCAQA7CjoR2kfK1x877RLNPbgS+CkZsqQ0Oo2hLximmrQQ5jGR
# eFPMVSFDLrRUvVymY6x3R5x9fFoyiXezG9Nvtx3++Gi02/VvRHL2+k3yaDAtBWc7
# UPi3DmXtNSYucUrCxGAx1emq0iYGITCn/IaCYTkYr4VhmURJxz368DJX3PelS+h8
# LE/ivWLPNdCj93f6tGgouVo1C5i4gJSASrjIvS3TqPUy11W2PAtgNWqW79bXTRcr
# iD29d063GF50EiygD7zOX0+vDL4EcLjFFLH464zu5snkr6anGThGe8OdMJE6ezuJ
# gee5PpFd2CBIiEp0oA9Nrhz2Xy90BYd7YwzvFFLtoYICDzCCAgsGCSqGSIb3DQEJ
# BjGCAfwwggH4AgEBMHYwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0
# IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNl
# cnQgQXNzdXJlZCBJRCBDQS0xAhADAZoCOv9YsWvW1ermF/BmMAkGBSsOAwIaBQCg
# XTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMDAz
# MjYyMDU2MzFaMCMGCSqGSIb3DQEJBDEWBBRo+bEvcxWlKn38ZSOITFobziDFoDAN
# BgkqhkiG9w0BAQEFAASCAQA+hj2jhLpwsQ4WP9xXNJ1eM8g2sGds357jDgHYyu1h
# f5EwXS9FKV/nXg5jDlaHGSbArIwgTfjg/wD6BCmzDrfiMWZJKLbmUfHEwBCcl1dL
# Dw5YbYfeoO+Lp4a7y0jrWyzJ/TC2CuxSSSsOsBjbcgmuod/qQPN8eg10hevIee9G
# 92HklMzceGoybsZ3zlRkbSXkZhWrPp1iG0zbpLGoDSFzBa85lELBrBRWgQlaCkUW
# DN4BKpED8BNmP99Fzb7cBOoV3S4qwKt3Gc+AaR0qGVEvdQH/6390XiBKDSjJf44O
# 0q4ASjuZ0DvU9J/txcMO1M7A4GaoaGz0ic3hF2mpbCSL
# SIG # End signature block