Public/Connectors.ps1

$ModuleVersion = $myInvocation.MyCommand.Version

<#
.SYNOPSIS
    Retrieves existing SEPPmail Exchange Online connectors.
 
.DESCRIPTION
    Gets information about the SEPPmail inbound and outbound connectors in Exchange Online.
    SEPPmail uses 2 connectors to transfer messages between the SEPPmail Appliance and Exchange Online.
    This cmdlet displays the current configuration and status of these connectors.
 
.PARAMETER IncludeTestModeConnectors
    Includes connectors that are in test mode in the output.
    Test mode connectors are typically used for testing configurations before going live.
 
.EXAMPLE
    Get-SM365Connectors
     
    Retrieves and displays the SEPPmail inbound and outbound connectors.
 
.EXAMPLE
    Get-SM365Connectors -IncludeTestModeConnectors
     
    Retrieves SEPPmail connectors including those in test mode.
 
.OUTPUTS
    System.Object
    Returns connector objects with properties: Name, Enabled, WhenCreated, SmartHosts (outbound), TlsSenderCertificateName (inbound)
 
.NOTES
    Requires an active Exchange Online PowerShell session.
 
.LINK
    https://docs.seppmail.com/ch-en/04_com_powershell.html
#>

function Get-SM365Connectors
{
    [CmdletBinding()]
    Param
    (
    [Switch]$IncludeTestModeConnectors
    )

    if (!(Test-SM365ConnectionStatus))
    { 
        throw [System.Exception] "You're not connected to Exchange Online - please connect prior to using this CmdLet"
    }
    else {
        Write-Information "Connected to Exchange Organization `"$Script:ExODefaultDomain`"" -InformationAction Continue

        $inbound = Get-SM365InboundConnectorSettings
        $outbound = Get-SM365OutboundConnectorSettings

        if ($IncludeTestModeConnectors) {
            Get-OutboundConnector -IncludeTestModeConnectors:$true |where-object {$_.TestMode -eq $true}|select-object Name,Enabled,WhenCreated,SmartHosts
        }

        if (Get-OutboundConnector -outVariable obc | Where-Object Identity -eq $($outbound.Name))
        {
            $obc | Where-Object Identity -eq $($outbound.Name) |select-object Name,Enabled,WhenCreated,SmartHosts
        }
        else {
            Write-Warning "No SEPPmail Outbound Connector with name `"$($outbound.Name)`" found"
        }

        if (Get-InboundConnector -outVariable ibc | Where-Object Identity -eq $($inbound.Name))
        {
            $ibc|select-object Name,Enabled,WhenCreated,TlsSenderCertificateName
        }
        else 
        {
            Write-Warning "No SEPPmail Inbound Connector with Name `"$($inbound.Name)`" found"
        }
    }
}

<#
.SYNOPSIS
    Creates SEPPmail Exchange Online connectors for inbound and outbound mail flow.
 
.DESCRIPTION
    SEPPmail uses 2 connectors to transfer messages between SEPPmail Appliance and Exchange Online.
    This cmdlet creates both the inbound and outbound connectors with the specified configuration.
     
    The cmdlet will:
    - Create an outbound connector to route mails from Exchange Online to the SEPPmail Appliance
    - Create an inbound connector to receive mails from the SEPPmail Appliance
    - Configure TLS certificate validation for secure communication
    - Optionally add the SEPPmail IP to the anti-spam allow list
    - Support Certificate Based Connectors (CBC) for MSP setups
     
    The cmdlet checks for existing SEPPmail connectors and hybrid configurations before creating new ones.
 
.PARAMETER SEPPmailFQDN
    The fully qualified domain name (FQDN) of the SEPPmail Appliance.
    This is used as the SmartHost for the outbound connector.
    Example: 'securemail.contoso.com'
     
    Aliases: FQDN, SMFQDN, SmartHost
 
.PARAMETER TLSCertificateName
    The name of the TLS certificate used by the SEPPmail Appliance.
    This can be different from the FQDN if using a wildcard certificate.
    Read the certificate name in your SEPPmail under SSL => Issued to => Name (CN).
    Example: '*.contoso.com' or 'securemail.contoso.com'
     
    Aliases: TLSCertName, CertName
 
.PARAMETER CBCcertName
    The certificate name for the inbound connector in Certificate Based Connector (CBC) setups.
    Required for MSP environments where inbound and outbound connectors use different certificates.
    When specified, the outbound connector uses TLSCertificateName, and the inbound connector uses CBCcertName.
    Example: 'inbound.contoso.com'
     
    Aliases: SenderCertificateName
 
.PARAMETER Disabled
    Creates the connectors in a disabled state.
    Use this if you want to review the configuration before enabling the connectors.
 
.EXAMPLE
    New-SM365Connectors -SEPPmailFQDN 'securemail.contoso.com' -TLSCertificateName 'securemail.contoso.com'
     
    Creates inbound and outbound connectors using the same certificate name as the FQDN.
 
.EXAMPLE
    New-SM365Connectors -SEPPmailFQDN 'securemail.contoso.com' -TLSCertificateName '*.contoso.com'
     
    Creates connectors using a wildcard certificate for TLS validation.
 
.EXAMPLE
    New-SM365Connectors -SEPPmailFQDN 'securemail.contoso.com' -TLSCertificateName '*.contoso.com' -CBCcertName 'inbound.contoso.com'
     
    Creates connectors for an MSP setup with Certificate Based Connectors (CBC).
    The outbound connector uses the wildcard certificate '*.contoso.com'.
    The inbound connector uses the specific certificate 'inbound.contoso.com'.
    See https://docs.seppmail.com/de/09_ht_mso365_ssl_certificate.html?q=CBC for details on CBC setup.
 
.EXAMPLE
    New-SM365Connectors -SEPPmailFQDN 'securemail.contoso.com' -TLSCertificateName '*.contoso.com' -Disabled
     
    Creates the connectors but leaves them disabled for review before activation.
 
.NOTES
    - Requires an active Exchange Online PowerShell session
    - Will prompt for confirmation in hybrid environments
    - Automatically detects and handles existing SEPPmail connectors
    - Adds SEPPmail IP to anti-spam allow list by default
 
.LINK
    https://docs.seppmail.com/ch-en/04_com_powershell.html
#>

function New-SM365Connectors
{
    [CmdletBinding(
         SupportsShouldProcess = $true,
         ConfirmImpact = 'Medium',
         DefaultParameterSetName = 'FqdnTls'
     )]

    param
    (
        [Parameter(
            Mandatory = $true,
            HelpMessage = 'FQDN of the SEPPmail Appliance, i.e. securemail.contoso.com',
            Position = 0
        )]
        [ValidatePattern("^(?!:\/\/)(?=.{1,255}$)((.{1,63}\.){1,127}(?![0-9]*$)[a-z0-9-]+\.?)$")]
        [Alias('FQDN','SMFQDN','SmartHost')]
        [String] $SEPPmailFQDN,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Name of the certificate if different from the SEPPmail-FQDN. Read the certificate name in your SEPPmail under SSL==>Issued to==>Name (CN)',
            Position = 1
        )]
        [Alias('TLSCertName','CertName')]
        [String] $TLSCertificateName,

        [Parameter(
            Mandatory = $false,
            HelpMessage = 'MSP setup requires a second certificate',
            ParameterSetName = 'FqdnTls',
            Position = 2
        )]
        [Alias('SenderCertificateName')]
        [string]$CBCcertName,

        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Disable the connectors on creation'
         )]
        [switch]$Disabled

        #endregion
    )

    begin
    {
        if(!(Test-SM365ConnectionStatus))
        {throw [System.Exception] "You're not connected to Exchange Online - please connect prior to using this CmdLet"}
        Write-Information "Connected to Exchange Organization `"$Script:ExODefaultDomain`"" -InformationAction Continue

        #region collecting existing connectors
        Write-Verbose "Collecting existing connectors"
        $allInboundConnectors = Get-InboundConnector
        $allOutboundConnectors = Get-OutboundConnector

        Write-Verbose "Testing for hybrid Setup"
        $HybridInboundConn = $allInboundConnectors |Where-Object {(($_.Name -clike 'Inbound from *') -or ($_.ConnectorSource -clike 'HybridWizard'))}
        $HybridOutBoundConn = $allOutboundConnectors |Where-Object {(($_.Name -clike 'Outbound to *') -or ($_.ConnectorSource -clike 'HybridWizard'))}
        #endregion collecting existing connectors

        #region warn on hybrid
        if ($HybridInboundConn -or $HybridOutBoundConn)
        {
            Write-Warning "!!! - Hybrid Configuration detected - we assume you know what you are doing. Be sure to backup your connector settings before making any change."

            if($InteractiveSession)
            {
                Write-Verbose "Ask user to continue if Hybrid is found."
                Do {
                    try {
                        [ValidateSet('y', 'Y', 'n', 'N')]$hybridContinue = Read-Host -Prompt "Create SEPPmail connectors in hybrid environment ? (Y/N)"
                    }
                    catch {}
                }
                until ($?)
                if ($hybridContinue -eq 'n') {
                    Write-Verbose "Exiting due to user decision."
                    break
                }
            }
            else
            {
                # should we error out here, since connector creation might be dangerous?
            }
        } else {
            Write-Information "No Hybrid Connectors detected, seems to be a clean cloud-only environment" -InformationAction Continue
        }
        #endregion warn on hybrid
    }

    process
    {
        #region - Check existing Outbound Connector
        Write-Verbose "Read existing SEPPmail outbound connector"
        $existingSMOutboundConn = $allOutboundConnectors | Where-Object Name -like '`[SEPPmail`]*'
        # only $false if the user says so interactively
        
        [bool]$createOutBound = $true #Set Default Value
        if ($existingSMOutboundConn)
        {
            Write-Warning "Found existing SEPPmail outbound connector with name: `"$($existingSMOutboundConn.Name)`" created on `"$($existingSMOutboundConn.WhenCreated)`" pointing to SEPPmail `"$($existingSMOutboundConn.TlsDomain)`" "

            if($InteractiveSession)
            {
                [string] $tmp = $null

                Do {
                    try {
                        [ValidateSet('y', 'Y', 'n', 'N')]$tmp = Read-Host -Prompt "Shall we delete and recreate the outbound connector (will only work if no rules use it)? (Y/N)"
                        break
                    }
                    catch {}
                }
                until ($?)

                if ($tmp -eq 'y') {
                    $createOutbound = $true

                    Write-Verbose "Removing existing Outbound Connector $($existingSMOutboundConn.Name) !"
                    if ($PSCmdLet.ShouldProcess($($existingSMOutboundConn.Name), 'Removing existing SEPPmail Outbound Connector')) {
                        $existingSMOutboundConn | Remove-OutboundConnector -Confirm:$false # user already confirmed action

                        if (!$?)
                        { throw $error[0] }
                    }
                }
                else {
                    Write-Warning "Leaving existing SEPPmail outbound connector `"$($existingSMOutboundConn.Name)`" untouched."
                    $createOutbound = $false
                }
            }
            else
            {
                throw [System.Exception] "Outbound connector $($outbound.Name) already exists"
            }
        }
        else
        {Write-Verbose "No existing Outbound Connector found"}
        #endregion - Check existing Outbound Connector

        #region - Create Outbound Connector
        $outboundParam = Get-SM365OutboundConnectorSettings
        Write-verbose "if -disabled switch is used, the connector stays deactivated"
        if ($Disabled) {
            $outboundParam.Enabled = $false
        }

        Write-Verbose "FQDN and TLS, using $SEPPmailFQDN as SmartHost and $TLSCertificate as TlsDoman"
        $outboundParam.SmartHosts = $SEPPmailFQDN
        $outboundParam.TlsDomain = $TLSCertificateName

        if($createOutbound)
        {
            Write-Verbose "Creating SEPPmail Outbound Connector $($outboundParam.Name)!"
            if ($PSCmdLet.ShouldProcess($($outboundParam.Name), 'Creating Outbound Connector'))
            {
                Write-Verbose "Adding creation comment to outbound connector"
                $Now = Get-Date
                $outboundParam.Comment += "`n#Created with SEPPmail365 PowerShell Module version $ModuleVersion on $now"

                Write-Debug "Outbound Connector settings:"
                $outboundParam.GetEnumerator() | ForEach-Object{
                    Write-Debug "$($_.Key) = $($_.Value)"
                }

                [void](New-OutboundConnector @outboundParam)

                if(!$?)
                {throw $error[0]}
            }
        }
        #endregion - Create Outbound Connector

        #region - Check existing inbound connector
        Write-Verbose "Read existing SEPPmail Inbound Connector from Exchange Online"
        $existingSMInboundConn = $allInboundConnectors | Where-Object Name -like '`[SEPPmail`]*'

        # only $false if the user says so interactively
        [bool]$createInbound = $true
        if ($existingSMInboundConn)
        {
            Write-Warning "Found existing SEPPmail inbound Connector with name: `"$($existingSMInboundConn.Name)`", created `"$($existingSMInboundConn.WhenCreated)`" incoming SEPPmail is `"$($existingSMInboundConn.TlsSenderCertificateName)`""

            if($InteractiveSession)
            {
                [string] $tmp = $null
                Do {
                    try {
                        [ValidateSet('y', 'Y', 'n', 'N')]$tmp = Read-Host -Prompt "Shall we delete and recreate the inbound connector (will only work if no rules use it)? (Y/N)"
                        break
                    }
                    catch {}
                }
                until ($?)

                if ($tmp -eq 'y') {
                    $createInbound = $true

                    Write-Verbose "Removing existing SEPPmail Inbound Connector $($existingSMInboundConn.Name) !"
                    if ($PSCmdLet.ShouldProcess($($existingSMInboundConn.Name), 'Removing existing SEPPmail inbound Connector')) {
                        $existingSMInboundConn | Remove-InboundConnector -Confirm:$false # user already confirmed action

                        if (!$?)
                        { throw $error[0] }
                    }
                }
                else {
                    Write-Warning "Leaving existing SEPPmail Inbound Connector `"$($existingSMInboundConn.Name)`" untouched."
                    $createInbound = $false
                }
            }
            else
            {
                throw [System.Exception] "Inbound connector $($inboundParam.Name) already exists"
            }
        }
        else
        {Write-Verbose "No existing Inbound Connector found"}
        #endregion - Check existing inbound connector

        #region - Create Inbound Connector
        Write-Verbose "Read Inbound Connector Settings"
        $inboundParam = Get-SM365InboundConnectorSettings
        
        Write-verbose "if -disabled switch is used, the connector stays deactivated"
        if ($disabled) {
            $inboundParam.Enabled = $false
        }

        if($createInbound)
        {
            Write-Verbose "Modify params based on Parameters given"
            # Due to ARC Setup of Exo tenants and EFSkipLastIP is $true by default, EFSKipIP´s must be empty in certain setups.

            # Configure inbound connector parameters based CBCCert given yes or no.
            if ($CBCcertName) {
                Write-Verbose "FQDN and CBC CertificateName, using $CBCcertName as TLSCertificateName"
                $inboundParam.TlsSenderCertificateName = $CBCcertName
            } else {
                Write-Verbose "FQDN and CertificateName specified, using $TLSCertificateName as TLSCertificateName"
                $inboundParam.TlsSenderCertificateName = $TLSCertificateName
            }

            Write-Verbose "Creating SEPPmail Inbound Connector $($inboundParam.Name)!"
            if ($PSCmdLet.ShouldProcess($($inboundParam.Name), 'Creating Inbound Connector'))
            {
                Write-Verbose "Adding creation comment to inbound connector"
                $Now = Get-Date
                $ModuleVersion = $myInvocation.MyCommand.Version
                $inboundParam.Comment += "`n#Created with SEPPmail365 PowerShell Module version $ModuleVersion on $now"

                Write-Debug "Inbound Connector settings:"
                $inboundParam.GetEnumerator() | Foreach-Object {
                    Write-Debug "$($_.Key) = $($_.Value)"
                }
                [void](New-InboundConnector @inboundParam)

                if(!$?) {
                    throw $error[0]
                } else {
                }
            }
        }
        #endRegion - Create InboundConnector
    }

    end
    {
    }
}

<#
.SYNOPSIS
    Removes the SEPPmail inbound and outbound connectors from Exchange Online.
 
.DESCRIPTION
    Removes both SEPPmail connectors (inbound and outbound) from Exchange Online.
    By default, this cmdlet also removes the SEPPmail Appliance IP address from the
    anti-spam allow list (Hosted Connection Filter Policy).
     
    The cmdlet will:
    - Remove the SEPPmail outbound connector
    - Remove the SEPPmail inbound connector
    - Remove the SEPPmail IP from the Hosted Connection Filter Policy IPAllowList (unless -leaveAntiSpamWhiteList is specified)
 
.PARAMETER leaveAntiSpamWhiteList
    When specified, the SEPPmail Appliance IP address will remain in the anti-spam allow list
    even after the connectors are removed. Use this if you plan to recreate the connectors
    or want to keep the IP address whitelisted for other reasons.
 
.EXAMPLE
    Remove-SM365Connectors
     
    Removes the SEPPmail connectors and removes the SEPPmail IP from the anti-spam allow list.
 
.EXAMPLE
    Remove-SM365Connectors -leaveAntiSpamWhiteList
     
    Removes the connectors but keeps the SEPPmail Appliance IP address in the anti-spam allow list.
 
.EXAMPLE
    Remove-SM365Connectors -WhatIf
     
    Shows what would happen if the cmdlet runs without actually removing the connectors.
 
.NOTES
    - Requires an active Exchange Online PowerShell session
    - Supports -WhatIf and -Confirm parameters
    - The SEPPmail IP is determined from the inbound connector's SenderIPAddresses or TlsSenderCertificateName
 
.LINK
    https://docs.seppmail.com/ch-en/04_com_powershell.html
#>

function Remove-SM365Connectors
{
    [CmdletBinding(SupportsShouldProcess=$true,
                   ConfirmImpact='Medium')]
    Param
    (
    [Switch]$leaveAntiSpamWhiteList
    )

    if (!(Test-SM365ConnectionStatus))
    { throw [System.Exception] "You're not connected to Exchange Online - please connect prior to using this CmdLet" }

    Write-Information "Connected to Exchange Organization `"$Script:ExODefaultDomain`"" -InformationAction Continue

    $inbound = Get-SM365InboundConnectorSettings
    $outbound = Get-SM365OutboundConnectorSettings
    $hcfp = Get-HostedConnectionFilterPolicy

    if($PSCmdlet.ShouldProcess($outbound.Name, "Remove SEPPmail outbound connector $($Outbound.Name)"))
    {
        if (Get-OutboundConnector | Where-Object Identity -eq $($outbound.Name))
        {
            Remove-OutboundConnector $outbound.Name
        }
        else {
            Write-Warning 'No SEPPmail Outbound Connector found'
        }
    }

    if($PSCmdlet.ShouldProcess($inbound.Name, "Remove SEPPmail inbound connector $($inbound.Name)"))
    {
        $InboundConnector = Get-InboundConnector | Where-Object Identity -eq $($inbound.Name)
        if ($inboundConnector)
            {
            Write-Verbose 'Collect Inbound Connector IP for later WhiteListRemoval'
            
            [string]$InboundSEPPmailIP = $null
            if ($inboundConnector.SenderIPAddresses.count -le 1) {
                $InboundSEPPmailIP = $InboundConnector.SenderIPAddresses[0]
            } 
            if ($inboundConnector.TlsSenderCertificateName) {
                try {
                    $InboundSEPPmailIP = ([System.Net.Dns]::GetHostAddresses($($inboundConnector.TlsSenderCertificateName)).IPAddressToString)
                }
                catch {
                    $InboundSEPPmailIP = $null
                }
            }
            Remove-InboundConnector $inbound.Name

            Write-Verbose "If Inbound Connector has been removed, remove also Whitelisted IPs"
            if ((!($leaveAntiSpamWhiteList)) -and (!(Get-InboundConnector | Where-Object Identity -eq $($inbound.Name))) -and ($InboundSEPPmailIP))
            {
                    Write-Verbose "Remove SEPPmail Appliance IP from Whitelist in 'Hosted Connection Filter Policy'"
                    
                    Write-Verbose "Collecting existing WhiteList"
                    [System.Collections.ArrayList]$existingAllowList = $hcfp.IPAllowList
                    Write-verbose "Removing SEPPmail Appliance IP $InboundSEPPmailIP from Policy $($hcfp.Id)"
                    if ($existingAllowList) {
                        $existingAllowList.Remove($InboundSEPPmailIP)
                        Set-HostedConnectionFilterPolicy -Identity $hcfp.Id -IPAllowList $existingAllowList
                        Write-Information "IP: $InboundSEPPmailIP removed from Hosted Connection Filter Policy $hcfp.Id"
                }
            }
        }
        else 
        {
            Write-Warning 'No SEPPmail Inbound Connector found'
        }
    }
}


if (!(Get-Alias 'Set-SM365Connectors' -ErrorAction SilentlyContinue)) {
    New-Alias -Name Set-SM365Connectors -Value New-SM365Connectors
}

# SIG # Begin signature block
# MIIVzAYJKoZIhvcNAQcCoIIVvTCCFbkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBSMo0bDFMnvk9w
# Fti8rgSvuPGzdVZuRBuTZXB3bSs5YaCCEggwggVvMIIEV6ADAgECAhBI/JO0YFWU
# jTanyYqJ1pQWMA0GCSqGSIb3DQEBDAUAMHsxCzAJBgNVBAYTAkdCMRswGQYDVQQI
# DBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoM
# EUNvbW9kbyBDQSBMaW1pdGVkMSEwHwYDVQQDDBhBQUEgQ2VydGlmaWNhdGUgU2Vy
# dmljZXMwHhcNMjEwNTI1MDAwMDAwWhcNMjgxMjMxMjM1OTU5WjBWMQswCQYDVQQG
# EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQDEyRTZWN0aWdv
# IFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEBAQUA
# A4ICDwAwggIKAoICAQCN55QSIgQkdC7/FiMCkoq2rjaFrEfUI5ErPtx94jGgUW+s
# hJHjUoq14pbe0IdjJImK/+8Skzt9u7aKvb0Ffyeba2XTpQxpsbxJOZrxbW6q5KCD
# J9qaDStQ6Utbs7hkNqR+Sj2pcaths3OzPAsM79szV+W+NDfjlxtd/R8SPYIDdub7
# P2bSlDFp+m2zNKzBenjcklDyZMeqLQSrw2rq4C+np9xu1+j/2iGrQL+57g2extme
# me/G3h+pDHazJyCh1rr9gOcB0u/rgimVcI3/uxXP/tEPNqIuTzKQdEZrRzUTdwUz
# T2MuuC3hv2WnBGsY2HH6zAjybYmZELGt2z4s5KoYsMYHAXVn3m3pY2MeNn9pib6q
# RT5uWl+PoVvLnTCGMOgDs0DGDQ84zWeoU4j6uDBl+m/H5x2xg3RpPqzEaDux5mcz
# mrYI4IAFSEDu9oJkRqj1c7AGlfJsZZ+/VVscnFcax3hGfHCqlBuCF6yH6bbJDoEc
# QNYWFyn8XJwYK+pF9e+91WdPKF4F7pBMeufG9ND8+s0+MkYTIDaKBOq3qgdGnA2T
# OglmmVhcKaO5DKYwODzQRjY1fJy67sPV+Qp2+n4FG0DKkjXp1XrRtX8ArqmQqsV/
# AZwQsRb8zG4Y3G9i/qZQp7h7uJ0VP/4gDHXIIloTlRmQAOka1cKG8eOO7F/05QID
# AQABo4IBEjCCAQ4wHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQwHQYD
# VR0OBBYEFDLrkpr/NZZILyhAQnAgNpFcF4XmMA4GA1UdDwEB/wQEAwIBhjAPBgNV
# HRMBAf8EBTADAQH/MBMGA1UdJQQMMAoGCCsGAQUFBwMDMBsGA1UdIAQUMBIwBgYE
# VR0gADAIBgZngQwBBAEwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21v
# ZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEE
# KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZI
# hvcNAQEMBQADggEBABK/oe+LdJqYRLhpRrWrJAoMpIpnuDqBv0WKfVIHqI0fTiGF
# OaNrXi0ghr8QuK55O1PNtPvYRL4G2VxjZ9RAFodEhnIq1jIV9RKDwvnhXRFAZ/ZC
# J3LFI+ICOBpMIOLbAffNRk8monxmwFE2tokCVMf8WPtsAO7+mKYulaEMUykfb9gZ
# pk+e96wJ6l2CxouvgKe9gUhShDHaMuwV5KZMPWw5c9QLhTkg4IUaaOGnSDip0TYl
# d8GNGRbFiExmfS9jzpjoad+sPKhdnckcW67Y8y90z7h+9teDnRGWYpquRRPaf9xH
# +9/DUp/mBlXpnYzyOmJRvOwkDynUWICE5EV7WtgwggYaMIIEAqADAgECAhBiHW0M
# UgGeO5B5FSCJIRwKMA0GCSqGSIb3DQEBDAUAMFYxCzAJBgNVBAYTAkdCMRgwFgYD
# VQQKEw9TZWN0aWdvIExpbWl0ZWQxLTArBgNVBAMTJFNlY3RpZ28gUHVibGljIENv
# ZGUgU2lnbmluZyBSb290IFI0NjAeFw0yMTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5
# NTlaMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzAp
# BgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYwggGiMA0G
# CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCbK51T+jU/jmAGQ2rAz/V/9shTUxjI
# ztNsfvxYB5UXeWUzCxEeAEZGbEN4QMgCsJLZUKhWThj/yPqy0iSZhXkZ6Pg2A2NV
# DgFigOMYzB2OKhdqfWGVoYW3haT29PSTahYkwmMv0b/83nbeECbiMXhSOtbam+/3
# 6F09fy1tsB8je/RV0mIk8XL/tfCK6cPuYHE215wzrK0h1SWHTxPbPuYkRdkP05Zw
# mRmTnAO5/arnY83jeNzhP06ShdnRqtZlV59+8yv+KIhE5ILMqgOZYAENHNX9SJDm
# +qxp4VqpB3MV/h53yl41aHU5pledi9lCBbH9JeIkNFICiVHNkRmq4TpxtwfvjsUe
# dyz8rNyfQJy/aOs5b4s+ac7IH60B+Ja7TVM+EKv1WuTGwcLmoU3FpOFMbmPj8pz4
# 4MPZ1f9+YEQIQty/NQd/2yGgW+ufflcZ/ZE9o1M7a5Jnqf2i2/uMSWymR8r2oQBM
# dlyh2n5HirY4jKnFH/9gRvd+QOfdRrJZb1sCAwEAAaOCAWQwggFgMB8GA1UdIwQY
# MBaAFDLrkpr/NZZILyhAQnAgNpFcF4XmMB0GA1UdDgQWBBQPKssghyi47G9IritU
# pimqF6TNDDAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADATBgNV
# HSUEDDAKBggrBgEFBQcDAzAbBgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEsG
# A1UdHwREMEIwQKA+oDyGOmh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1
# YmxpY0NvZGVTaWduaW5nUm9vdFI0Ni5jcmwwewYIKwYBBQUHAQEEbzBtMEYGCCsG
# AQUFBzAChjpodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2Rl
# U2lnbmluZ1Jvb3RSNDYucDdjMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0
# aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEABv+C4XdjNm57oRUgmxP/BP6YdURh
# w1aVcdGRP4Wh60BAscjW4HL9hcpkOTz5jUug2oeunbYAowbFC2AKK+cMcXIBD0Zd
# OaWTsyNyBBsMLHqafvIhrCymlaS98+QpoBCyKppP0OcxYEdU0hpsaqBBIZOtBajj
# cw5+w/KeFvPYfLF/ldYpmlG+vd0xqlqd099iChnyIMvY5HexjO2AmtsbpVn0OhNc
# WbWDRF/3sBp6fWXhz7DcML4iTAWS+MVXeNLj1lJziVKEoroGs9Mlizg0bUMbOalO
# hOfCipnx8CaLZeVme5yELg09Jlo8BMe80jO37PU8ejfkP9/uPak7VLwELKxAMcJs
# zkyeiaerlphwoKx1uHRzNyE6bxuSKcutisqmKL5OTunAvtONEoteSiabkPVSZ2z7
# 6mKnzAfZxCl/3dq3dUNw4rg3sTCggkHSRqTqlLMS7gjrhTqBmzu1L90Y1KWN/Y5J
# KdGvspbOrTfOXyXvmPL6E52z1NZJ6ctuMFBQZH3pwWvqURR8AgQdULUvrxjUYbHH
# j95Ejza63zdrEcxWLDX6xWls/GDnVNueKjWUH3fTv1Y8Wdho698YADR7TNx8X8z2
# Bev6SivBBOHY+uqiirZtg0y9ShQoPzmCcn63Syatatvx157YK9hlcPmVoa1oDE5/
# L9Uo2bC5a4CH2RwwggZzMIIE26ADAgECAhAMcJlHeeRMvJV4PjhvyrrbMA0GCSqG
# SIb3DQEBDAUAMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0
# ZWQxKzApBgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYw
# HhcNMjMwMzIwMDAwMDAwWhcNMjYwMzE5MjM1OTU5WjBqMQswCQYDVQQGEwJERTEP
# MA0GA1UECAwGQmF5ZXJuMSQwIgYDVQQKDBtTRVBQbWFpbCAtIERldXRzY2hsYW5k
# IEdtYkgxJDAiBgNVBAMMG1NFUFBtYWlsIC0gRGV1dHNjaGxhbmQgR21iSDCCAiIw
# DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOapobQkNYCMP+Y33JcGo90Soe9Y
# /WWojr4bKHbLNBzKqZ6cku2uCxhMF1Ln6xuI4ATdZvm4O7GqvplG9nF1ad5t2Lus
# 5SLs45AYnODP4aqPbPU/2NGDRpfnceF+XhKeiYBwoIwrPZ04b8bfTpckj/tvenB9
# P8/9hAjWK97xv7+qsIz4lMMaCuWZgi8RlP6XVxsb+jYrHGA1UdHZEpunEFLaO9Ss
# OPqatPAL2LNGs/JVuGdq9p47GKzn+vl+ANd5zZ/TIP1ifX76vorqZ9l9a5mzi/HG
# vq43v2Cj3jrzIQ7uTbxtiLlPQUqkRzPRtiwTV80JdtRE+M+gTf7bT1CTvG2L3scf
# YKFk7S80M7NydxV/qL+l8blGGageCzJ8svju2Mo4BB+ALWr+gBmCGqrM8YKy/wXR
# tbvdEvBOLsATcHX0maw9xRCDRle2jO+ndYkTKZ92AMH6a/WdDfL0HrAWloWWSg62
# TxmJ/QiX54ILQv2Tlh1Al+pjGHN2evxS8i+XoWcUdHPIOoQd37yjnMjCN593wDzj
# XCEuDABYw9BbvfSp29G/uiDGtjttDXzeMRdVCJFgULV9suBVP7yFh9pK/mVpz+aC
# L2PvqiGYR41xRBKqwrfJEdoluRsqDy6KD985EdXkTvdIFKv0B7MfbcBCiGUBcm1r
# fLAbs8Q2lqvqM4bxAgMBAAGjggGpMIIBpTAfBgNVHSMEGDAWgBQPKssghyi47G9I
# ritUpimqF6TNDDAdBgNVHQ4EFgQUL96+KAGrvUgJnXwdVnA/uy+RlEcwDgYDVR0P
# AQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwSgYD
# VR0gBEMwQTA1BgwrBgEEAbIxAQIBAwIwJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9z
# ZWN0aWdvLmNvbS9DUFMwCAYGZ4EMAQQBMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHA6
# Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY0NvZGVTaWduaW5nQ0FSMzYu
# Y3JsMHkGCCsGAQUFBwEBBG0wazBEBggrBgEFBQcwAoY4aHR0cDovL2NydC5zZWN0
# aWdvLmNvbS9TZWN0aWdvUHVibGljQ29kZVNpZ25pbmdDQVIzNi5jcnQwIwYIKwYB
# BQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29tMB4GA1UdEQQXMBWBE3N1cHBv
# cnRAc2VwcG1haWwuY2gwDQYJKoZIhvcNAQEMBQADggGBAHnWpS4Jw/QiiLQi2EYv
# THCtwKsj7O3G7wAN7wijSJcWF7iCx6AoCuCIgGdWiQuEZcv9pIUrXQ6jOSRHsDNX
# SvIhCK9JakZJSseW/SCb1rvxZ4d0n2jm2SdkWf5j7+W+X4JHeCF9ZOw0ULpe5pFs
# IGTh8bmTtUr3yA11yw4vHfXFwin7WbEoTLVKiL0ZUN0Qk+yBniPPSRRlUZIX8P4e
# iXuw7lh9CMaS3HWRKkK89w//18PjUMxhTZJ6dszN2TAfwu1zxdG/RQqvxXUTTAxU
# JrrCuvowtnDQ55yXMxkkSxWUwLxk76WvXwmohRdsavsGJJ9+yxj5JKOd+HIZ1fZ7
# oi0VhyOqFQAnjNbwR/TqPjRxZKjCNLXSM5YSMZKAhqrJssGLINZ2qDK/CEcVDkBS
# 6Hke4jWMczny8nB8+ATJ84MB7tfSoXE7R0FMs1dinuvjVWIyg6klHigpeEiAaSaG
# 5KF7vk+OlquA+x4ohPuWdtFxobOT2OgHQnK4bJitb9aDazGCAxowggMWAgEBMGgw
# VDELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDErMCkGA1UE
# AxMiU2VjdGlnbyBQdWJsaWMgQ29kZSBTaWduaW5nIENBIFIzNgIQDHCZR3nkTLyV
# eD44b8q62zANBglghkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgACh
# AoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAM
# BgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCDy8q3uzIhhv1NfJrPpqkcx21tA
# JvPMW908SQh52S3nbDANBgkqhkiG9w0BAQEFAASCAgBYRERiXjT24cudf3UpVbLl
# 2xJcPOGA+5fKPZdqVqPeYjTVz4ZS6T+u/+vReSl4hBMf+76msQDa00UEr1+0X1Ah
# uBL/Ej2qnIMbU6yA+WxmzROC8zp+Kfb+wt9KLIHBID06U5V60nwGLXlpbYiIY4/V
# LJ7KncNakUJPbNzeJdc1CMh6zjo2pn7yx2G9C0Ip4QtfkhD8qPGxMwq9QGZ94+fy
# cZwQFNneBRlbv+D9DiOYM0PmKz3/D7HjVEERqGmwwWNQZw3OLAuInoPRJV6DV3zL
# 7gL9EmEc9JS6vAjkFU5Um7aIKyeAJDQZv3YqKpgg+7eQjZI2oXQ9qAzx9OHnWZ48
# kfpFnamQuVlmrx7iKyH0qPHL9U0OhFnrfEd2NlktecH8exaR1ltmAuHBI40lHQMo
# KH0WrEkP4bhAOcnRbn1r394dILcNwPu40rVB0WLw98OY9fde8XyWFO8vZcUrmLZS
# nwjDi5EHiK6OysqYgt4PAI6QnvxHm+/al4DnmmHxQG06/VmmmnTRUTE0mIy1fdCC
# Q3CEzEvBd8dWqumjSs/b5TO3n/VUp61/dMlO94qiJt7oNoxTx1ftjo/zf6ZhrRnq
# e6amQsN0Kgd/fAcoorGTzvqSJ+erECN+30gTwcJHx3F3hzsUBX65y1raOx94yBdk
# y0psMSmx/uZKuzijtYZ1Ew==
# SIG # End signature block