ADLAB PowerShell source file: buildup-tmg.ps1

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



#$global:outClass = 'main'

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

$vmName = $args[0]

DBG ("TMG Installation library")
Redirect-TempToOutput
Load-VMConfig

Find-MarkedVolumes

$appTag = 'tmg'
$appConfig = $vmConfig.$appTag
$firstAppHost = Get-FirstAppHostInInstance $appTag $appConfig.instance 'waitParams'
$firstAppHostInInstance = Check-FirstAppHostInInstance $appTag $appConfig.instance


#====================
#====================


#====================
DBG ('Installing TMG.')

if ($appConfig.type -eq 'standalone') {

  $instRoot = Get-FirstPathFromWildcard (Join-Path $installMediaVolume 'TMG2010-*\StandardEdition\FPC')
  $updRoot = Get-FirstPathFromWildcard (Join-Path $installMediaVolume 'TMG2010-*\!Updates')
  $customRoot = Join-Path $global:rootDir 'TMG2010'
}


#================  
DBG ('Install prerequisities')
Run-Process (Join-Path $instRoot 'PrerequisiteInstaller.exe') '/unattended /TMGSA'


#================  
DBG ('Update installation .INI')
$configINIPath = Get-DataFileApp 'tmg-config' $null '.ini'
$argReplacement = 'internalNET${0}' -f (Escape-ForMultiValue $appConfig.internalNET)
Replace-ArgumentsInFile (Join-Path $customRoot 'tmg-install.ini') $argReplacement $configINIPath Unicode

DBG ('Install TMG')
# Note: the path is somewhat weir, but it really must be done this way
Run-Process (Join-Path $instRoot 'setup.exe') ('/r /v" /qb REBOOT=ReallySuppress FULLPATHANSWERFILE=\"{0}\""' -f $configINIPath)

$safeDelay = 95

DBG ('Just a brief sleep to allow full initialization before continuing with updates')
Start-Sleep $safeDelay


DBG ('Install TMG SP1')
Run-Process 'MSIEXEC' ('/update "{0}" /qb /norestart' -f (Join-Path $updRoot 'tmg2010-sp1-TMG-KB981324-AMD64-ENU.msp'))

DBG ('Just a brief sleep to allow full initialization before continuing with updates')
Start-Sleep $safeDelay


DBG ('Install TMG SP1 SU1')
Run-Process (Join-Path $updRoot 'tmg2010-sp1-su1-TMG-KB2288910-amd64-ENU.exe') '/qb /norestart'

DBG ('Just a brief sleep to allow full initialization before continuing with updates')
Start-Sleep $safeDelay


DBG ('Install TMG SP2')
Run-Process (Join-Path $updRoot 'tmg2010-sp2-TMG-KB2555840-amd64-ENU.exe') '/qb /norestart'

DBG ('Just a brief sleep to allow full initialization before continuing with updates')
Start-Sleep $safeDelay


DBG ('Install TMG SP2 Rollup 5')
Run-Process MSIEXEC ('/update "{0}" /qb /norestart' -f (Join-Path $updRoot 'tmg2010-sp2rollup5-TMG-KB2954173-amd64-GLB.msp'))

DBG ('Just a brief sleep to allow full initialization before continuing')
Start-Sleep $safeDelay



[System.Collections.ArrayList] $comList = @()
$tmg = Get-TmgObject ([ref] $comList)


#================
$importXMLs = $appConfig.SelectNodes('./import[@rules or @syspol]')
DBG ('Found import config XMLs: {0}' -f (Get-CountSafe $importXMLs))

if ((Get-CountSafe $importXMLs) -gt 0) {

  DBG ('Import config XMLs')

  $importXMLs | % {

    DBG ('One XML config to be processed.')
    $oneConfig = $_

    if (Is-ValidString $oneConfig.rules) {

      $xmlFile = $oneConfig.rules
      DBG ('One config XML - rules: {0}' -f $xmlFile)
       
      DBGSTART
      $tmg.ArrayPolicy.PolicyRules.ImportFromFile((Join-Path $customRoot ('tmg-rules-{0}.xml' -f $xmlFile)), $null, $null, $false, $true, $true)
      DBGER $MyInvocation.MyCommand.Name $error
      DBGEND
    }

    if (Is-ValidString $oneConfig.syspol) {

      $xmlFile = $oneConfig.syspol
      DBG ('One config XML - rules: {0}' -f $xmlFile)
       
      DBGSTART
      $tmg.SystemPolicy.PolicyRules.ImportFromFile((Join-Path $customRoot ('tmg-rules-{0}.xml' -f $xmlFile)), $null, $null, $false, $true, $true)
      DBGER $MyInvocation.MyCommand.Name $error
      DBGEND
    }
  }


  DBG ('Will also define all subnets known from the configuration')
  # Note: I select nicName instead of hvNetwork because if the builder works on non-virtual
  #       platform, or if it does not build VM switches at all, then we have only the nicName
  #       available
  $subnetMap = Get-NetworkMap | Select -Unique hvNetwork, nicName, subnetIP, mask
  DBGIF $MyInvocation.MyCommand.Name { (Get-CountSafe $subnetMap) -lt 1 }

  if ((Get-CountSafe $subnetMap) -gt 0) {

    DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $appConfig.company }

    [int] $subnetsDefined = 0

    foreach ($subnetMapElement in $subnetMap) {

      DBG ('One subnet to be tried: ip = {0} | mask = {1} | hyperv = {2} | nic = {3}' -f $subnetMapElement.subnetIP, $subnetMapElement.mask, $subnetMapElement.hvNetwork, $subnetMapElement.nicName)

      if ((Is-ValidString $subnetMapElement.subnetIP) -and ($subnetMapElement.subnetIP -ne $global:emptyValueMarker)) {

        [string] $subnetName = $null

        if (Is-ValidString $subnetMapElement.hvNetwork) {

          $subnetName = '{0}: {1}' -f $appConfig.company, $subnetMapElement.hvNetwork
      
        } else {

          $subnetName = '{0}: {1}' -f $appConfig.company, $subnetMapElement.nicName
        }

        DBG ('One subnet to be defined: subnet = {0} | ip = {1} | mask = {2} | hyperv = {3} | nic = {4}' -f $subnetName, $subnetMapElement.subnetIP, $subnetMapElement.mask, $subnetMapElement.hvNetwork, $subnetMapElement.nicName)
        DBGSTART
        $newSubnet = $null
        $newSubnet = $tmg.RuleElements.Subnets.Add($subnetName, $subnetMapElement.subnetIP, $subnetMapElement.mask)

        if ($error.Count -eq 0) {

          $subnetsDefined ++
        }
        DBGER $MyInvocation.MyCommand.Name $error
        DBGEND

        if (Is-NonNull $newSubnet) {

          [void] $comList.Add($newSubnet)
        }
      }
    }

    DBG ('Save the new subnets: {0}' -f $subnetsDefined)
    DBGIF $MyInvocation.MyCommand.Name { $subnetsDefined -lt 1 }
    DBGSTART
    $tmg.RuleElements.Save()
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND
  }
}


DBG ('Should we enable VPN: {0} | rras = {1}' -f (Parse-BoolSafe $appConfig.vpn.enable), (-not (Parse-BoolSafe $vmConfig.rras.dnd)))
if ((Parse-BoolSafe $appConfig.vpn.enable) -or (-not (Parse-BoolSafe $vmConfig.rras.dnd))) {

  DBG ('Enabling VPN client access')
  DBGSTART
  $tmg.NetworkConfiguration.VpnConfiguration.EnableVpnClients = $true
  $tmg.NetworkConfiguration.VpnConfiguration.UseDHCPForAddressAssignment = $true
  $tmg.NetworkConfiguration.VpnConfiguration.Save()
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND
}


DBG ('Should we disable the TMG Getting Started Wizard (GSW): {0}' -f (Parse-BoolSafe $appConfig.noGettingStartedWizard))

if (Parse-BoolSafe $appConfig.noGettingStartedWizard) {

  DBGSTART
  Set-ItemProperty 'HKLM:\Software\Microsoft\FPC\Getting Started Wizard' 'Auto Launch GSW If Not Completed' 0 -Type Dword
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND
}


DBG ('Should we disable the telemetry for spy net: {0}' -f (Parse-BoolSafe $appConfig.telemetryOff))

if (Parse-BoolSafe $appConfig.telemetryOff) {

  DBG ('Set the telemetry to off. Current: {0}' -f $tmg.SpyNetLevel)
  DBGSTART
  $tmg.SpyNetLevel = 1 # Note: actual constant fpcSpyNetDisabled
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND
  
  DBG ('Save the telemetry SpyNetLevel setting')
  DBGSTART
  $tmg.Save()
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND
}


DBG ('Should we disable the Customer Experience Improvement Program (CEIP): {0}' -f (Parse-BoolSafe $appConfig.ceipOff))

if (Parse-BoolSafe $appConfig.ceipOff) {

  DBG ('Get the CEIP VendorParametersSet if it already exists')
  DBGSTART  
  $ceipParamSet = $null
  $ceipParamSet = $tmg.RuleElements.VendorParametersSets.Item('{EF506E47-A862-4010-AEE5-D2A2A015FDDA}')
  # DBGER $MyInvocation.MyCommand.Name $error # Note: just ignore nonexisting errors
  DBGEND

  if (Is-Null $ceipParamSet) {
  
    DBG ('The CEIP VendorParametersSet does not exist yet. Create')
    DBGSTART  
    $ceipParamSet = $tmg.RuleElements.VendorParametersSets.Add('{EF506E47-A862-4010-AEE5-D2A2A015FDDA}')
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND
  }

  DBGSTART
  DBG ('Current CEIP value: {0}' -f $ceipParamSet.Value('{EF506E47-A862-4010-AEE5-D2A2A015FDDA}'))
  # Note: just ignore the nonexisting Value errors
  DBGEND

  DBG ('Set the CEIP value')
  DBGSTART
  $ceipParamSet.Value('{EF506E47-A862-4010-AEE5-D2A2A015FDDA}') = 1
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND

  DBG ('Save the vendor parameters sets')
  DBGSTART
  $tmg.RuleElements.VendorParametersSets.Save()
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND
}


DBG ('Should we use the SP2 error pages: {0}' -f (Parse-BoolSafe $appConfig.errorsSP2))

if (Parse-BoolSafe $appConfig.errorsSP2) {

  DBG ('Set the error pages to use the SP2 versions. Current: {0}' -f $tmg.ArrayPolicy.WebProxy.UseLegacyErrorPagesDirectory)
  DBGSTART
  $tmg.ArrayPolicy.WebProxy.UseLegacyErrorPagesDirectory = $false
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND
  
  DBG ('Save the telemetry SpyNetLevel setting')
  DBGSTART
  $tmg.ArrayPolicy.WebProxy.Save()
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND
}




$defineNetworks = $appConfig.SelectNodes('./net[@name]')
DBG ('Found network definitions required: {0}' -f (Get-CountSafe $defineNetworks))

if ((Get-CountSafe $defineNetworks) -gt 0) {

  foreach ($oneDefineNetwork in $defineNetworks) {
   
    $oneIP = Split-MultiValue $oneDefineNetwork.ip
    $oneIPstart = Get-IPSubnet $oneIP[0] $oneIP[1]
    $oneIPend = Get-IPBroadcast $oneIP[0] $oneIP[1]
    DBG ('Going to define network: {0} | {1} | {2} | {3} | {4}' -f $oneDefineNetwork.name, $oneIP[0], $oneIP[1], $oneIPstart, $oneIPend)
    DBGSTART
    $newNet = $tmg.NetworkConfiguration.Networks.Add($oneDefineNetwork.name)
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND
    
    DBG ('Set the IP address range')
    DBGSTART
    $newNet.IpRangeSet.Add($oneIPstart, $oneIPend)
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND

    DBG ('Save the new network')
    DBGSTART
    $newNet.Save()
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND
  }
}


$defineNetRules = $appConfig.SelectNodes('./netRule[@name]')
DBG ('Found network rule definitions required: {0}' -f (Get-CountSafe $defineNetRules))

if ((Get-CountSafe $defineNetRules) -gt 0) {

  foreach ($oneDefineNetRule in $defineNetRules) {

    switch ($oneDefineNetRule.type) {

      'route' { $netRuleType = 0 }
      'nat'   { $netRuleType = 1 }
    }

    DBG ('Add a new network rule: {0} | from = {1} | to = {2} | type = {3}' -f $oneDefineNetRule.name, $oneDefineNetRule.source, $oneDefineNetRule.destination, $netRuleType)
    DBGSTART
    $newNetRule = $tmg.NetworkConfiguration.NetworkRules.Add($oneDefineNetRule.name)
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND

    DBG ('Add the source network')
    DBGSTART
    $newNetRule.SourceSelectionIPs.Networks.Add($oneDefineNetRule.source, 0)  # fpcIncludeStatus: fpcInclude = 0
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND

    DBG ('Add the destination network')
    DBGSTART
    $newNetRule.DestinationSelectionIPs.Networks.Add($oneDefineNetRule.destination, 0)
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND

    DBG ('Set the routing type')
    DBGSTART
    # Note: IFPCNetworkRule.RoutingType
    $newNetRule.RoutingType = $netRuleType # Note: fpcRoutingType: FpcNetworkRoutingTypes: fpcRoute = 0, fpcNat = 1
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND

    DBG ('Save the new network rule')
    DBGSTART
    $newNetRule.Save()
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND
  }
}



$natRules = Get-NatRulesForInstance $appConfig.instance
  
if ((Get-CountSafe $natRules) -gt 0) {

  foreach ($oneNATRule in $natRules) { 
      
    DBGIF ('Not implemented yet to configure NAT rule with a specific INTERFACE: {0}' -f $oneNATRule.if) { Is-ValidString $oneNATRule.if }
    DBGIF ('Not implemented yet to configure NAT rule with a specific PUBLIC IP: {0}' -f $oneNATRule.publicIP) { ((Is-ValidString $oneNATRule.publicIP) -and ($oneNATRule.publicIP -ne '0.0.0.0')) }


    [int] $oneNATRulePrivatePort = 0

    if (($oneNATRule.privatePort -ne $oneNATRule.publicPort) -and ($oneNATRule.privatePort -ne 0)) {

      $oneNATRulePrivatePort = $oneNATRule.privatePort
      $oneNATRuleName = 'Forward {0} {1} to {2}:{3}' -f $oneNATRule.protocol, $oneNATRule.publicPort, $oneNATRule.privateIP, $oneNATRulePrivatePort
  
    } else {

      $oneNATRuleName = 'Forward {0} {1} to {2}:{1}' -f $oneNATRule.protocol, $oneNATRule.publicPort, $oneNATRule.privateIP
    }


    [string] $oneNATRulePublicIP = ''

    if ((Is-ValidString $oneNATRule.publicIP) -and ($oneNATRule.publicIP -ne '0.0.0.0')) {

      $oneNATRulePublicIP = $oneNATRule.publicIP
    }


    New-TmgPortPublishingRule -ruleName $oneNATRuleName -objectNamePrefix $appConfig.company -portOrProtocol $oneNATRule.publicPort -tcpOrUDP $oneNATRule.protocol -internalIP $oneNATRule.privateIP -network External -publicIPs $oneNATRulePublicIP -privatePort $oneNATRulePrivatePort
  }
}



Release-ComList ([ref] $comList)

  


# SIG # Begin signature block
# MIIYMAYJKoZIhvcNAQcCoIIYITCCGB0CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBv78EQitsOvRBh
# e1cvgT5JIZHry399LvKsvvgkVVz3n6CCE0cwggYEMIID7KADAgECAgoqHIRwAAEA
# AAB/MA0GCSqGSIb3DQEBCwUAMGwxCzAJBgNVBAYTAkNaMRcwFQYDVQQIEw5DemVj
# aCBSZXB1YmxpYzENMAsGA1UEBxMEQnJubzEQMA4GA1UEChMHU2V2ZWNlazEjMCEG
# A1UEAxMaU2V2ZWNlayBFbnRlcnByaXNlIFJvb3QgQ0EwHhcNMTkwNjExMTkyMzMy
# WhcNMjQwNjA5MTkyMzMyWjCBjzELMAkGA1UEBhMCQ1oxFzAVBgNVBAgTDkN6ZWNo
# IFJlcHVibGljMQ0wCwYDVQQHEwRCcm5vMRwwGgYDVQQKExNJbmcuIE9uZHJlaiBT
# ZXZlY2VrMRcwFQYDVQQDEw5PbmRyZWogU2V2ZWNlazEhMB8GCSqGSIb3DQEJARYS
# b25kcmVqQHNldmVjZWsuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
# AQEAnkjWNkK4FfUUN8iAN91ry+wsSn8cFKJbMnROAqTrx8t3H315p2/bUG2DosCF
# Odu0WcaTOLdm5obhT+/3O7BqpdcnlWKlSEz4AL9zQeCbe4++NObBVPBbPE16j9C4
# xELoXW/Ti86C2PEkN5azGUvxGxzQQ45g32OsEI+Bh05qHMkk3oQ6L8O0Fpd5W4e+
# L4HuKS3JOikNhhryTNPD9grF/0wXTzn94TrL1GohuaCPh8g9HOtMoDCd+ExnqV8q
# 4k60D37BOK1I81hYFIBn8MvCsjMRC5TK87MtI7aUUIeve5kopc8ZpxNti3F/+Puh
# 4UUxL3nKjfAM6HE0b7FqkfkRpwIDAQABo4IBgjCCAX4wEwYDVR0lBAwwCgYIKwYB
# BQUHAwMwDgYDVR0PAQH/BAQDAgbAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYBBQUH
# AwMwHQYDVR0OBBYEFOKbNkkiAht2GxCISJMJxLg4gOC9MB0GA1UdEQQWMBSBEm9u
# ZHJlakBzZXZlY2VrLmNvbTAfBgNVHSMEGDAWgBQNnMgyfdUi8l9UfithS4FQ88Vs
# wDBSBgNVHR8ESzBJMEegRaBDhkFodHRwOi8vcGtpLnNldmVjZWsuY29tL0NBL1Nl
# dmVjZWslMjBFbnRlcnByaXNlJTIwUm9vdCUyMENBKDEpLmNybDCBhgYIKwYBBQUH
# AQEEejB4ME0GCCsGAQUFBzAChkFodHRwOi8vcGtpLnNldmVjZWsuY29tL0NBL1Nl
# dmVjZWslMjBFbnRlcnByaXNlJTIwUm9vdCUyMENBKDEpLmNydDAnBggrBgEFBQcw
# AYYbaHR0cDovL3BraS5zZXZlY2VrLmNvbS9vY3NwMA0GCSqGSIb3DQEBCwUAA4IC
# AQCfr6XDtt/O8OBr+X5l49UBLaJrjUXHkAHofdC7p7BLCXIs4GYIti1lf6pas5yB
# Q428aKITITq/vEHUTyiiyKtzVkafILWXXKPxy+zmmuw9odB3Hea4ECNpcaG8UNtz
# vMm1Dr0ZrkENhcv6I3tNhRr2AOE9AKOfnVEullFD/mZqfmaNkhpnl31jk7OMSUQc
# oY8qD6BDQP9371C10gJOmp57sHfPa4Vn5E4aNzn8+o9C9HI7nNagZF5BamKOFdR2
# ui7K3krMbTuDHo+ZcA9nHnzZqiVKpEBFu8lGv9Mf+GDb9yxz6EjV3xS2RcnywX2v
# z0VUt2NGno8LudrnWpgrRy4Sl7x6FwVVKtS/o7zFSIiHgntIKFv8urSKSTukCLFK
# Y9fBIDDlWFV1ZV1DNpNWxnexWIRv2AH7YlzKQCA4Rysn01hVeBGsWFkCr9J33LmV
# enQYpk9eoYMPRwAYg48r65wOOOzLvmyLSGllH88BMvmTQ9myXqwp6NDH1psljXTl
# PUbpf7w6IZwsY0dhGhP9iyqbcrGdK0Bnf8Za6Qdj3iXtwd1VgpatFZrxOM5KawCL
# pkYl1ABupbzNpWzmC+nfymqwbYiCogPt1vHOyF4EJ73ExVDCqXkpiNvFRqmu1eaZ
# IOdbPCdl00a9rk52NKqo/BUsw16TKsDEYTA/7ACbEsnERzCCBmowggVSoAMCAQIC
# EAMBmgI6/1ixa9bV6uYX8GYwDQYJKoZIhvcNAQEFBQAwYjELMAkGA1UEBhMCVVMx
# FTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNv
# bTEhMB8GA1UEAxMYRGlnaUNlcnQgQXNzdXJlZCBJRCBDQS0xMB4XDTE0MTAyMjAw
# MDAwMFoXDTI0MTAyMjAwMDAwMFowRzELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERp
# Z2lDZXJ0MSUwIwYDVQQDExxEaWdpQ2VydCBUaW1lc3RhbXAgUmVzcG9uZGVyMIIB
# IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo2Rd/Hyz4II14OD2xirmSXU7
# zG7gU6mfH2RZ5nxrf2uMnVX4kuOe1VpjWwJJUNmDzm9m7t3LhelfpfnUh3SIRDsZ
# yeX1kZ/GFDmsJOqoSyyRicxeKPRktlC39RKzc5YKZ6O+YZ+u8/0SeHUOplsU/UUj
# joZEVX0YhgWMVYd5SEb3yg6Np95OX+Koti1ZAmGIYXIYaLm4fO7m5zQvMXeBMB+7
# NgGN7yfj95rwTDFkjePr+hmHqH7P7IwMNlt6wXq4eMfJBi5GEMiN6ARg27xzdPpO
# 2P6qQPGyznBGg+naQKFZOtkVCVeZVjCT88lhzNAIzGvsYkKRrALA76TwiRGPdwID
# AQABo4IDNTCCAzEwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0l
# AQH/BAwwCgYIKwYBBQUHAwgwggG/BgNVHSAEggG2MIIBsjCCAaEGCWCGSAGG/WwH
# ATCCAZIwKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMw
# ggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgA
# aQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQA
# ZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcA
# aQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBlAGwA
# eQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkA
# YwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAgAGEA
# cgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIA
# eQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9bAMVMB8GA1UdIwQYMBaA
# FBUAEisTmLKZB+0e36K+Vw0rZwLNMB0GA1UdDgQWBBRhWk0ktkkynUoqeRqDS/Qe
# icHKfTB9BgNVHR8EdjB0MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20v
# RGlnaUNlcnRBc3N1cmVkSURDQS0xLmNybDA4oDagNIYyaHR0cDovL2NybDQuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcmwwdwYIKwYBBQUHAQEE
# azBpMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYB
# BQUHMAKGNWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3Vy
# ZWRJRENBLTEuY3J0MA0GCSqGSIb3DQEBBQUAA4IBAQCdJX4bM02yJoFcm4bOIyAP
# gIfliP//sdRqLDHtOhcZcRfNqRu8WhY5AJ3jbITkWkD73gYBjDf6m7GdJH7+IKRX
# rVu3mrBgJuppVyFdNC8fcbCDlBkFazWQEKB7l8f2P+fiEUGmvWLZ8Cc9OB0obzpS
# CfDscGLTYkuw4HOmksDTjjHYL+NtFxMG7uQDthSr849Dp3GdId0UyhVdkkHa+Q+B
# 0Zl0DSbEDn8btfWg8cZ3BigV6diT5VUW8LsKqxzbXEgnZsijiwoc5ZXarsQuWaBh
# 3drzbaJh6YoLbewSGL33VVRAA5Ira8JRwgpIr7DUbuD0FAo6G+OPPcqvao173NhE
# MIIGzTCCBbWgAwIBAgIQBv35A5YDreoACus/J7u6GzANBgkqhkiG9w0BAQUFADBl
# MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
# d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
# b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMjExMTEwMDAwMDAwWjBiMQswCQYDVQQG
# EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
# cnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBBc3N1cmVkIElEIENBLTEwggEiMA0G
# CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDogi2Z+crCQpWlgHNAcNKeVlRcqcTS
# QQaPyTP8TUWRXIGf7Syc+BZZ3561JBXCmLm0d0ncicQK2q/LXmvtrbBxMevPOkAM
# Rk2T7It6NggDqww0/hhJgv7HxzFIgHweog+SDlDJxofrNj/YMMP/pvf7os1vcyP+
# rFYFkPAyIRaJxnCI+QWXfaPHQ90C6Ds97bFBo+0/vtuVSMTuHrPyvAwrmdDGXRJC
# geGDboJzPyZLFJCuWWYKxI2+0s4Grq2Eb0iEm09AufFM8q+Y+/bOQF1c9qjxL6/s
# iSLyaxhlscFzrdfx2M8eCnRcQrhofrfVdwonVnwPYqQ/MhRglf0HBKIJAgMBAAGj
# ggN6MIIDdjAOBgNVHQ8BAf8EBAMCAYYwOwYDVR0lBDQwMgYIKwYBBQUHAwEGCCsG
# AQUFBwMCBggrBgEFBQcDAwYIKwYBBQUHAwQGCCsGAQUFBwMIMIIB0gYDVR0gBIIB
# yTCCAcUwggG0BgpghkgBhv1sAAEEMIIBpDA6BggrBgEFBQcCARYuaHR0cDovL3d3
# dy5kaWdpY2VydC5jb20vc3NsLWNwcy1yZXBvc2l0b3J5Lmh0bTCCAWQGCCsGAQUF
# BwICMIIBVh6CAVIAQQBuAHkAIAB1AHMAZQAgAG8AZgAgAHQAaABpAHMAIABDAGUA
# cgB0AGkAZgBpAGMAYQB0AGUAIABjAG8AbgBzAHQAaQB0AHUAdABlAHMAIABhAGMA
# YwBlAHAAdABhAG4AYwBlACAAbwBmACAAdABoAGUAIABEAGkAZwBpAEMAZQByAHQA
# IABDAFAALwBDAFAAUwAgAGEAbgBkACAAdABoAGUAIABSAGUAbAB5AGkAbgBnACAA
# UABhAHIAdAB5ACAAQQBnAHIAZQBlAG0AZQBuAHQAIAB3AGgAaQBjAGgAIABsAGkA
# bQBpAHQAIABsAGkAYQBiAGkAbABpAHQAeQAgAGEAbgBkACAAYQByAGUAIABpAG4A
# YwBvAHIAcABvAHIAYQB0AGUAZAAgAGgAZQByAGUAaQBuACAAYgB5ACAAcgBlAGYA
# ZQByAGUAbgBjAGUALjALBglghkgBhv1sAxUwEgYDVR0TAQH/BAgwBgEB/wIBADB5
# BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0
# LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0Rp
# Z2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHoweDA6oDigNoY0aHR0
# cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNy
# bDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl
# ZElEUm9vdENBLmNybDAdBgNVHQ4EFgQUFQASKxOYspkH7R7for5XDStnAs0wHwYD
# VR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQEFBQADggEB
# AEZQPsm3KCSnOB22WymvUs9S6TFHq1Zce9UNC0Gz7+x1H3Q48rJcYaKclcNQ5IK5
# I9G6OoZyrTh4rHVdFxc0ckeFlFbR67s2hHfMJKXzBBlVqefj56tizfuLLZDCwNK1
# lL1eT7EF0g49GqkUW6aGMWKoqDPkmzmnxPXOHXh2lCVz5Cqrz5x2S+1fwksW5Etw
# TACJHvzFebxMElf+X+EevAJdqP77BzhPDcZdkbkPZ0XN1oPt55INjbFpjE/7WeAj
# D9KqrgB87pxCDs+R1ye3Fu4Pw718CqDuLAhVhSK46xgaTfwqIa1JMYNHlXdx3LEb
# S0scEJx3FMGdTy9alQgpECYxggQ/MIIEOwIBATB6MGwxCzAJBgNVBAYTAkNaMRcw
# FQYDVQQIEw5DemVjaCBSZXB1YmxpYzENMAsGA1UEBxMEQnJubzEQMA4GA1UEChMH
# U2V2ZWNlazEjMCEGA1UEAxMaU2V2ZWNlayBFbnRlcnByaXNlIFJvb3QgQ0ECCioc
# hHAAAQAAAH8wDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAA
# oQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4w
# DAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg5vKUUcO8SUF9o99hDrk4VT4c
# WObs9phn+cuZKLFpKFowDQYJKoZIhvcNAQEBBQAEggEAfZb7XsV0qAD/OB8jxHwN
# 8d+CEF+ytJR7tAs7FUTyj/LOl4CJEBP+g/FAGwz9vAoeobzQT7U0qfGALaylHjr9
# RMw3Fx+ee6X3FHvMVS6+msOrR5YcRkioD73lwhyb0oUxOOS5x3xjn54iEWRyjKiI
# tSaQ94Ty4EREYK8G4ktAi+vvmNfH2Y3s2Mbc1S1ooLCjXEzcRlP1w8mx0/SN0reP
# bJYpJHEkpgZg0bBnncRZ4oFtZ5eFhEqOPoG21TjRc8WlXQPH6hvEgWwvrf2S8YRt
# RxChjwMfRfFb05IuOFNSQ3GbxB6erXuFcH99QEM6q6RPzOxjYp4jeMlT3vrDXzfO
# H6GCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIBATB2MGIxCzAJBgNVBAYTAlVT
# MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
# b20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMQIQAwGaAjr/WLFr
# 1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAc
# BgkqhkiG9w0BCQUxDxcNMTkwOTIxMTMwMTA0WjAjBgkqhkiG9w0BCQQxFgQUdjQx
# WHFazKpx7mzOnsaKmXUpfBQwDQYJKoZIhvcNAQEBBQAEggEAVXcqURYfCtw3IuQN
# aPp5up6pGDrLPYpUdms5djrJsFb9BeW99GVm4ZF4/x+GeraANXJNwsBUkvHFtE//
# z7SKeTVwTUnuElYjacZ8tWyU0E+9xUi/pUfFqmbiBrnBmKBNZvAB7mor+YcQuYiK
# 763WjROc5X8aQlcq5CyYFQ+ZCk+b8AgcKT8gnziUZz/TXEC3hyx8iOsq14IJwqOR
# 15gXtWd+PqjtEaTWGV71dB99BeJCwylEFkcbt80hQMLJrspt7nUIo6lQ1t+KfzKg
# Y1PmCqhSz0WG3SxBDM0fmoT8rVtIZEpsRt2ddxEwvjs8+tuAY0UxyA2zadVqHJmz
# zidorA==
# SIG # End signature block