ADLAB PowerShell source file: buildup-nlb.ps1

(C) Ondrej Sevecek, 2019 - www.sevecek.com, ondrej@sevecek.com



$libDir = Split-Path -parent $MyInvocation.MyCommand.Definition
& "$libDir\lib-common.ps1" -defaultConfig -rootDir $libDir -outFile nlbLib
& "$libDir\lib-modifyActions.ps1"
& "$libDir\lib-buildup.ps1"

$vmName = $args[0]

DBG ('NLB installation library')
Redirect-TempToOutput
Load-VMConfig

Find-MarkedVolumes


$appTag = 'nlb'
$appConfig = $vmConfig.$appTag
$firstAppHost = Get-FirstAppHostInInstance $appTag $appConfig.instance 'waitParams'
$firstAppConfig = (Get-FirstAppHostInInstance $appTag $appConfig.instance 'vmConfig').$appTag
$firstAppHostInInstance = Check-FirstAppHostInInstance $appTag $appConfig.instance
$firstAppHostFQDN = Get-FirstAppHostInInstance $appTag $appConfig.instance 'fqdn'

if (-not $firstAppHostInInstance) {

  $allPreviousAppHosts = Get-AllAppHostsOfInstance $appTag $appConfig.instance 'waitParams' -onlyBeforeMyHostName $vmConfig.hostName
}



if (Is-Null $appConfig) { DBG ('Invalid appCofnig, exiting'); exit }
#====================
#====================




################
#
# Note: Main code
#

if ($global:thisOSVersionNumber -lt 6.1) { 

  DBGIF $MyInvocation.MyCommand.Name { $global:thisOSVersionNumber -lt 6.3 }
  exit
}

#
# Note: on Windows Server 2016 the IPv6 must be completelly enabled in order to configure the new NLB cluster
#       or we get "Provider load failure" errors or error 314

[string] $sevecekDisabledComponentsBackupValue = 'SevecekDisabledComponentsBackup'
DBGSTART
[long] $regIPv6DisablerReal = (Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\TCPIP6\Parameters).DisabledComponents
[long] $regIPv6DisablerBackup = (Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\TCPIP6\Parameters).$sevecekDisabledComponentsBackupValue
DBGEND
DBG ('IPv6 disabling registry DisabledComponents values: value = {0} | backup = {1}' -f $regIPv6DisablerReal, $regIPv6DisablerBackup)

if (($global:thisOSVersionNumber -ge 10) -and ($regIPv6DisablerReal -ne 0)) {

  DBG ('Creating DisabledComponents backup and resetting the original value')
  DBGSTART
  Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\TCPIP6\Parameters -Name DisabledComponents -Value 0 -Type DWord
  Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\TCPIP6\Parameters -Name $sevecekDisabledComponentsBackupValue -Value $regIPv6DisablerReal -Type DWord
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND

  DBGIF ('NLB cluster requires IPv6 to configure. Trying again after restart.') { $true }
  exit $global:VM_RECYCLE_REQUIRED    
}

#
#

[string] $clusterName = $appConfig.instance
[string] $nlbIp = $firstAppConfig.ip
[string] $nlbMask = $firstAppConfig.mask
[string] $nlbType = $firstAppConfig.type


[string] $nlbSubnet = Get-IPSubnet $nlbIp $nlbMask
DBG ('Get the best NIC on my computer: {0} | {1}' -f $nlbSubnet, $vmConfig.hostName)
# Note: we could have more IPs on a single interface so we must get just a single representation here
[object[]] $bestNICs = (Get-NetworkMap $vmConfig.hostName) | ? { ($_.subnetIP -eq $nlbSubnet) -and (Is-EmptyString $_.nlbInstance) } | select -Unique nicName, subnetIP
DBGIF $MyInvocation.MyCommand.Name { (Get-CountSafe $bestNICs) -ne 1 }

[string] $nicName = $bestNICs[0].nicName
DBG ('The best NIC determined: {0}' -f $nicName)

#
#

[bool] $loggingEnabledByScript = $false

try {

    [string] $nlbLogFileName = Get-DataFileApp nlbBuildupLogFile $null '.txt'
    DBG ('Enable NLB logging for duration of the buildup: {0}' -f $nlbLogFileName)
    [int] $currentLoggingEnabled = Get-RegValue '.' 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\NLB' LoggingEnabled DWord
    [string] $currentLogFileName = Get-RegValue '.' 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\NLB' LogFileName String
    DBGIF $MyInvocation.MyCommand.Name { ($currentLoggingEnabled -ne 0) -or (Is-ValidString $currentLogFileName) }

    Set-RegistryValue 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NLB' LogFileName $nlbLogFileName String
    Set-RegistryValue 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NLB' LoggingEnabled 1 DWord
    $loggingEnabledByScript = $true

    #
    #

    DBG ('Import the NLB powershell module')
    DBGSTART
    Import-Module NetworkLoadBalancingClusters
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND


    if ($firstAppHostInInstance) {

      DBG ('Check first if there is not a partially configured cluster from the previous failed run')
      DBGSTART
      $someNlbExists = $null
      $someNlbExists = Get-NlbCluster -HostName localhost
      # Note: we must ignore errors because if there is no cluster it would raise some error
      #DBGER $MyInvocation.MyCommand.Name $error
      DBGEND

      if (Is-NonNull $someNlbExists) {

        DBG ('Must delete existing NLB cluster with possibly incomplete configuration: {0} | {1}' -f $someNlbExists.Name, $someNlbExists.IPAddress)
        DBGSTART
        Remove-NlbCluster -HostName localhost -Force
        DBGER $MyInvocation.MyCommand.Name $error
        DBGEND
      }

      DBG ('Create new NLB cluster: clusterName = {0} | clusterPrimaryIP = {1} | subnetMask = {2} | operationMode = {3} | interfaceName = {4}' -f $clusterName, $nlbIp, $nlbMask, $nlbType, $nicName)
      DBGSTART
      $newCluster = $null
      $newCluster = New-NlbCluster -ClusterName $clusterName -InterfaceName $nicName -OperationMode $nlbType -ClusterPrimaryIP $nlbIp -SubnetMask $nlbMask
      [bool] $errorOccured = $error.Count -gt 0
      DBGER $MyInvocation.MyCommand.Name $error
      DBGEND

      if ((Is-Null $newCluster) -or ($errorOccured)) {

        # Note: on Windows 2016 TP4 and TP5 and RTM this error 314 occurs which results in
        #       partially configured NLB cluster with invalid configuration, so we restart
        #       and remove it.
    
        if (Is-Null $someNlbExists) {

          DBGIF ('NLB cluster was not created successfully. Trying again after restart.') { $true }
          exit $global:VM_RECYCLE_REQUIRED    

        } else {

          DBGIF ('This is the second time the NLB cluster promotion failed. Just continue and dont bother, we must not cycle forever') { $true }
        }
      }


      if (Is-NonNull $newCluster) {

        DBG ('Clear all current port rules')
        DBGSTART
        Get-NlbClusterPortRule | Remove-NlbClusterPortRule -Force
        DBGER $MyInvocation.MyCommand.Name $Error
        DBGEND

        DBG ('Add all requested application port rules')
        $portRules = $xmlConfig.SelectNodes('./VMs/MACHINE[vm/@do="true"]//nlbrule[translate(@instance,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")="{0}"]' -f $appConfig.instance.ToLower())
        DBG ('Port rules requested: {0}' -f (Get-CountSafe $portRules))
 
        if ((Get-CountSafe $portRules) -gt 0) {

          foreach ($onePortRule in $portRules) {

            DBG ('Add one port rule: {0} | {1} | {2}' -f $onePortRule.proto, $onePortRule.port, $onePortRule.affinity)
            DBGSTART
            Add-NlbClusterPortRule -Protocol $onePortRule.proto -StartPort $onePortRule.port -EndPort $onePortRule.port -InterfaceName $nicName -Mode Multiple -Affinity $onePortRule.affinity
            DBGER $MyInvocation.MyCommand.Name
            DBGEND
          }
        }
      }
    }

    if (-not $firstAppHostInInstance) {

      DBG ('Wait for the first NLB node to finish')
      Wait-MachineAllPrevious $allPreviousAppHosts

      DBG ('Join existing NLB cluster: {0} | {1} | {2} | {3} | {4}' -f $clusterName, $nlbIp, $nlbMask, $nlbType, $nicName)

      DBG ('Get the existing cluster from first host: {0}' -f $firstAppHostFQDN)
      DBGSTART
      $existingCluster = Get-NlbCluster -HostName $firstAppHostFQDN
      DBGER $MyInvocation.MyCommand.Name $Error
      DBGEND
      DBGIF $MyInvocation.MyCommand.Name { Is-Null $existingCluster }
      DBGIF ('Weird cluster IP address: {0} | {1}' -f $nlbIp, $existingCluster.IPAddress) { $existingCluster.IPAddress -ne $nlbIp }
      DBGIF ('Weird cluster name: {0} | {1}' -f $clusterName, $existingCluster.Name) { $existingCluster.Name -ne $clusterName }
   
      DBG ('Joining cluster: {0}' -f $existingCluster.Name)
      DBGSTART
      Add-NlbClusterNode -Input $existingCluster -NewNodeName localhost -NewNodeInterface $nicName
      DBGER $MyInvocation.MyCommand.Name $Error
      DBGEND
    }
}

catch {

  DBGER $MyInvocation.MyCommand.Name $_
}

finally {

  if ($loggingEnabledByScript) {

    DBG ('Disable NLB logging again after the buildup: {0}' -f $nlbLogFileName)
    $currentLoggingEnabled = Get-RegValue '.' 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\NLB' LoggingEnabled DWord
    $currentLogFileName = Get-RegValue '.' 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\NLB' LogFileName String
    DBGIF $MyInvocation.MyCommand.Name { ($currentLoggingEnabled -ne 1) -or ($currentLogFileName -ne $nlbLogFileName) }

    DBGSTART
    Remove-ItemProperty -Literal 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NLB' LogFileName -Force
    Remove-ItemProperty -Literal 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NLB' LoggingEnabled -Force
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND
  }
}



#
#

if (($global:thisOSVersionNumber -ge 10) -and ($regIPv6DisablerBackup -ne 0)) {

  DBG ('Restoring DisabledComponents backup')
  DBGSTART
  Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\TCPIP6\Parameters -Name DisabledComponents -Value $regIPv6DisablerBackup -Type DWord
  Remove-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\TCPIP6\Parameters -Name $sevecekDisabledComponentsBackupValue
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND
}

#
#



DBG ('Throw up any remaining errors')
DBGSTART; DBGEND




# SIG # Begin signature block
# MIIc2AYJKoZIhvcNAQcCoIIcyTCCHMUCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU4bZs2f8jh/Zzjb+aMWyxunoH
# MXigghgEMIIE5TCCA82gAwIBAgIQOb1CntKBbrrVvMkDtLpl5zANBgkqhkiG9w0B
# AQsFADB1MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjEpMCcG
# A1UECxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIzAhBgNVBAMT
# GlN0YXJ0Q29tIENsYXNzIDIgT2JqZWN0IENBMB4XDTE2MTIwMTE1NTExM1oXDTE4
# MTIwMTE1NTExM1owUTELMAkGA1UEBhMCQ1oxGjAYBgNVBAgMEUppaG9tb3JhdnNr
# eSBLcmFqMQ0wCwYDVQQHDARCcm5vMRcwFQYDVQQDDA5PbmRyZWogU2V2ZWNlazCC
# ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK/RPYTY9Om2rIfSV/e5KbKi
# vWf3IjgQurayOJ75VvQj8ISgU2+cAK4cS2GQRvY1j1q3ctZy/3JuajKgW/5qIGMD
# MLxGFjL7CDjlZ+hw/ZVYTQKLw+jx89M4gQSiLM1048lD0GqCJSBC6L8/eu5u/ByX
# VGSRsaoZbgLEvaPf3xMwO111IVLqkSrHrbmVfwLH4ebDr3nqEbG3luHWk1r7c90y
# blCy9MONyzVWCZHeiy/HHoLyz1WVHtTe7JL4nRL57XZaMELCZahXax90hiiDygOj
# m3GPEmVpBEEm1QRfCrptVE/ozdIJvvOBcDoOVqisflb0ytpY1Vpz6wg9Gc7tqCEC
# AwEAAaOCAZMwggGPMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcD
# AzAJBgNVHRMEAjAAMB0GA1UdDgQWBBRtr0qNzYUFiC3lLNKDfV8x03o3HzAfBgNV
# HSMEGDAWgBQ+YpOa18cZ7j6PSRCFVRUg45SEHDBtBggrBgEFBQcBAQRhMF8wJAYI
# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLnN0YXJ0c3NsLmNvbTA3BggrBgEFBQcwAoYr
# aHR0cDovL2FpYS5zdGFydHNzbC5jb20vY2VydHMvc2NhLmNvZGUyLmNydDA2BgNV
# HR8ELzAtMCugKaAnhiVodHRwOi8vY3JsLnN0YXJ0c3NsLmNvbS9zY2EtY29kZTIu
# Y3JsMCMGA1UdEgQcMBqGGGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tLzBRBgNVHSAE
# SjBIMAgGBmeBDAEEATA8BgsrBgEEAYG1NwECBTAtMCsGCCsGAQUFBwIBFh9odHRw
# czovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5MA0GCSqGSIb3DQEBCwUAA4IBAQCb
# kYhLx7SGEqbJjJD4U8+EDjkoTd6eSlnX/L5u9QN9wkfX3bNdHfA9gdmhPr45hyev
# RmNG234OaGWNi7Ktnri+w2uf4UvPuk9zBLQ+IZ43x1e5sZqEnxbsvyg4qaFersIS
# PpLKnIV2HVB/ubgkDtDDjvCE4wIM1ELJ2eFCARwRnj/nvt5Y8UmEl1nTyK4MXUfe
# 9T/x4VklXyjZZgYBCjEDOSctGgHeI8UpfwM3WLdPuqZYhSdaXhoXRZ4thZvH7uaD
# evGkyKzbXOVmOy84H/rEU1XljkVkt+/tcwA9Jo83fIMnwS8UbmkUn42MZ8tKy13p
# QMH4gN83tg1NgX4Pf633MIIF2DCCA8CgAwIBAgIQbDvSft08lJ6Vjiips8dXoDAN
# BgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20g
# THRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu
# ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcN
# MTUxMjE2MDEwMDA1WhcNMzAxMjE2MDEwMDA1WjB1MQswCQYDVQQGEwJJTDEWMBQG
# A1UEChMNU3RhcnRDb20gTHRkLjEpMCcGA1UECxMgU3RhcnRDb20gQ2VydGlmaWNh
# dGlvbiBBdXRob3JpdHkxIzAhBgNVBAMTGlN0YXJ0Q29tIENsYXNzIDIgT2JqZWN0
# IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuRQEWPeyxYYsCDJg
# rQgmwIF3uWgZ2RUrHRhp5NoalgWXLmR5Gqk9UTNa0Hdq9AKTQcOOunAbq9h7dG+Y
# 6Ne5qT5odqSJoCKsF9Yp+Lu4YZ/SB9BmDjBHICtwAh7+cwkccTS14n6prKin8Y46
# QAZ2ksr3eGzvWAVzfX+DUOmiVQLjAK6Wp8bCZHvj+FhAlS5Ne7/dggDeSVWnMyPm
# 2k/5YKOTVXExJJaAlYkmyH1OiC3soTkkGb6aJjGJPHiaiNJ4pjkySX5l2p4DQ7K1
# /J6ft5Vw9PuqwmYrF0ViGnn38kzB2d9UI9Q+dFmHUbV+cnr+FoGl6CiUDd5ZIF1H
# Mrb8hwIDAQABo4IBWjCCAVYwDgYDVR0PAQH/BAQDAgEGMBMGA1UdJQQMMAoGCCsG
# AQUFBwMDMBIGA1UdEwEB/wQIMAYBAf8CAQAwMgYDVR0fBCswKTAnoCWgI4YhaHR0
# cDovL2NybC5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMGYGCCsGAQUFBwEBBFowWDAk
# BggrBgEFBQcwAYYYaHR0cDovL29jc3Auc3RhcnRzc2wuY29tMDAGCCsGAQUFBzAC
# hiRodHRwOi8vYWlhLnN0YXJ0c3NsLmNvbS9jZXJ0cy9jYS5jcnQwHQYDVR0OBBYE
# FD5ik5rXxxnuPo9JEIVVFSDjlIQcMB8GA1UdIwQYMBaAFE4L7xqkQFulF2mHMMo0
# aEPQQa7yMD8GA1UdIAQ4MDYwNAYEVR0gADAsMCoGCCsGAQUFBwIBFh5odHRwOi8v
# d3d3LnN0YXJ0c3NsLmNvbS9wb2xpY3kwDQYJKoZIhvcNAQELBQADggIBAGOlPNWz
# bSco2Ou6U68wC+pKXRLV+ZrKcPpMY4zXTVR+RupS54WhJCManab2P1ncPlHTbRMb
# PjfHnyj0sIdpvwcV49n0nizMF3MBxaKJEnBBEfHs9Krgjc4qKjR2nOywlzxJ0M27
# RthR5XjyjQ1ofHlOisYgMzcyKyMT7YYpxxoC0wTgAh0DNmE5Q/GKFOaDd3S5gTqr
# R9AQzGaC3IxCKBFtcwvk51W98lNRtMbm+oJze5T+dL2wIhyWK58sEIl2paAVfAfW
# GH3umYL46scLn8BXDFchN1Jgrg07DqY6gxCqSdubPhVHZInuVagktWmrnS6N9V/v
# VLz+OaX4Mkas8n1J1RIR+GV8ZQVmTM49l6L+fpv/h95MWLhQOcXanbIY/2cdNEuz
# 5AkhfvDNTQnLxYEMIyMOtW2QIwwZdz92vMTU17G9goxXYjSm09yw+iBniH9G/xGz
# 39BV3bwa8ZtKHzDoZ54HT6JT2AraDhrWTwFXv8Xrvv2cir+k0h5bIWlDtImH7Jm1
# 52edb77f5JI8JrPf6jxcUrhNH4xHxe2kGs8ERA39oYlT0dKQIb0obTN6FOF63hBR
# FFhGB7NuX2FeFjJsZFCkoJkpsEauObb7Rh+C02+fnHfoi6ivKwUC9BOsWlI4xn7G
# Me27niL6k7wpK0L6MTG5/6gxwosqaMA1aukwMIIGajCCBVKgAwIBAgIQAwGaAjr/
# WLFr1tXq5hfwZjANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UE
# ChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYD
# VQQDExhEaWdpQ2VydCBBc3N1cmVkIElEIENBLTEwHhcNMTQxMDIyMDAwMDAwWhcN
# MjQxMDIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzERMA8GA1UEChMIRGlnaUNlcnQx
# JTAjBgNVBAMTHERpZ2lDZXJ0IFRpbWVzdGFtcCBSZXNwb25kZXIwggEiMA0GCSqG
# SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCjZF38fLPggjXg4PbGKuZJdTvMbuBTqZ8f
# ZFnmfGt/a4ydVfiS457VWmNbAklQ2YPOb2bu3cuF6V+l+dSHdIhEOxnJ5fWRn8YU
# Oawk6qhLLJGJzF4o9GS2ULf1ErNzlgpno75hn67z/RJ4dQ6mWxT9RSOOhkRVfRiG
# BYxVh3lIRvfKDo2n3k5f4qi2LVkCYYhhchhoubh87ubnNC8xd4EwH7s2AY3vJ+P3
# mvBMMWSN4+v6GYeofs/sjAw2W3rBerh4x8kGLkYQyI3oBGDbvHN0+k7Y/qpA8bLO
# cEaD6dpAoVk62RUJV5lWMJPzyWHM0AjMa+xiQpGsAsDvpPCJEY93AgMBAAGjggM1
# MIIDMTAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAK
# BggrBgEFBQcDCDCCAb8GA1UdIASCAbYwggGyMIIBoQYJYIZIAYb9bAcBMIIBkjAo
# BggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzCCAWQGCCsG
# AQUFBwICMIIBVh6CAVIAQQBuAHkAIAB1AHMAZQAgAG8AZgAgAHQAaABpAHMAIABD
# AGUAcgB0AGkAZgBpAGMAYQB0AGUAIABjAG8AbgBzAHQAaQB0AHUAdABlAHMAIABh
# AGMAYwBlAHAAdABhAG4AYwBlACAAbwBmACAAdABoAGUAIABEAGkAZwBpAEMAZQBy
# AHQAIABDAFAALwBDAFAAUwAgAGEAbgBkACAAdABoAGUAIABSAGUAbAB5AGkAbgBn
# ACAAUABhAHIAdAB5ACAAQQBnAHIAZQBlAG0AZQBuAHQAIAB3AGgAaQBjAGgAIABs
# AGkAbQBpAHQAIABsAGkAYQBiAGkAbABpAHQAeQAgAGEAbgBkACAAYQByAGUAIABp
# AG4AYwBvAHIAcABvAHIAYQB0AGUAZAAgAGgAZQByAGUAaQBuACAAYgB5ACAAcgBl
# AGYAZQByAGUAbgBjAGUALjALBglghkgBhv1sAxUwHwYDVR0jBBgwFoAUFQASKxOY
# spkH7R7for5XDStnAs0wHQYDVR0OBBYEFGFaTSS2STKdSip5GoNL9B6Jwcp9MH0G
# A1UdHwR2MHQwOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy
# dEFzc3VyZWRJRENBLTEuY3JsMDigNqA0hjJodHRwOi8vY3JsNC5kaWdpY2VydC5j
# b20vRGlnaUNlcnRBc3N1cmVkSURDQS0xLmNybDB3BggrBgEFBQcBAQRrMGkwJAYI
# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1
# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0Et
# MS5jcnQwDQYJKoZIhvcNAQEFBQADggEBAJ0lfhszTbImgVybhs4jIA+Ah+WI//+x
# 1GosMe06FxlxF82pG7xaFjkAneNshORaQPveBgGMN/qbsZ0kfv4gpFetW7easGAm
# 6mlXIV00Lx9xsIOUGQVrNZAQoHuXx/Y/5+IRQaa9YtnwJz04HShvOlIJ8OxwYtNi
# S7Dgc6aSwNOOMdgv420XEwbu5AO2FKvzj0OncZ0h3RTKFV2SQdr5D4HRmXQNJsQO
# fxu19aDxxncGKBXp2JPlVRbwuwqrHNtcSCdmyKOLChzlldquxC5ZoGHd2vNtomHp
# igtt7BIYvfdVVEADkitrwlHCCkivsNRu4PQUCjob4489yq9qjXvc2EQwggbNMIIF
# taADAgECAhAG/fkDlgOt6gAK6z8nu7obMA0GCSqGSIb3DQEBBQUAMGUxCzAJBgNV
# BAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdp
# Y2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAe
# Fw0wNjExMTAwMDAwMDBaFw0yMTExMTAwMDAwMDBaMGIxCzAJBgNVBAYTAlVTMRUw
# EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x
# ITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMTCCASIwDQYJKoZIhvcN
# AQEBBQADggEPADCCAQoCggEBAOiCLZn5ysJClaWAc0Bw0p5WVFypxNJBBo/JM/xN
# RZFcgZ/tLJz4FlnfnrUkFcKYubR3SdyJxArar8tea+2tsHEx6886QAxGTZPsi3o2
# CAOrDDT+GEmC/sfHMUiAfB6iD5IOUMnGh+s2P9gww/+m9/uizW9zI/6sVgWQ8DIh
# FonGcIj5BZd9o8dD3QLoOz3tsUGj7T++25VIxO4es/K8DCuZ0MZdEkKB4YNugnM/
# JksUkK5ZZgrEjb7SzgaurYRvSISbT0C58Uzyr5j79s5AXVz2qPEvr+yJIvJrGGWx
# wXOt1/HYzx4KdFxCuGh+t9V3CidWfA9ipD8yFGCV/QcEogkCAwEAAaOCA3owggN2
# MA4GA1UdDwEB/wQEAwIBhjA7BgNVHSUENDAyBggrBgEFBQcDAQYIKwYBBQUHAwIG
# CCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUHAwgwggHSBgNVHSAEggHJMIIBxTCC
# AbQGCmCGSAGG/WwAAQQwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3LmRpZ2lj
# ZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUHAgIwggFW
# HoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBm
# AGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0
# AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAv
# AEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0
# AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAg
# AGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBw
# AG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBu
# AGMAZQAuMAsGCWCGSAGG/WwDFTASBgNVHRMBAf8ECDAGAQH/AgEAMHkGCCsGAQUF
# BwEBBG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMG
# CCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRB
# c3N1cmVkSURSb290Q0EuY3J0MIGBBgNVHR8EejB4MDqgOKA2hjRodHRwOi8vY3Js
# My5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMDqgOKA2
# hjRodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290
# Q0EuY3JsMB0GA1UdDgQWBBQVABIrE5iymQftHt+ivlcNK2cCzTAfBgNVHSMEGDAW
# gBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEARlA+ybco
# JKc4HbZbKa9Sz1LpMUerVlx71Q0LQbPv7HUfdDjyslxhopyVw1Dkgrkj0bo6hnKt
# OHisdV0XFzRyR4WUVtHruzaEd8wkpfMEGVWp5+Pnq2LN+4stkMLA0rWUvV5PsQXS
# Dj0aqRRbpoYxYqioM+SbOafE9c4deHaUJXPkKqvPnHZL7V/CSxbkS3BMAIke/MV5
# vEwSV/5f4R68Al2o/vsHOE8Nxl2RuQ9nRc3Wg+3nkg2NsWmMT/tZ4CMP0qquAHzu
# nEIOz5HXJ7cW7g/DvXwKoO4sCFWFIrjrGBpN/CohrUkxg0eVd3HcsRtLSxwQnHcU
# wZ1PL1qVCCkQJjGCBD4wggQ6AgEBMIGJMHUxCzAJBgNVBAYTAklMMRYwFAYDVQQK
# Ew1TdGFydENvbSBMdGQuMSkwJwYDVQQLEyBTdGFydENvbSBDZXJ0aWZpY2F0aW9u
# IEF1dGhvcml0eTEjMCEGA1UEAxMaU3RhcnRDb20gQ2xhc3MgMiBPYmplY3QgQ0EC
# EDm9Qp7SgW661bzJA7S6ZecwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAI
# oAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIB
# CzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFIhPUenCuKHuqDE7qJ58
# Haivq4kRMA0GCSqGSIb3DQEBAQUABIIBAKffoR7G90w4qFR6MwkdWf2dhiL2UcS2
# XyhwDik9EgOb7UYtyEbWwf0hs7BYKvXypAG4wInuVgoGt4Gieg3R0yMgLijI6jvb
# 7OFhawAmDDBaX7jWJ7RK4yUGY2WUj37KqIzryvakdS1c7JPxDyI7ZkL8YsryBHxo
# pAQMS3Mi47RgfC744rMefWlkWSoocEoc6beANi189ETWZClbeXHehmC91mFJsbDN
# PC0FaT1JRq0iesDbP8/f84T3S4VRS8MMe47hY6AaDt6bCsSjm42Rhhrm62DleLaE
# ZqSo44dzmhFbWOVvprRWJ6M+Vgtjvzr5qCFyRNhFzCnBSbiaf5vGTnChggIPMIIC
# CwYJKoZIhvcNAQkGMYIB/DCCAfgCAQEwdjBiMQswCQYDVQQGEwJVUzEVMBMGA1UE
# ChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYD
# VQQDExhEaWdpQ2VydCBBc3N1cmVkIElEIENBLTECEAMBmgI6/1ixa9bV6uYX8GYw
# CQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcN
# AQkFMQ8XDTE3MDMyMzEzNDExMlowIwYJKoZIhvcNAQkEMRYEFBPE4EZmtDGgS2Y6
# QquGSRluXL8JMA0GCSqGSIb3DQEBAQUABIIBAET0ghtBuvESvMEnkQQ4maM3Pg/g
# VcZhM7PFhRj8LavStPxus80irJYxZl3n2rOFqVyOcpu0zYw6ViM8dvFWVbiSjyjJ
# dtBU8zPm9hAJST57/lsWgu9i2SD3Y2XJEWy0dmOeAZfjHBjxxj8kO13H6K/RAZ4M
# WrD7H8PDMPx37xJq1pwrMgLE+7RQXLaWi8c8svDgOQ8/uIXWKG92d+B8KczX1CqL
# bOpZvUo+YptH/YhS48ayWZhJx8+yEW+6MVcLirures0Onbijw4s0R0UbBkUtgz1C
# z0yrCCeP7OmigLdSTsbtpWu0CY6pH725iVIWEpIyxjclVHexmIvBUMos8Pg=
# SIG # End signature block