commands/Set-VIMachineCertificate.ps1

using module VMware.PowerCLI.Sdk.Types
using module VMware.PowerCLI.VCenter.Types.CertificateManagement
using namespace VMware.VimAutomation.ViCore.Types.V1
using namespace VMware.VimAutomation.ViCore.Types.V1.Inventory
using namespace System.Security.Cryptography
using namespace System.Security.Cryptography.X509Certificates
using namespace VMware.VimAutomation.Sdk.Util10Ps.BaseCmdlet

. (Join-Path $PSScriptRoot "../utils/ConvertTo-Pem.ps1")
. (Join-Path $PSScriptRoot "../utils/Report-CommandUsage.ps1")
. (Join-Path $PSScriptRoot "../utils/ConvertTo-PemCertificate.ps1")
. (Join-Path $PSScriptRoot "../utils/Connection.ps1")

<#
.SYNOPSIS
 
This cmdlet sets a machine SSL certificate to a vCenter Server instance or a connected ESXi host.
 
.DESCRIPTION
 
This cmdlet sets a machine SSL certificate to a vCenter Server instance or a connected ESXi host.
 
By default, the certificate is set to the vCenter Server instance. If you want to set the certificate to a specific ESXi host, you must use the VMHost parameter.
 
The result from the command is the updated vCenter Server or ESXi entity with the certificate that was set.
 
To use this cmdlet, you must connect to vCenter Server through the Connect-VIServer cmdlet.
 
IMPORTANT: When you change the machine SSL certificate of a vCenter Server system prior to version 8.0.3, a restart is triggered.
 
.PARAMETER VMHost
 
Specifies the ESXi host to which you want to set a machine SSL certificate.
 
.PARAMETER PemCertificate
 
Specifies a certificate in PEM format to be set as the machine SSL certificate to a vCenter Server instance or an ESXi host.
 
.PARAMETER X509Certificate
 
Specifies a certificate as an X509Certificate object to be set as the machine SSL certificate to a vCenter Server instance or an ESXi host.
 
 
.PARAMETER PemKey
 
Specifies the private key in PEM format of the certificate that you want to set to a vCenter Server system or an ESXi host.
 
.PARAMETER Key
 
Specifies the private key as AsymmetricAlgorithm type of the certificate that you want to set.
 
Note: To use this parameter, you must have PowerShell version 7.1 or later.
 
 
.EXAMPLE
PS C:\> $certificatePem = Get-Content cert.pem -Raw
PS C:\> Set-VIMachineCertificate -PemCertificate $certificatePem
 
Sets the certificate from the cert.pem file to the vCenter Server system.
 
 
.EXAMPLE
PS C:\> $certificatePem = Get-Content cert.pem -Raw
PS C:\> Set-VIMachineCertificate -PemCertificate $certificatePem -VMHost 'MyHost'
 
Sets the certificate from the cert.pem file to the 'MyHost' ESXi host.
 
 
.OUTPUTS
 
The updated ViMachineCertificateInfo object
 
.LINK
 
https://developer.vmware.com/docs/powercli/latest/vmware.powercli.vcenter/commands/set-vimachinecertificate
 
 
#>

function Set-VIMachineCertificate {
   [CmdletBinding(
      ConfirmImpact = "High",
      DefaultParameterSetName = "VCenter",
      SupportsShouldProcess = $true)]
   [OutputType([ViMachineCertificateInfo])]
   Param (
      [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0, ParameterSetName = 'VmHost')]
      [ObnArgumentTransformation([VMHost])]
      [VMHost]
      $VMHost,

      [Parameter()]
      [String]
      $PemCertificate,

      [Parameter()]
      [String]
      $PemKey,

      [Parameter()]
      [X509Certificate]
      $X509Certificate,

      [Parameter()]
      [AsymmetricAlgorithm]
      $Key,

      [Parameter()]
      [ObnArgumentTransformation([VIServer], Critical = $true)]
      [VIServer]
      $Server
   )

   Begin {
      Report-CommandUsage $MyInvocation

      # Handle Server obn first
      if($Server) {
         $resolvedServer = Resolve-ObjectByName -Object $Server `
             -Type ([VIServer]) `
             -OneObjectExpected

         $Server = [VIServer] $resolvedServer
      }

      $activeServer = GetActiveServer($Server)
      if (-not $VMHost) {
         ValidateApiVersionSupported -server $activeServer -major 7 -minor 0
         $apiServer = GetApiServer($activeServer)
      }

      # Collect OBN for parameter 'VMHost'
      if($VMHost) {
         $resolvedVMHost = Resolve-ObjectByName -Object $VMHost `
            -Type ([VMHost]) `
            -CollectorCmdlet 'Get-VMHost' `
            -OneObjectExpected `
            -Server $activeServer

         $VMHost = [VMHost] $resolvedVMHost
      }

      if (![string]::IsNullOrEmpty($PemCertificate) -and $X509Certificate) {
         Write-PowerCLIError `
            -ErrorObject 'Only one of the parameters PemCertificate and X509Certificate must be supplied.' `
            -Terminating
      } elseif ([string]::IsNullOrEmpty($PemCertificate) -and $X509Certificate -eq $null) {
         Write-PowerCLIError `
            -ErrorObject 'One of the parameters PemCertificate and X509Certificate must be supplied.' `
            -Terminating
      }

      if (![string]::IsNullOrEmpty($PemKey) -and $Key) {
         Write-PowerCLIError `
            -ErrorObject 'Only one of the parameters PemKey and Key must be supplied.' `
            -Terminating
      }

      if ($Key -and ($PSVersionTable.PSVersion -lt ([Version]'7.1'))) {
         Write-PowerCLIError `
            -ErrorObject 'The Key parameter is available on PowerShell 7.1 and above.' `
            -Terminating
      }

      if ($X509Certificate) {
         $PemCertificate = $X509Certificate | ConvertTo-PemCertificate
      }

      if ($Key) {
         $PemKey = ConvertTo-Pem `
            -Bytes $key.ExportPkcs8PrivateKey() `
            -Type "PRIVATE KEY"
      }
   }

   Process {
      # Validate all objects are from the same server
      if($VMHost) {
         $VMHost | ValidateSameServer -ExpectedServer $activeServer
      }

      $vcName = ''
      $vmHostName = ''
      if ($VMHost) {
         $vmHostName = $VMHost.Name
      } else {
         $vcName = $activeServer.Name
      }


      $shouldProcessMessageSuccessfullyConstructed = $true
      try {
         $shouldProcessDescription = Get-SetVIMachineCertificateShouldProcessMessage $PemCertificate $vcName $vmHostName
         $shouldProcessWarning = Get-SetVIMachineCertificateShouldProcessMessage $PemCertificate $vcName $vmHostName -warning
      } catch {
         $shouldProcessMessageSuccessfullyConstructed = $false
         Write-PowerCLIError `
            -ErrorObject $_ `
            -ErrorId "PowerCLI_SetVIMachineCertificate_UnableToGenerateConfirmMessages"
      }

      if($shouldProcessMessageSuccessfullyConstructed -And $PSCmdlet.ShouldProcess(
         $shouldProcessDescription,
         $shouldProcessWarning,
         "Set certificate")) {

         if ($VMHost) {
            try {
               # hosts
               $certificateManager = Get-View $VMHost.ExtensionData.ConfigManager.CertificateManager -Server $activeServer
               $apiVersionSupported = IsApiVersionSupported -server $activeServer -major 8 -minor 0 -revision 3

               if (![string]::IsNullOrEmpty($PemKey)) {
                  if (!$apiVersionSupported) {
                     throw "Specifying the certificate private key (PemKey parameter) is not supported on versions of the server prior to 8.0.3"
                  }
                  $certificateManager.provisionServerPrivateKey($PemKey) | Out-Null
               }

               $certificateManager.installServerCertificate($PemCertificate) | Out-Null

               if ($apiVersionSupported) {
                  $certificateManager.notifyAffectedServices(@()) | Out-Null
               }

               $VMHost | Get-VIMachineCertificate -Server $activeServer | Write-Output
            } catch {
               Write-PowerCLIError `
                  -ErrorObject $_ `
                  -ErrorId "PowerCLI_SetVIMachineCertificate_UnableToSetESXiCertificate"
            }
         } else {
            try {
               # vc
               $initSplat = @{
                  Cert = $PemCertificate
               }

               if ($PemKey) {
                  $initSplat['Key'] = $PemKey
               }

               Initialize-CertificateManagementVcenterTlsSpec @initSplat | `
               Invoke-SetCertificateManagementTls -Server $apiServer -ErrorAction:Stop

               Get-VIMachineCertificate -VCenterOnly -Server $activeServer | Write-Output
            } catch {
               Write-PowerCLIError `
                  -ErrorObject $_ `
                  -ErrorId "PowerCLI_SetVIMachineCertificate_UnableToSetVcCertificate"
            }
         }
      }
   }
}

function Get-SetVIMachineCertificateShouldProcessMessage {
   param(
      [string]
      $pem,

      [string]
      $vcName,

      [string]
      $hostName,

      [switch]
      $warning
   )

   $sb = [System.Text.StringBuilder]::new()

   if ($warning.ToBool()) {
      $sb.Append("Are you sure you want to set ") | Out-Null
   } else {
      $sb.Append("Setting ") | Out-Null
   }

   $pem | ConvertTo-X509Certificate | % {
      $sb.Append("'") | Out-Null
      $sb.Append($_.GetNameInfo([X509NameType]::SimpleName, $false)) | Out-Null
      $sb.Append("'") | Out-Null
   }

   $sb.Append(" certificate") | Out-Null
   $sb.Append(" to") | Out-Null

   if(-not [string]::IsNullOrEmpty($vcName)) {
      $sb.Append(" vCenter Server '$vcName'") | Out-Null
   } elseif (-not [string]::IsNullOrEmpty($hostName)) {
      $sb.Append(" host '$hostName'") | Out-Null
   }

   if ($warning.ToBool()) {
      $sb.Append("?") | Out-Null
   } else {
      $sb.Append(".") | Out-Null
   }

   $restartRequired = -Not (IsApiVersionSupported -server $activeServer -major 8 -minor 0 -revision 3)
   if (-not [string]::IsNullOrEmpty($vcName) -and $restartRequired) {
      $sb.Append(" THIS WILL RESTART THE VCENTER SERVER!") | Out-Null
   }

   $sb.ToString() | Write-Output
}

# SIG # Begin signature block
# MIIphwYJKoZIhvcNAQcCoIIpeDCCKXQCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCGTTl29Yrv7XVP
# 5Obxpz/DUZ2AVBP0VZMxoXDvfYiDaqCCDnQwggawMIIEmKADAgECAhAIrUCyYNKc
# TJ9ezam9k67ZMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0z
# NjA0MjgyMzU5NTlaMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
# ggIKAoICAQDVtC9C0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0
# JAfhS0/TeEP0F9ce2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJr
# Q5qZ8sU7H/Lvy0daE6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhF
# LqGfLOEYwhrMxe6TSXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+F
# LEikVoQ11vkunKoAFdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh
# 3K3kGKDYwSNHR7OhD26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJ
# wZPt4bRc4G/rJvmM1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQay
# g9Rc9hUZTO1i4F4z8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbI
# YViY9XwCFjyDKK05huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchAp
# QfDVxW0mdmgRQRNYmtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRro
# OBl8ZhzNeDhFMJlP/2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IB
# WTCCAVUwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+
# YXsIiGX0TkIwHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0P
# AQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAk
# BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAC
# hjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9v
# dEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5j
# b20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAED
# MAgGBmeBDAEEATANBgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql
# +Eg08yy25nRm95RysQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFF
# UP2cvbaF4HZ+N3HLIvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1h
# mYFW9snjdufE5BtfQ/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3Ryw
# YFzzDaju4ImhvTnhOE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5Ubdld
# AhQfQDN8A+KVssIhdXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw
# 8MzK7/0pNVwfiThV9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnP
# LqR0kq3bPKSchh/jwVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatE
# QOON8BUozu3xGFYHKi8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bn
# KD+sEq6lLyJsQfmCXBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQji
# WQ1tygVQK+pKHJ6l/aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbq
# yK+p/pQd52MbOoZWeE4wgge8MIIFpKADAgECAhAGQAJb/wxIlzKZ1GMgg8N7MA0G
# CSqGSIb3DQEBCwUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwHhcNMjQwMzI4MDAwMDAwWhcNMjcwMzMw
# MjM1OTU5WjCBxDETMBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgEC
# EwhEZWxhd2FyZTEdMBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xEDAOBgNV
# BAUTBzY2MTAxMTcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMREw
# DwYDVQQHEwhTYW4gSm9zZTEVMBMGA1UEChMMQnJvYWRjb20gSW5jMRUwEwYDVQQD
# EwxCcm9hZGNvbSBJbmMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC6
# 8C+jJxKfxp9c1A6fghwSNjnvV+Y1my2XmTHEPmk7COFTWw1VRBZBtv/zUXrSStlW
# 9Qqqp48wk+H+H5oM0qL9jsUQ6wAJpmjt33F3xvKqc8ehzVZLXUEACQBXTo30Hnbh
# bcacKAyX9sGByGqKlR6cHHrEebSWMoxOO6TOgYg06xfeYzxpx8HqMQjOKgNEbh4c
# m1Yx+ER/4BmWmYcvVHR9KtXwJWxmzUJCaS5OB9lp8JqO5+uAHvpGtQEAb39/h/vy
# 290isCYw16/uaLOz5Epchv5fogTBTh4o6SXWHam03FfMNVbmbMVuciTzfPSDt0i5
# xQWYtVkwVU+NGE4XUutX4zbaing53QYKNAa+NW9FiWcaoEwE1qTvk7ilsucPzdqC
# ikQpHyaaGCzHoDhveSLiJKgKRiDqee8KXfa/hkCoat9AtCihyd899kJ1kSlTO9fk
# ci5/CdKwmwXQIKh6OPueKr+OJ69XPx0V+RaaMAkkVtFb7/VwecFmFgsXFkAK8ulP
# aGYyIFOFqLMqH8ZuKiLVP5HkxDgSitJcWbaSf89TJuxNqh0vV1k4iwaQpcmQZhPK
# 49pNlj5j0cyw8B+xTYNDwnKvyoWgqKAb2cfzc6pCLk2GJLBwakKZ5YKC50XdhPlS
# yrqlLTb5otZRoYWFGKL1SqxxrBjRqg4qp1RvuxRcBQIDAQABo4ICAjCCAf4wHwYD
# VR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYEFL9ZTMS/Phgv
# GnXZm3XOrmDoJSIfMD0GA1UdIAQ2MDQwMgYFZ4EMAQMwKTAnBggrBgEFBQcCARYb
# aHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMA4GA1UdDwEB/wQEAwIHgDATBgNV
# HSUEDDAKBggrBgEFBQcDAzCBtQYDVR0fBIGtMIGqMFOgUaBPhk1odHRwOi8vY3Js
# My5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQw
# OTZTSEEzODQyMDIxQ0ExLmNybDBToFGgT4ZNaHR0cDovL2NybDQuZGlnaWNlcnQu
# Y29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAy
# MUNBMS5jcmwwgZQGCCsGAQUFBwEBBIGHMIGEMCQGCCsGAQUFBzABhhhodHRwOi8v
# b2NzcC5kaWdpY2VydC5jb20wXAYIKwYBBQUHMAKGUGh0dHA6Ly9jYWNlcnRzLmRp
# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNI
# QTM4NDIwMjFDQTEuY3J0MAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggIBAMLk
# M/5BZB1j2xjjR9IkyDYtLrqS9IztErzl4ogf2dXuAgBJP1EKm/EN4oOi/BHxLW0T
# VuclGZ3aEa3/n3UlHxqmvKm+4elD5oh4sEOm7+J7ECV0HobeZhRiLmdyla1Mqbxw
# xp56AVvhG/++nf+AVzw95c671a3kb+6VXMDzZK1qcUh6zyLklZ/vfjKipry9KgMR
# sXd81I+PRr+99iRI+5pUsF7ixmS4vTldNh2r/VRFKLtXtTefZ20Q55Efu/8NefJf
# fD/+LLmHszB3LRlguFYOUGon7q8eQKi20PMW6PQb8az4mn6MfA1Y2x+L7HFDiy//
# VMOy5DqcKOHWqz/NZr7/VEPtywoLbHlUwNJyFY0wnQhzvPCg67YpDEYERDnggtpe
# OchKlJoqLKQInq6xDLTtco+ynR6IHnWRcz+oYhm6TdvRcmP7giiqObKEFXQwLsjS
# HsqRluk2FQ4DhG63+8gyD4rKQVoybP0obE7Gi2RYHOT0qwPos6GklZraht/capa7
# wos1gNKXJL7G6BaCXQWCyakT76ZOJOdHzHjJ2n+yBgKWWGBv186nrP5nfuRu0vJE
# HG74cy5oRwe3vme6ztPQy9VSgSrP+g67bZ6yPe+z09T7NczW4aXrXbMUdTb02zKQ
# sK8WEKfcvzBmZ115SwMuf3g34C5yRhOsWInfosmAMYIaaTCCGmUCAQEwfTBpMQsw
# CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERp
# Z2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIw
# MjEgQ0ExAhAGQAJb/wxIlzKZ1GMgg8N7MA0GCWCGSAFlAwQCAQUAoHwwEAYKKwYB
# BAGCNwIBDDECMAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGC
# NwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIDPQabAMFb5Air2f
# JwdAGx7F+SP1NxzSwP4qBc/+j52xMA0GCSqGSIb3DQEBAQUABIICAGuzVg6hDfjM
# 2s2XOzgrg6RZ9Ysu1MmDLIbaAilJRaQP8Oho58d8tLCkXVTMcpMv7Hv4Uo9DW0gv
# h3fJtjFpMwQq1ChXJpim4nxoFcdYzaL0+F3I1KpBhO9awhYs16JH+95kFDsH7CX8
# aizHjWXv/LgirPRrmNJamqwiDM4wFgadwcQKPcrnoB6pDG+gPJSl8ypP/VsXROFo
# i77YW52sL0ZIaE0w5yckD53lPeFNh3++XpofO/ip83dZBKNCiK1Xa4G6032qrWix
# I1HPsU5NyY4ZbiyDEECZ5Rx0ArYSBS85Pty47a00m9VbCg+45TQGib1XkEwWCLLU
# nkRctc4MlTZxebty7FswABO/Eg/XLpvEw6iodMG+p10U0Hnz3HkIt2sp+QWLgEbP
# JA+G5Vql7j5mFVqbGSh+LOyO34kRSHkndp1Nf+u0kLGeGdOjPlOzN6Wf/2PLaJvc
# wS6dm1kU/uECXvHHfq0nNVhRI2w1+W57rlBJ6lF07k0JoczspglmJJOQ2HtMzFsu
# aU6+cr8qczCLKa1xokCw00WSct2u7UIeVMYKYc2mgE1vYFX4vXQERdRPDgrvVvif
# 6D1KmEIbqEtiotfHRVRg1yD+JO1SUVgSMNkDZM8Z9GyeU3gnljHnw0HwQ0shkTEe
# 1iv6CZpknNz2jLWCtqNJzNpxr++EGcksoYIXPzCCFzsGCisGAQQBgjcDAwExghcr
# MIIXJwYJKoZIhvcNAQcCoIIXGDCCFxQCAQMxDzANBglghkgBZQMEAgEFADB3Bgsq
# hkiG9w0BCRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAE
# IDW747a2RGF6GJEMAG4hv8G0bdKsph2S8yADc3CVHQPDAhA3QPZYOKhg3SyNJtSa
# uiiWGA8yMDI0MDcyNDE5NDkzMFqgghMJMIIGwjCCBKqgAwIBAgIQBUSv85SdCDmm
# v9s/X+VhFjANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMO
# RGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNB
# NDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTIzMDcxNDAwMDAwMFoXDTM0
# MTAxMzIzNTk1OVowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
# bmMuMSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMzCCAiIwDQYJKoZI
# hvcNAQEBBQADggIPADCCAgoCggIBAKNTRYcdg45brD5UsyPgz5/X5dLnXaEOCdwv
# SKOXejsqnGfcYhVYwamTEafNqrJq3RApih5iY2nTWJw1cb86l+uUUI8cIOrHmjsv
# lmbjaedp/lvD1isgHMGXlLSlUIHyz8sHpjBoyoNC2vx/CSSUpIIa2mq62DvKXd4Z
# GIX7ReoNYWyd/nFexAaaPPDFLnkPG2ZS48jWPl/aQ9OE9dDH9kgtXkV1lnX+3RCh
# G4PBuOZSlbVH13gpOWvgeFmX40QrStWVzu8IF+qCZE3/I+PKhu60pCFkcOvV5aDa
# Y7Mu6QXuqvYk9R28mxyyt1/f8O52fTGZZUdVnUokL6wrl76f5P17cz4y7lI0+9S7
# 69SgLDSb495uZBkHNwGRDxy1Uc2qTGaDiGhiu7xBG3gZbeTZD+BYQfvYsSzhUa+0
# rRUGFOpiCBPTaR58ZE2dD9/O0V6MqqtQFcmzyrzXxDtoRKOlO0L9c33u3Qr/eTQQ
# fqZcClhMAD6FaXXHg2TWdc2PEnZWpST618RrIbroHzSYLzrqawGw9/sqhux7Ujip
# mAmhcbJsca8+uG+W1eEQE/5hRwqM/vC2x9XH3mwk8L9CgsqgcT2ckpMEtGlwJw1P
# t7U20clfCKRwo+wK8REuZODLIivK8SgTIUlRfgZm0zu++uuRONhRB8qUt+JQofM6
# 04qDy0B7AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIw
# ADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjAL
# BglghkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYD
# VR0OBBYEFKW27xPn783QZKHVVqllMaPe1eNJMFoGA1UdHwRTMFEwT6BNoEuGSWh0
# dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZT
# SEEyNTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsG
# AQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0
# dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQw
# OTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAIEa
# 1t6gqbWYF7xwjU+KPGic2CX/yyzkzepdIpLsjCICqbjPgKjZ5+PF7SaCinEvGN1O
# tt5s1+FgnCvt7T1IjrhrunxdvcJhN2hJd6PrkKoS1yeF844ektrCQDifXcigLiV4
# JZ0qBXqEKZi2V3mP2yZWK7Dzp703DNiYdk9WuVLCtp04qYHnbUFcjGnRuSvExnvP
# nPp44pMadqJpddNQ5EQSviANnqlE0PjlSXcIWiHFtM+YlRpUurm8wWkZus8W8oM3
# NG6wQSbd3lqXTzON1I13fXVFoaVYJmoDRd7ZULVQjK9WvUzF4UbFKNOt50MAcN7M
# mJ4ZiQPq1JE3701S88lgIcRWR+3aEUuMMsOI5ljitts++V+wQtaP4xeR0arAVeOG
# v6wnLEHQmjNKqDbUuXKWfpd5OEhfysLcPTLfddY2Z1qJ+Panx+VPNTwAvb6cKmx5
# AdzaROY63jg7B145WPR8czFVoIARyxQMfq68/qTreWWqaNYiyjvrmoI1VygWy2ny
# Mpqy0tg6uLFGhmu6F/3Ed2wVbK6rr3M66ElGt9V/zLY4wNjsHPW2obhDLN9OTH0e
# aHDAdwrUAuBcYLso/zjlUlrWrBciI0707NMX+1Br/wd3H3GXREHJuEbTbDJ8WC9n
# R2XlG3O2mflrLAZG70Ee8PBf4NvZrZCARK+AEEGKMIIGrjCCBJagAwIBAgIQBzY3
# tyRUfNhHrP0oZipeWzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMG
# A1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEw
# HwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAw
# WhcNMzcwMzIyMjM1OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
# SEEyNTYgVGltZVN0YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
# CgKCAgEAxoY1BkmzwT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQV
# Ql+kiPNo+n3znIkLf50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY
# 3+vaPcQXf6sZKz5C3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB7
# 20RBidx8ald68Dd5n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71
# ORJn7w6lY2zkpsUdzTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW
# 8LAxE6lXKZYnLvWHpo9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7
# W+yB3iIU2YIqx5K/oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qq
# lnNCaJ+2RrOdOqPVA+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWc
# ZklSUPRR8zZJTYsg0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI
# /rOP015LdhJRk8mMDDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27Ktd
# RnXiYKNYCQEoAA6EVO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0w
# ggFZMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WM
# aiCPnshvMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB
# /wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYI
# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1
# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RH
# NC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29t
# L0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIw
# CwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbY
# IULhsBguEE0TzzBTzr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6
# hCAlNDFnzbYSlm/EUExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6
# q4XNQ1/tYLaqT5Fmniye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/
# KFSJ8NWKcXZl2szwcqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/E
# jaDnmPv7pp1yr8THwcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNT
# rDsdCEkPlM05et3/JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRx
# ktcma+Q4c6umAU+9Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7
# K9h+8kaddSweJywm228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrd
# VcA6KYawmKAr7ZVBtzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C
# +dAjfwAL5HYCJtnwZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QV
# SucTDh3bNzgaoSv27dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFow
# DQYJKoZIhvcNAQEMBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0
# IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNl
# cnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIz
# NTk1OVowYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcG
# A1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3Rl
# ZCBSb290IEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2je
# u+RdSjwwIjBpM+zCpyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bG
# l20dq7J58soR0uRf1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBE
# EC7fgvMHhOZ0O21x4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/N
# rDRAX7F6Zu53yEioZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A
# 2raRmECQecN4x7axxLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8
# IUzUvK4bA3VdeGbZOjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfB
# aYh2mHY9WV1CdoeJl2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaa
# RBkrfsCUtNJhbesz2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZi
# fvaAsPvoZKYz0YkH4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXe
# eqxfjT/JvNNBERJb5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g
# /KEexcCPorF+CiaZ9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB
# /wQFMAMBAf8wHQYDVR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQY
# MBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEF
# BQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBD
# BggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0
# QXNzdXJlZElEUm9vdENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3Js
# My5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1Ud
# IAQKMAgwBgYEVR0gADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22
# Ftf3v1cHvZqsoYcs7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih
# 9/Jy3iS8UgPITtAq3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYD
# E3cnRNTnf+hZqPC/Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c
# 2PR3WlxUjG/voVA9/HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88n
# q2x2zm8jLfR+cWojayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5
# lDGCA3YwggNyAgEBMHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0
# LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hB
# MjU2IFRpbWVTdGFtcGluZyBDQQIQBUSv85SdCDmmv9s/X+VhFjANBglghkgBZQME
# AgEFAKCB0TAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkF
# MQ8XDTI0MDcyNDE5NDkzMFowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQUZvArMsLC
# yQ+CXc6qisnGTxmcz0AwLwYJKoZIhvcNAQkEMSIEIOKeixsyv/rFuf2vKh5bYePi
# eIx13hAakL295w6SL9F9MDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEINL25G3tdCLM
# 0dRAV2hBNm+CitpVmq4zFq9NGprUDHgoMA0GCSqGSIb3DQEBAQUABIICACZKLx6o
# 3UB63SKj3qHNOqUCW6xqTyHHKrEGlk2ABPssO2xuq1PkA7yB9QHa7RqN9KGD76yY
# 9atJCEvD7nZntYDEYrtqHGMEkCO4fY4Ty8WkZTfXwd5ohjHCteV85zb2VXdqL4Ls
# M6S7ge9dW6DXiCpDLB1SHbv/AKRL2qaZh/wc8weR4qkhomkHSB7OKZGMDXumAxFR
# 1MqcYWEjUEsu/D3TotMISp7/Y90g1IfYOwFj1c47V1y+0lm3xPZxMlBSsAbzdSrM
# AV4oXAFWkwADVXM0dkOIMjxffzNdIRQYYl2Zh2RrbspdH7hfUBx/sQ5MMxqu61T2
# sx9xNmTwaVdrbwrF/scf5yXX5jPrMki42aS39rVufy0JrqMmM8YNjEyt4VO4J5da
# rBxxY+lYTm6v4pjF7fvJda4zXQcIplbELXlzKDJcd/4WWyUOKuu4OZ5t6UIpkwa2
# NG7g5U96sVU/uAZpcfzcAjSt1eEnl+qISWGtV+1iP1UGjOjGkaAAIap+3VBj5U14
# dkcglQoAIPCh6KCKDPuFni2kSF79p60KnAWa/w5NUJ5dJ3Aazs7Wo3Nv61ySFf57
# My0K821Usmb7D+DmgCLfCpwQlR6fhgwOpJZNwsjS+Fo/27AWvUI9ECo2qa+dzkQm
# ydFGMkQHcXB1pW6OxZk6myj+taLvnc+lCvSK
# SIG # End signature block