ADLAB PowerShell source file: buildup-SP.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 spLib
& "$libDir\lib-modifyActions.ps1"
& "$libDir\lib-buildup.ps1"
& "$libDir\lib-utils.ps1"

$vmName = $args[0]

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

Find-MarkedVolumes
$global:installSourceMediaSP = '!sevecek-installation-source-media-ExchangeSharePoint.txt'
$global:installMediaVolumeSP = Find-MarkedVolume $global:installSourceMediaSP 3
DBG ('SharePoint install media volume: {0}' -f $installMediaVolumeSP)

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

if (-not $firstAppHostInInstance) {

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


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



#########################################################################
#########################################################################
#
<# Note: these come directly from BUILDUP-SPINIT, keep it in sync ---> #>
#
#

if (Is-ValidString $firstAppConfig.version) {

  $spVersion = $firstAppConfig.version

} else {

  $spVersion = '2010'
}

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

  $spType = 'OWA'

} else {

  if ($firstAppConfig.type -eq 'foundation') {

    $spType = 'foundation'

  } else {

    $spType = 'server'
  }
}

if ($spVersion -eq '2010') { 

  $spVerID = '14.0'
  $spProductId = '{90140000-110D-0000-1000-0000000FF1CE}'
}

if ($spVersion -eq '2013') { 

  $spVerID = '15.0'
  $spProductId = '{90150000-110D-0000-1000-0000000FF1CE}'
}

DBG ('SharePoint version requested: type = {0} | version = {1} | verId = {2} | product = {3}' -f $spType, $spVersion, $spVerID, $spProductId)


#====================
DBG ('Installing SharePoint. Version: {0} | {1}' -f $spVersion, $spType)

if ($spType -eq 'OWA') {

  DBGIF ('OWA not supported on SharePoint 2010') { $spVersion -eq '2010' }

  $instRoot = Get-FirstPathFromWildcard (Join-Path $installMediaVolumeSP ('OWA{0}-*' -f $spVersion))
  $customRoot = Join-Path $global:rootDir ('SharePointServer{0}' -f $spVersion)

} elseif ($spType -eq 'foundation') {

    $instRoot = Get-FirstPathFromWildcard (Join-Path $installMediaVolumeSP ('SharePointFoundation{0}-*' -f $spVersion))
    $customRoot = Join-Path $global:rootDir ('SharePointFoundation{0}' -f $spVersion)

} else {

  $instRoot = Get-FirstPathFromWildcard (Join-Path $installMediaVolumeSP ('SharePointServer{0}-*' -f $spVersion))
  $customRoot = Join-Path $global:rootDir ('SharePointServer{0}' -f $spVersion)
}

#
#    
<# Note: <--- these come directly from BUILDUP-SPINIT, keep it in sync #>
# 
#########################################################################
#########################################################################



if ($spVersion -eq '2013') {

  $netfxVersionInfo = Detect-NetFxVersions

  # Note: there is a bug in SP 2013 SP1 installer which produces 30066 PreReqCheckFailure when the NET framework is not exactly 4.5 but has been
  #       updated to 4.6 already. For example, installer requires this version 4.5.50501 and can accept anything 4.5.xxx but cannot accept
  #       the version 4.6.01055. If this is the case, we have to adjust the value for duration of the installation
  DBGIF $MyInvocation.MyCommand.Name { -not $netfxVersionInfo.v45UpdateInstalled }

    Take-RegOwnership 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Client'
    Set-RegPermissions 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Client' "Administrators"
  
    Take-RegOwnership 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full'
    Set-RegPermissions 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' "Administrators"

    Take-RegOwnership 'SOFTWARE\Wow6432Node\Microsoft\NET Framework Setup\NDP\v4\Client'
    Set-RegPermissions 'SOFTWARE\Wow6432Node\Microsoft\NET Framework Setup\NDP\v4\Client' "Administrators"
  
    Take-RegOwnership 'SOFTWARE\Wow6432Node\Microsoft\NET Framework Setup\NDP\v4\Full'
    Set-RegPermissions 'SOFTWARE\Wow6432Node\Microsoft\NET Framework Setup\NDP\v4\Full' "Administrators"

    <#
    DBGSTART
    [Version] $netFxVersionCheck = Parse-VersionSafe ((Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Client').Version)
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND
    DBG ('The current NETFX version obtained from registry is: {0}' -f $netFxVersionCheck)
    #>
    
    [Version] $netFxVersionCheck = $netfxVersionInfo.v45Version

    if (-not (($netFxVersionCheck.Major -eq 4) -and ($netFxVersionCheck.Minor -eq 5))) {

      DBGIF ('It is possible that the SharePoint setup.exe will fail with error 30066 due to NETFX version being too new: required = {0} | current = {1}' -f '4.5.50501', $netFxVersionCheck) { $true }

      DBG ('')
      DBG ('Repair method for during installation:')
      DBG ('')

      DBGSTART
      [string] $compatibleNetFxVersionStr = '4.5.50501'
      sp 'hklm:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Client' -Name Version_ -Value $compatibleNetFxVersionStr
      sp 'hklm:\SOFTWARE\Wow6432Node\Microsoft\NET Framework Setup\NDP\v4\Client' -Name Version_ -Value $compatibleNetFxVersionStr
      sp 'hklm:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -Name Version_ -Value $compatibleNetFxVersionStr
      sp 'hklm:\SOFTWARE\Wow6432Node\Microsoft\NET Framework Setup\NDP\v4\Full' -Name Version_ -Value $compatibleNetFxVersionStr

      [string] $currentNetFxVersionStr = '{0}.{1}.{2}' -f $netFxVersionCheck.Major, $netFxVersionCheck.Minor, $netFxVersionCheck.Build
      sp 'hklm:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Client' -Name Version__ -Value $currentNetFxVersionStr
      sp 'hklm:\SOFTWARE\Wow6432Node\Microsoft\NET Framework Setup\NDP\v4\Client' -Name Version__ -Value $currentNetFxVersionStr
      sp 'hklm:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -Name Version__ -Value $currentNetFxVersionStr
      sp 'hklm:\SOFTWARE\Wow6432Node\Microsoft\NET Framework Setup\NDP\v4\Full' -Name Version__ -Value $currentNetFxVersionStr
      DBGER $MyInvocation.MyCommand.Name $error
      DBGEND

      $netfxRepairBefore = @"

sp 'hklm:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Client' -Name Version -Value '$compatibleNetFxVersionStr'
sp 'hklm:\SOFTWARE\Wow6432Node\Microsoft\NET Framework Setup\NDP\v4\Client' -Name Version -Value '$compatibleNetFxVersionStr'
sp 'hklm:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -Name Version -Value '$compatibleNetFxVersionStr'
sp 'hklm:\SOFTWARE\Wow6432Node\Microsoft\NET Framework Setup\NDP\v4\Full' -Name Version -Value '$compatibleNetFxVersionStr'

"@

      DBG ($netfxRepairBefore)

      DBG ('')
      DBG ('Return correct values back after the installation:')
      DBG ('')

      $netfxRepairAfter = @"

sp 'hklm:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Client' -Name Version -Value '$currentNetFxVersionStr'
sp 'hklm:\SOFTWARE\Wow6432Node\Microsoft\NET Framework Setup\NDP\v4\Client' -Name Version -Value '$currentNetFxVersionStr'
sp 'hklm:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -Name Version -Value '$currentNetFxVersionStr'
sp 'hklm:\SOFTWARE\Wow6432Node\Microsoft\NET Framework Setup\NDP\v4\Full' -Name Version -Value '$currentNetFxVersionStr'

"@

      DBG ($netfxRepairAfter)

      DBG ('')
      DBG ('Press ENTER when ready to continue')
      Read-Host | Out-Null
    }
}

 

if ($spType -ne 'OWA') {


  [hashtable] $sqlRSCompatibilityBestVersion = @{

    # Note: in case we have SSRS in SharePoint integrated mode, the Add-In must match the version of the SQL server
    #       or when starting the service instance, we get error 7034, SharePoint Foundation: An attempt to start/stop instance  of service Microsoft SQL Server Reporting Services Shared Service did not succeed. Installation Error: Could not find SharedCode on SOFTWARE\Microsoft\Microsoft SQL Server\120 registry key.
    
    'sp2010-sql2008' = 'sqlrsAddin2008';
    'sp2010-sql2012' = 'sqlrsAddin2012';
    'sp2010-sql2014' = 'sqlrsAddin2014';

    'sp2013-sql2012' = 'sqlrsAddin2012';
    'sp2013-sql2014' = 'sqlrsAddin2014';
    'sp2013-sql2016' = 'sqlrsAddin2016';

    'sp2016-sql2014' = 'sqlrsAddin2016';
    'sp2016-sql2016' = 'sqlrsAddin2016';

  }


  [hashtable] $sqlPowerPivotCompatibilityBestVersion = @{

    'sp2010-sql2008' = $null;
    'sp2010-sql2012' = $null;
    'sp2010-sql2014' = $null;

    'sp2013-sql2012' = 'powerpivotAddin2012';
    'sp2013-sql2014' = 'powerpivotAddin2014';
    'sp2013-sql2016' = 'powerpivotAddin2016';

    'sp2016-sql2014' = 'powerpivotAddin2016';
    'sp2016-sql2016' = 'powerpivotAddin2016';

  }



  DBG ('Installing SharePoint binaries, either foundation or server')

  #================  
  DBG ('Update prerequisite installer paths: instRoot = {0}' -f $instRoot)
  $prereqFile = Get-DataFileApp ('sp-install-prerequisities-{0}' -f $global:thisOSVersionNormal) $null '.txt'
  $argReplacement = 'root${0}' -f (Escape-ForMultiValue (Join-Path $instRoot '!SevecekBuildup'))

  $srcPrereqFile = Join-Path $customRoot ('!install-prerequisities-{0}.txt' -f $global:thisOSVersionNormal)
  DBGIF ('This SharePoint version is not supported on this system: {0}' -f $srcPrereqFile) { -not (Test-Path -Literal $srcPrereqFile) }
  if (-not (Test-Path -Literal $srcPrereqFile)) {

    exit
  }

  Replace-ArgumentsInFile $srcPrereqFile $argReplacement $prereqFile Unicode

  DBG ('Install SP prerequisites')
  $prereqExitCode = Run-Process (Join-Path $instRoot 'PrerequisiteInstaller.exe') ('/unattended {0}' -f ((Get-Content $prereqFile) -join ' ')) $false $null $null $null $false $null $true

  if ($prereqExitCode -eq 3010) {

    DBG ('Prerequisite installer returned 3010 to force restart and recycle prerequisite installation the next time.')
    exit $global:VM_RECYCLE_REQUIRED 
  }


  #================
  # Note: there is a weird bug within ARPWrite action which sometimes fails the installation with 1603 error
  #       one solution was to empty %temp% which I try here. We will see if that helps

  DBG ('Save the prerequisite installer log files from TEMP which will be deleted: {0}' -f $env:TEMP)
  DBGSTART
  Copy-Item (Join-Path $env:TEMP 'prerequisiteinstaller.*.log') $global:outPath
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND

  # Note: deleting the temp is contraproductive, we just try to redirect it somewhere else
  <#
  DBGSTART
  DBG ('Empty %temp% to prevent the ARPWrite action failure possibly: {0}' -f $env:TEMP)
  Remove-Item "$env:TEMP\*.*" -Recurse -EA SilentlyContinue
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND
  #>
  Redirect-TempToOutput


  #================  
  DBG ('Update installation config.xml')
  $configXMLPath = Get-DataFileApp 'sp-config' $null '.xml'
  $argReplacement = 'spData${0}|logDir${1}' -f (Escape-ForMultiValue (Resolve-VolumePath $firstAppConfig.dataDir), $global:outPath)
  Replace-ArgumentsInFile (Join-Path $customRoot '!config.xml') $argReplacement $configXMLPath Unicode

  DBG ('Install SP binaries')
  # Note: there is sometimes the latent behavior of Windows Error Reporting (WER) while Admin service or Timer service fails
  #       which just prevents the installation from proceeding (regardless if it fails or succeeds eventually)
  $binariesExitCode = Run-Process (Join-Path $instRoot 'setup.exe') ('/config "{0}"' -f $configXMLPath) -returnExitCode $true -disableWER
  DBG ('Binaries installation returned: {0}' -f $binariesExitCode)
  DBGIF ('Binaries failed, try again: {0} = 0x{0:X}' -f $binariesExitCode) { $binariesExitCode -ne 0 }

  if ($binariesExitCode -ne 0) {

    DBG ('Binaries did not install well, uninstall and try again')
    # SharePoint 2013 ProductCode = {90150000-110D-0000-1000-0000000FF1CE}
    # SharePoint 2010 ProductCode = {90140000-110D-0000-1000-0000000FF1CE}

    $spProductsDebug = Get-WmiQueryArray '.' 'SELECT * FROM Win32_Product WHERE Name LIKE "%sharepoint%"'
    DBG ('Found already installed SharePoint products: {0} | {1}' -f (Get-CountSafe $spProductsDebug), ($spProductsDebug | select Name, IdentifyingNumber | ft -Auto | Out-String))


    DBG ('If we really find the big SharePoint binaries: {0}' -f $spProductId)

    $sharePointProduct = Get-WmiQuerySingleObject '.' ('SELECT * FROM win32_product WHERE IdentifyingNumber = "{0}"' -f $spProductId)

    if (Is-NonNull $sharePointProduct) {

      DBG ('Going to uninstall the incorrectly installed SharePoint')
      DBGSTART
      $wmiRs = $sharePointProduct.Uninstall()
      DBGER $MyInvocation.MyCommand.MyCommand $error
      DBGEND
      DBGWMI $wmiRs
    }

    DBG ('Give it 1 minute and try again.')
    Start-Sleep -Seconds 60
    exit $global:VM_RECYCLE_REQUIRED
  }


  #================  
  DBG ('Install SP MUI binaries: {0} | {1}' -f (Is-ValidString $firstAppConfig.mui), $firstAppConfig.mui)

  if (Is-ValidString $firstAppConfig.mui) {

    [string[]] $muiLanguages = Split-MultiValue $firstAppConfig.mui
    foreach ($oneMuiLanguage in $muiLanguages) {
   
      DBG ('Update MUI installation mui-config.xml: {0}' -f $oneMuiLanguage)

      $muiConfigXMLPath = Get-DataFileApp ('sp-mui-config-{0}' -f $oneMuiLanguage) $null '.xml'
      $argReplacement = 'logDir${0}' -f (Escape-ForMultiValue $global:outPath)
      Replace-ArgumentsInFile (Join-Path $customRoot ('!mui-config.xml')) $argReplacement $muiConfigXMLPath Unicode

      Run-Process (Join-Path $instRoot ('!MUI-{0}\setup.exe' -f $oneMuiLanguage)) ('/config "{0}"' -f $muiConfigXMLPath) -disableWER
    }  
  }


  #================
  DBG ('Should we install an SQL RS (SSRS) Addin for SharePoint: {0}' -f $firstAppConfig.rsAddIn, (Is-ValidString $firstAppConfig.rsAddIn))
  if (Is-ValidString $firstAppConfig.rsAddIn) {

    [string] $ssrsAddin = $sqlRSCompatibilityBestVersion[('sp{0}-sql{1}' -f $spVersion, $firstAppConfig.rsAddIn)]
    DBG ('The SSRS add-in version requested: {0}' -f $ssrsAddin)

    DBGIF ('Unsupported SharePoint version for the SQL Server Reporting Services version requested: sp = {0} | sql = {1}' -f $spVersion, $firstAppConfig.rsAddIn) { Is-EmptyString $ssrsAddin }

    if (Is-ValidString $ssrsAddin) {

      Run-Process msiexec ('/i "{0}" /q /norestart' -f (Join-Path (Join-Path $instRoot '!SevecekBuildup') ('{0}-rsSharePoint.msi' -f $ssrsAddin)))
    }
  }


  #================
  DBG ('Should we install an SQL PowerPivot (SSAS) Addin for SharePoint: {0}' -f $firstAppConfig.ppvtAddIn, (Is-ValidString $firstAppConfig.ppvtAddIn))
  if (Is-ValidString $firstAppConfig.ppvtAddIn) {

    [string] $ppvtAddin = $sqlPowerPivotCompatibilityBestVersion[('sp{0}-sql{1}' -f $spVersion, $firstAppConfig.ppvtAddIn)]
    DBG ('The PowerPivot add-in version requested: {0}' -f $ppvtAddin)
    DBGIF ('Unsupported SharePoint version for the SQL Server Analysis Services PowerPivot version requested: sp = {0} | sql = {1}' -f $spVersion, $firstAppConfig.ppvtAddIn) { Is-EmptyString $ppvtAddin }
  
    if (Is-ValidString $ppvtAddin) {

      Run-Process msiexec ('/i "{0}" /q /norestart' -f (Join-Path (Join-Path $instRoot '!SevecekBuildup') ('{0}-spPowerPivot.msi' -f $ppvtAddin)))
    }
  }


  #================
  # Note: it is the problem with ADOMD version missing on installations where SQL Server 2014 or SQL Server 2012 has
  #       already been installed (RS SP integrated mode for example) yet before the SharePoint binaries and the ADOMD
  #       does not get installed if a newer version of the module is installed
  if ($spVersion -eq '2013') {

    DBG ('Fix the bug about ADOMD v10.0.0.0 assembly missing')
    Run-Process msiexec ('/i "{0}" /q /norestart' -f (Join-Path (Join-Path $instRoot '!SevecekBuildup') 'adomd-v10.0.0.0-fix-SQLSERVER2008_ASADOMD10.msi'))
  }


  #
  #
  #
  #

  DBG ('Install Cummulative Update if any exists')
  #SharePointServer2013CU-2016-07-KB3115293
  $cuExeFile = Get-FirstPathFromWildcard (Join-Path $installMediaVolumeSP ('SharePointServer{0}CU-?*-?*-KB?*\*.exe' -f $spVersion)) -doNotAssertNonExisting $true
  DBG ('Any cummulative update found: {0} | {1}' -f (Is-ValidString $cuExeFile), $cuExeFile)

  if (Is-ValidString $cuExeFile) {

    DBG ('Going to update to the newest CU: {0}' -f $cuExeFile)
    Run-Process $cuExeFile ('/passive /norestart')
  }

  #
  #
  #

} else {



  DBG ('Installing OWA')

  [string] $owaBaseConfigXMLPath = Join-Path $customRoot owa-config.xml
  DBG ('Will install OWA server binaries: {0} | exists = {1} | cfg = {2} | cfgExists = {3}' -f $instRoot, (Test-Path $instRoot), $owaBaseConfigXMLPath, (Test-Path $owaBaseConfigXMLPath))

  $owaConfigXMLPath = Get-DataFileApp 'sp-owa-config' $null '.xml'
  $argReplacement = 'logDir${0}' -f (Escape-ForMultiValue $global:outPath)
  Replace-ArgumentsInFile $owaBaseConfigXMLPath $argReplacement $owaConfigXMLPath Unicode

  $binariesExitCode = Run-Process (Join-Path $instRoot 'setup.exe') ('/config "{0}"' -f $owaConfigXMLPath) -returnExitCode $true -disableWER
  DBG ('Binaries installation returned: {0}' -f $binariesExitCode)
  DBGIF $MyInvocation.MyCommand.Name { $binariesExitCode -ne 0 }

  if ($binariesExitCode -ne 0) {

    DBG ('Binaries failed, let us restart and try again')
    exit $global:VM_RECYCLE_REQUIRED
  }


  DBG ('Any valid OWA MUI requirements: {0} | {1}' -f (Is-ValidString $firstAppConfig.mui), $firstAppConfig.mui)
  if (Is-ValidString $firstAppConfig.mui) {

    [string[]] $muiLanguages = Split-MultiValue $firstAppConfig.mui
    DBG ('MUI languages requested: {0}' -f ($muiLanguages -join ', '))

    foreach ($oneMuiLanguage in $muiLanguages) {
   
      [string] $muiBaseConfigXMLPath = Join-Path $customRoot owa-mui-config.xml
      DBG ('Will install OWA MUI binaries: {0} | cfg = {1} | cfgExists = {2}' -f $oneMuiLanguage, $muiBaseConfigXMLPath, (Test-Path $muiBaseConfigXMLPath))

      $muiConfigXMLPath = Get-DataFileApp ('sp-owa-mui-config-{0}' -f $oneMuiLanguage) $null '.xml'
      $argReplacement = 'logDir${0}' -f (Escape-ForMultiValue $global:outPath)
      Replace-ArgumentsInFile $muiBaseConfigXMLPath $argReplacement $muiConfigXMLPath Unicode

      # Note: the installation of OWA 2013 does not withstand our !M
      Run-Process (Join-Path ('{0}-MUI' -f $instRoot) ('!MUI-{0}\setup.exe' -f $oneMuiLanguage)) ('/config "{0}"' -f $muiConfigXMLPath) -disableWER
    }  
  }
}




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



# SIG # Begin signature block
# MIIc/QYJKoZIhvcNAQcCoIIc7jCCHOoCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDP3L6hxAdA6GGl
# wkrpQivygkE265P3yIHzhuO/WQtsw6CCGAQwggTlMIIDzaADAgECAhA5vUKe0oFu
# utW8yQO0umXnMA0GCSqGSIb3DQEBCwUAMHUxCzAJBgNVBAYTAklMMRYwFAYDVQQK
# Ew1TdGFydENvbSBMdGQuMSkwJwYDVQQLEyBTdGFydENvbSBDZXJ0aWZpY2F0aW9u
# IEF1dGhvcml0eTEjMCEGA1UEAxMaU3RhcnRDb20gQ2xhc3MgMiBPYmplY3QgQ0Ew
# HhcNMTYxMjAxMTU1MTEzWhcNMTgxMjAxMTU1MTEzWjBRMQswCQYDVQQGEwJDWjEa
# MBgGA1UECAwRSmlob21vcmF2c2t5IEtyYWoxDTALBgNVBAcMBEJybm8xFzAVBgNV
# BAMMDk9uZHJlaiBTZXZlY2VrMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
# AQEAr9E9hNj06bash9JX97kpsqK9Z/ciOBC6trI4nvlW9CPwhKBTb5wArhxLYZBG
# 9jWPWrdy1nL/cm5qMqBb/mogYwMwvEYWMvsIOOVn6HD9lVhNAovD6PHz0ziBBKIs
# zXTjyUPQaoIlIELovz967m78HJdUZJGxqhluAsS9o9/fEzA7XXUhUuqRKsetuZV/
# Asfh5sOveeoRsbeW4daTWvtz3TJuULL0w43LNVYJkd6LL8cegvLPVZUe1N7skvid
# EvntdlowQsJlqFdrH3SGKIPKA6ObcY8SZWkEQSbVBF8Kum1UT+jN0gm+84FwOg5W
# qKx+VvTK2ljVWnPrCD0Zzu2oIQIDAQABo4IBkzCCAY8wDgYDVR0PAQH/BAQDAgeA
# MBMGA1UdJQQMMAoGCCsGAQUFBwMDMAkGA1UdEwQCMAAwHQYDVR0OBBYEFG2vSo3N
# hQWILeUs0oN9XzHTejcfMB8GA1UdIwQYMBaAFD5ik5rXxxnuPo9JEIVVFSDjlIQc
# MG0GCCsGAQUFBwEBBGEwXzAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Auc3RhcnRz
# c2wuY29tMDcGCCsGAQUFBzAChitodHRwOi8vYWlhLnN0YXJ0c3NsLmNvbS9jZXJ0
# cy9zY2EuY29kZTIuY3J0MDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwuc3Rh
# cnRzc2wuY29tL3NjYS1jb2RlMi5jcmwwIwYDVR0SBBwwGoYYaHR0cDovL3d3dy5z
# dGFydHNzbC5jb20vMFEGA1UdIARKMEgwCAYGZ4EMAQQBMDwGCysGAQQBgbU3AQIF
# MC0wKwYIKwYBBQUHAgEWH2h0dHBzOi8vd3d3LnN0YXJ0c3NsLmNvbS9wb2xpY3kw
# DQYJKoZIhvcNAQELBQADggEBAJuRiEvHtIYSpsmMkPhTz4QOOShN3p5KWdf8vm71
# A33CR9fds10d8D2B2aE+vjmHJ69GY0bbfg5oZY2Lsq2euL7Da5/hS8+6T3MEtD4h
# njfHV7mxmoSfFuy/KDipoV6uwhI+ksqchXYdUH+5uCQO0MOO8ITjAgzUQsnZ4UIB
# HBGeP+e+3ljxSYSXWdPIrgxdR971P/HhWSVfKNlmBgEKMQM5Jy0aAd4jxSl/AzdY
# t0+6pliFJ1peGhdFni2Fm8fu5oN68aTIrNtc5WY7Lzgf+sRTVeWORWS37+1zAD0m
# jzd8gyfBLxRuaRSfjYxny0rLXelAwfiA3ze2DU2Bfg9/rfcwggXYMIIDwKADAgEC
# AhBsO9J+3TyUnpWOKKmzx1egMA0GCSqGSIb3DQEBCwUAMH0xCzAJBgNVBAYTAklM
# MRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMSswKQYDVQQLEyJTZWN1cmUgRGlnaXRh
# bCBDZXJ0aWZpY2F0ZSBTaWduaW5nMSkwJwYDVQQDEyBTdGFydENvbSBDZXJ0aWZp
# Y2F0aW9uIEF1dGhvcml0eTAeFw0xNTEyMTYwMTAwMDVaFw0zMDEyMTYwMTAwMDVa
# MHUxCzAJBgNVBAYTAklMMRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMSkwJwYDVQQL
# EyBTdGFydENvbSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEjMCEGA1UEAxMaU3Rh
# cnRDb20gQ2xhc3MgMiBPYmplY3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
# ggEKAoIBAQC5FARY97LFhiwIMmCtCCbAgXe5aBnZFSsdGGnk2hqWBZcuZHkaqT1R
# M1rQd2r0ApNBw466cBur2Ht0b5jo17mpPmh2pImgIqwX1in4u7hhn9IH0GYOMEcg
# K3ACHv5zCRxxNLXifqmsqKfxjjpABnaSyvd4bO9YBXN9f4NQ6aJVAuMArpanxsJk
# e+P4WECVLk17v92CAN5JVaczI+baT/lgo5NVcTEkloCViSbIfU6ILeyhOSQZvpom
# MYk8eJqI0nimOTJJfmXangNDsrX8np+3lXD0+6rCZisXRWIaeffyTMHZ31Qj1D50
# WYdRtX5yev4WgaXoKJQN3lkgXUcytvyHAgMBAAGjggFaMIIBVjAOBgNVHQ8BAf8E
# BAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAy
# BgNVHR8EKzApMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0c3NsLmNvbS9zZnNjYS5j
# cmwwZgYIKwYBBQUHAQEEWjBYMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5zdGFy
# dHNzbC5jb20wMAYIKwYBBQUHMAKGJGh0dHA6Ly9haWEuc3RhcnRzc2wuY29tL2Nl
# cnRzL2NhLmNydDAdBgNVHQ4EFgQUPmKTmtfHGe4+j0kQhVUVIOOUhBwwHwYDVR0j
# BBgwFoAUTgvvGqRAW6UXaYcwyjRoQ9BBrvIwPwYDVR0gBDgwNjA0BgRVHSAAMCww
# KgYIKwYBBQUHAgEWHmh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeTANBgkq
# hkiG9w0BAQsFAAOCAgEAY6U81bNtJyjY67pTrzAL6kpdEtX5mspw+kxjjNdNVH5G
# 6lLnhaEkIxqdpvY/Wdw+UdNtExs+N8efKPSwh2m/BxXj2fSeLMwXcwHFookScEER
# 8ez0quCNzioqNHac7LCXPEnQzbtG2FHlePKNDWh8eU6KxiAzNzIrIxPthinHGgLT
# BOACHQM2YTlD8YoU5oN3dLmBOqtH0BDMZoLcjEIoEW1zC+TnVb3yU1G0xub6gnN7
# lP50vbAiHJYrnywQiXaloBV8B9YYfe6ZgvjqxwufwFcMVyE3UmCuDTsOpjqDEKpJ
# 25s+FUdkie5VqCS1aaudLo31X+9UvP45pfgyRqzyfUnVEhH4ZXxlBWZMzj2Xov5+
# m/+H3kxYuFA5xdqdshj/Zx00S7PkCSF+8M1NCcvFgQwjIw61bZAjDBl3P3a8xNTX
# sb2CjFdiNKbT3LD6IGeIf0b/EbPf0FXdvBrxm0ofMOhnngdPolPYCtoOGtZPAVe/
# xeu+/ZyKv6TSHlshaUO0iYfsmbXnZ51vvt/kkjwms9/qPFxSuE0fjEfF7aQazwRE
# Df2hiVPR0pAhvShtM3oU4XreEFEUWEYHs25fYV4WMmxkUKSgmSmwRq45tvtGH4LT
# b5+cd+iLqK8rBQL0E6xaUjjGfsYx7bueIvqTvCkrQvoxMbn/qDHCiypowDVq6TAw
# ggZqMIIFUqADAgECAhADAZoCOv9YsWvW1ermF/BmMA0GCSqGSIb3DQEBBQUAMGIx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0Et
# MTAeFw0xNDEwMjIwMDAwMDBaFw0yNDEwMjIwMDAwMDBaMEcxCzAJBgNVBAYTAlVT
# MREwDwYDVQQKEwhEaWdpQ2VydDElMCMGA1UEAxMcRGlnaUNlcnQgVGltZXN0YW1w
# IFJlc3BvbmRlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKNkXfx8
# s+CCNeDg9sYq5kl1O8xu4FOpnx9kWeZ8a39rjJ1V+JLjntVaY1sCSVDZg85vZu7d
# y4XpX6X51Id0iEQ7Gcnl9ZGfxhQ5rCTqqEsskYnMXij0ZLZQt/USs3OWCmejvmGf
# rvP9Enh1DqZbFP1FI46GRFV9GIYFjFWHeUhG98oOjafeTl/iqLYtWQJhiGFyGGi5
# uHzu5uc0LzF3gTAfuzYBje8n4/ea8EwxZI3j6/oZh6h+z+yMDDZbesF6uHjHyQYu
# RhDIjegEYNu8c3T6Ttj+qkDxss5wRoPp2kChWTrZFQlXmVYwk/PJYczQCMxr7GJC
# kawCwO+k8IkRj3cCAwEAAaOCAzUwggMxMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMB
# Af8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMIIBvwYDVR0gBIIBtjCCAbIw
# ggGhBglghkgBhv1sBwEwggGSMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdp
# Y2VydC5jb20vQ1BTMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4AeQAgAHUAcwBl
# ACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBu
# AHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0
# AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAAYQBuAGQAIAB0
# AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcAcgBlAGUAbQBl
# AG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5
# ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQAZQBkACAAaABl
# AHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMAsGCWCGSAGG/WwD
# FTAfBgNVHSMEGDAWgBQVABIrE5iymQftHt+ivlcNK2cCzTAdBgNVHQ4EFgQUYVpN
# JLZJMp1KKnkag0v0HonByn0wfQYDVR0fBHYwdDA4oDagNIYyaHR0cDovL2NybDMu
# ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcmwwOKA2oDSGMmh0
# dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRENBLTEuY3Js
# MHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNl
# cnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20v
# RGlnaUNlcnRBc3N1cmVkSURDQS0xLmNydDANBgkqhkiG9w0BAQUFAAOCAQEAnSV+
# GzNNsiaBXJuGziMgD4CH5Yj//7HUaiwx7ToXGXEXzakbvFoWOQCd42yE5FpA+94G
# AYw3+puxnSR+/iCkV61bt5qwYCbqaVchXTQvH3Gwg5QZBWs1kBCge5fH9j/n4hFB
# pr1i2fAnPTgdKG86Ugnw7HBi02JLsOBzppLA044x2C/jbRcTBu7kA7YUq/OPQ6dx
# nSHdFMoVXZJB2vkPgdGZdA0mxA5/G7X1oPHGdwYoFenYk+VVFvC7Cqsc21xIJ2bI
# o4sKHOWV2q7ELlmgYd3a822iYemKC23sEhi991VUQAOSK2vCUcIKSK+w1G7g9BQK
# Ohvjjz3Kr2qNe9zYRDCCBs0wggW1oAMCAQICEAb9+QOWA63qAArrPye7uhswDQYJ
# KoZIhvcNAQEFBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IElu
# YzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQg
# QXNzdXJlZCBJRCBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTIxMTExMDAwMDAw
# MFowYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgQXNzdXJlZCBJ
# RCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6IItmfnKwkKV
# pYBzQHDSnlZUXKnE0kEGj8kz/E1FkVyBn+0snPgWWd+etSQVwpi5tHdJ3InECtqv
# y15r7a2wcTHrzzpADEZNk+yLejYIA6sMNP4YSYL+x8cxSIB8HqIPkg5QycaH6zY/
# 2DDD/6b3+6LNb3Mj/qxWBZDwMiEWicZwiPkFl32jx0PdAug7Pe2xQaPtP77blUjE
# 7h6z8rwMK5nQxl0SQoHhg26Ccz8mSxSQrllmCsSNvtLOBq6thG9IhJtPQLnxTPKv
# mPv2zkBdXPao8S+v7Iki8msYZbHBc63X8djPHgp0XEK4aH631XcKJ1Z8D2KkPzIU
# YJX9BwSiCQIDAQABo4IDejCCA3YwDgYDVR0PAQH/BAQDAgGGMDsGA1UdJQQ0MDIG
# CCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcD
# CDCCAdIGA1UdIASCAckwggHFMIIBtAYKYIZIAYb9bAABBDCCAaQwOgYIKwYBBQUH
# AgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5o
# dG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0
# AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1
# AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABp
# AGcAaQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBl
# AGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBo
# AGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg
# AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAg
# AGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9bAMVMBIGA1UdEwEB
# /wQIMAYBAf8CAQAweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8v
# b2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRp
# Z2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6
# MHgwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3Vy
# ZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9E
# aWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwHQYDVR0OBBYEFBUAEisTmLKZB+0e
# 36K+Vw0rZwLNMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqG
# SIb3DQEBBQUAA4IBAQBGUD7Jtygkpzgdtlspr1LPUukxR6tWXHvVDQtBs+/sdR90
# OPKyXGGinJXDUOSCuSPRujqGcq04eKx1XRcXNHJHhZRW0eu7NoR3zCSl8wQZVann
# 4+erYs37iy2QwsDStZS9Xk+xBdIOPRqpFFumhjFiqKgz5Js5p8T1zh14dpQlc+Qq
# q8+cdkvtX8JLFuRLcEwAiR78xXm8TBJX/l/hHrwCXaj++wc4Tw3GXZG5D2dFzdaD
# 7eeSDY2xaYxP+1ngIw/Sqq4AfO6cQg7PkdcntxbuD8O9fAqg7iwIVYUiuOsYGk38
# KiGtSTGDR5V3cdyxG0tLHBCcdxTBnU8vWpUIKRAmMYIETzCCBEsCAQEwgYkwdTEL
# MAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKTAnBgNVBAsTIFN0
# YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSMwIQYDVQQDExpTdGFydENv
# bSBDbGFzcyAyIE9iamVjdCBDQQIQOb1CntKBbrrVvMkDtLpl5zANBglghkgBZQME
# AgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEM
# BgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqG
# SIb3DQEJBDEiBCCTMrvHK+kIxn4tVxj76zJcIGIoHQqNfcbsxNO2X8rmXjANBgkq
# hkiG9w0BAQEFAASCAQAlO6bJAOc0I5MjPzddT7ajt5bU0rSJ0ze6sAuA+lxrAX2h
# p5WzH5wGpvuYdzLda/qW6H2X+QrKNuPLMQb7cxvmwnibSHjSPtySuC+IaFw9GCgH
# OBfYUtI3K8IOYkn/gkAkAtcvJ4cQ+JMJ/uMH0DjCNyLkU0iyVUFDs7pE1RcTfWJr
# TLlOoKMbj8iuRinzikoSRunZgjN6p3+xVDxcLpM0G3ptXPDBahYKeWzz9cAmYhPw
# YiniZ7Mum6tu97bunWokCIzUa25OpfiWICd3vNLNNgLilmIaQ1u9p55ejLasp+bG
# 8kA9IlHYLYE+trLzH3xnXoE71fjp5fB+Dx3pNZUioYICDzCCAgsGCSqGSIb3DQEJ
# BjGCAfwwggH4AgEBMHYwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0
# IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNl
# cnQgQXNzdXJlZCBJRCBDQS0xAhADAZoCOv9YsWvW1ermF/BmMAkGBSsOAwIaBQCg
# XTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xODAx
# MjMxNjA5NTlaMCMGCSqGSIb3DQEJBDEWBBQa07dP33bjoI6RNft9PO46pekI/DAN
# BgkqhkiG9w0BAQEFAASCAQAOAsWtJdwqCMeO3hwgHaKaPP91mNW/8p4DOmPiEJsX
# sXlWJwHQ6CzdmDgSWcp+DjOy72L83VLZlOR1uE01OGdwvVQJiy8FmjBV9YSTWx22
# uLmBUIdSmSN1lO6ysQ2+838lgFx5MbQbZWTBz1Vlzs4tf0DAvtyw28pOp/VaGdI4
# p89b1K1HigjLNIXC9UnY2GvEteuOvk7EAKqxOVjIRSouDQ9nw1E36iXxYoYCcKWq
# rSMVNHTmTzkqx2jOsctRNKYohIBmW7bU8nqIxCTpjMz2JDnpPfGSyCKgZBHzabGk
# 3SNr29l/wCdyhY3DGrpSpNId8RUnmt1QFISUrY1sH8VZ
# SIG # End signature block