ExchangeNodeMaintenanceMode.psm1
#requires -Version 3.0 -Modules DnsClient, FailoverClusters <# =========================================================================== Description: Exchange Cluster Node Maintenance Mode Utilities Author: Joerg Hochwald <joerg.hochwald@outlook.com> CompanyName: Enabling Technology - http://www.enatec.io Filename: ExchangeNodeMaintenanceMode.psm1 License: BSD License - BSD 3-clause "New" or "Revised" License Link: http://jhochwald.com --------------------------------------------------------------------------- Module Name: ExchangeNodeMaintenanceMode =========================================================================== #> function Invoke-Exchange2016Workaround { <# .SYNOPSIS Workaround for Exchange 2016 on Windows Server 2016 .DESCRIPTION Workaround for Exchange 2016 on Windows Server 2016 .EXAMPLE PS C:\> Invoke-Exchange2016Workaround .NOTES This is a quick an dirty one :) . LINK Set-ExchangeNodeMaintenanceModeOn Set-ExchangeNodeMaintenanceModeOff Test-ExchangeNodeMaintenanceMode #> $paramGetCommand = @{ Name = 'Get-MailboxDatabaseCopyStatus' ErrorAction = 'SilentlyContinue' WarningAction = 'SilentlyContinue' } if (-not (Get-Command @paramGetCommand )) { try { $paramAddPSSnapin = @{ Name = 'Microsoft.Exchange.Management.PowerShell.SnapIn' } Add-PSSnapin @paramAddPSSnapin } catch { $paramWriteError = @{ Message = 'Sure that this is a Exchange Server?' ErrorAction = 'Stop' WarningAction = 'SilentlyContinue' } Write-Error @paramWriteError } } } function Set-ExchangeNodeMaintenanceModeOn { <# .SYNOPSIS Set the Exchange Node to Service .DESCRIPTION Set the Exchange Node to Service .PARAMETER ComputerName Name of the Exchange Node, default is local system .EXAMPLE # Node is in Maintenance Mode PS C:\> Set-ExchangeNodeMaintenanceModeOn $false .EXAMPLE # Node is not in Maintenance Mode PS C:\> Set-ExchangeNodeMaintenanceModeOn $true .NOTES TODO: Find a detection for the Workaround TODO: Find a better solution for the certificate check issue . LINK Invoke-Exchange2016Workaround Set-ExchangeNodeMaintenanceModeOff Test-ExchangeNodeMaintenanceMode #> param ( [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 1)] [ValidateNotNullOrEmpty()] [string] $ComputerName = $env:COMPUTERNAME ) Begin { # Workaround for Exchange 2016 on Windows Server 2016 Invoke-Exchange2016Workaround } Process { # Draining the server $paramSetServerComponentState = @{ identity = $ComputerName Component = 'ServerWideOffline' State = 'Draining' Requester = 'Maintenance' } Set-ServerComponentState @paramSetServerComponentState # Restart of the Sertvices enforces the draining $paramRestartService = @{ Name = 'MSExchangeTransport' Force = $true ErrorAction = 'SilentlyContinue' WarningAction = 'SilentlyContinue' Confirm = $false } $null = (Restart-Service @paramRestartService) $paramRestartService = @{ Name = 'MSExchangeFrontEndTransport' Force = $true ErrorAction = 'SilentlyContinue' WarningAction = 'SilentlyContinue' Confirm = $false } $null = (Restart-Service @paramRestartService) # Suspend the cluster node $paramSuspendClusterNode = @{ Name = $ComputerName Confirm = $false } $null = (Suspend-ClusterNode @paramSuspendClusterNode) # Move all databases to the other servers $paramSetMailboxServer = @{ identity = $ComputerName DatabaseCopyActivationDisabledAndMoveNow = $true } $null = (Set-MailboxServer @paramSetMailboxServer) # Get the Cluster Twin $PartnerNode = (Get-ClusterNode | Where-Object -FilterScript { $_.Name -ne $ComputerName } | Select-Object -ExpandProperty name) $paramResolveDnsName = @{ Name = $PartnerNode Type = 'A' } $PartnerNodeFQDN = ((Resolve-DnsName @paramResolveDnsName).name) $paramSetServerComponentState = @{ identity = $ComputerName Component = 'ServerWideOffline' State = 'Inactive' Requester = 'Maintenance' } $null = (Set-ServerComponentState @paramSetServerComponentState) $paramRedirectMessage = @{ Server = $ComputerName Target = $PartnerNodeFQDN Confirm = $false } $null = (Redirect-Message @paramRedirectMessage) } } function Set-ExchangeNodeMaintenanceModeOff { <# .SYNOPSIS Return Exchange Node to normal operation .DESCRIPTION Return Exchange Node to normal operation .PARAMETER ComputerName Name of the Exchange Node, default is local system .EXAMPLE # Enable normal operations PS C:\> Set-ExchangeNodeMaintenanceModeOff $true .EXAMPLE # Fails to enable noprmal operations PS C:\> Set-ExchangeNodeMaintenanceModeOff $false .NOTES . LINK Invoke-Exchange2016Workaround Set-ExchangeNodeMaintenanceModeOn Test-ExchangeNodeMaintenanceMode #> [OutputType([bool])] param ( [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 1)] [ValidateNotNullOrEmpty()] [string] $ComputerName = $env:COMPUTERNAME ) Begin { # Workaround for Exchange 2016 on Windows Server 2016 Invoke-Exchange2016Workaround } Process { try { # Activate the components $paramSetServerComponentState = @{ identity = $ComputerName Component = 'ServerWideOffline' State = 'Active' Requester = 'Maintenance' } $null = (Set-ServerComponentState @paramSetServerComponentState) # Activate the Cluster Node $paramResumeClusterNode = @{ Name = $ComputerName ErrorAction = 'Stop' WarningAction = 'SilentlyContinue' } $null = (Resume-ClusterNode @paramResumeClusterNode) # Activate the Databases $paramSetMailboxServer = @{ identity = $ComputerName DatabaseCopyAutoActivationPolicy = 'Unrestricted' DatabaseCopyActivationDisabledAndMoveNow = $false ErrorAction = 'Stop' WarningAction = 'SilentlyContinue' } $null = (Set-MailboxServer @paramSetMailboxServer) } catch { return $false } # Default return $true } } function Test-ExchangeNodeMaintenanceMode { <# .SYNOPSIS Check if the exchange node is in maintenance mode .DESCRIPTION Check if the exchange node is in maintenance mode .PARAMETER ComputerName Name of the Exchange Node, default is local system .EXAMPLE # Given node is in normal operation mode PS C:\> Test-ExchangeNodeMaintenanceMode $false .EXAMPLE # Given node is in maintenance mode PS C:\> Test-ExchangeNodeMaintenanceMode $true .NOTES Additional information about the function. #> [OutputType([bool])] param ( [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 1)] [ValidateNotNullOrEmpty()] [string] $ComputerName = $env:COMPUTERNAME ) Begin { # Workaround for Exchange 2016 on Windows Server 2016 Invoke-Exchange2016Workaround # Set the Default $IsFalse = $false } Process { # Wait until all databases are moved $paramGetMailboxDatabaseCopyStatus = @{ server = $ComputerName ErrorAction = 'Stop' WarningAction = 'SilentlyContinue' } try { $ActiveDBs = $null $ActiveDBs = (Get-MailboxDatabaseCopyStatus @paramGetMailboxDatabaseCopyStatus | Where-Object -FilterScript { $_.Status -eq 'Mounted' }) } catch { $IsFalse = $true } if ($ActiveDBs) { $IsFalse = $false } else { # Build the URL to check $URL = 'https://' + $ComputerName + '/owa/healthcheck.htm' # Ignore certificate warning (Will not match anyway) try { Add-Type -TypeDefinition @' using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCertsPolicy : ICertificatePolicy { public bool CheckValidationResult( ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } } '@ } catch { Write-Verbose -Message 'Unable to add new Type' } $paramNewObject = @{ TypeName = 'TrustAllCertsPolicy' ErrorAction = 'SilentlyContinue' WarningAction = 'SilentlyContinue' } [Net.ServicePointManager]::CertificatePolicy = (New-Object @paramNewObject) try { $result = $null # Get the result $paramInvokeWebRequest = @{ Uri = $URL ErrorAction = 'Stop' WarningAction = 'SilentlyContinue' } $result = $null $result = (Invoke-WebRequest @paramInvokeWebRequest) } catch { $IsFalse = $true } # Check the result if ($result.StatusCode -eq '200') { $IsFalse = $false } } } End { # Default return $IsFalse } } Export-ModuleMember -Function Invoke-Exchange2016Workaround, Set-ExchangeNodeMaintenanceModeOn, Set-ExchangeNodeMaintenanceModeOff, Test-ExchangeNodeMaintenanceMode # SIG # Begin signature block # MIITegYJKoZIhvcNAQcCoIITazCCE2cCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQULdhI0c5Qo6zbmL5V+K00+X81 # 1v2ggg4LMIIEFDCCAvygAwIBAgILBAAAAAABL07hUtcwDQYJKoZIhvcNAQEFBQAw # VzELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNV # BAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMTA0 # MTMxMDAwMDBaFw0yODAxMjgxMjAwMDBaMFIxCzAJBgNVBAYTAkJFMRkwFwYDVQQK # ExBHbG9iYWxTaWduIG52LXNhMSgwJgYDVQQDEx9HbG9iYWxTaWduIFRpbWVzdGFt # cGluZyBDQSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlO9l # +LVXn6BTDTQG6wkft0cYasvwW+T/J6U00feJGr+esc0SQW5m1IGghYtkWkYvmaCN # d7HivFzdItdqZ9C76Mp03otPDbBS5ZBb60cO8eefnAuQZT4XljBFcm05oRc2yrmg # jBtPCBn2gTGtYRakYua0QJ7D/PuV9vu1LpWBmODvxevYAll4d/eq41JrUJEpxfz3 # zZNl0mBhIvIG+zLdFlH6Dv2KMPAXCae78wSuq5DnbN96qfTvxGInX2+ZbTh0qhGL # 2t/HFEzphbLswn1KJo/nVrqm4M+SU4B09APsaLJgvIQgAIMboe60dAXBKY5i0Eex # +vBTzBj5Ljv5cH60JQIDAQABo4HlMIHiMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMB # Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBRG2D7/3OO+/4Pm9IWbsN1q1hSpwTBHBgNV # HSAEQDA+MDwGBFUdIAAwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xvYmFs # c2lnbi5jb20vcmVwb3NpdG9yeS8wMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2Ny # bC5nbG9iYWxzaWduLm5ldC9yb290LmNybDAfBgNVHSMEGDAWgBRge2YaRQ2XyolQ # L30EzTSo//z9SzANBgkqhkiG9w0BAQUFAAOCAQEATl5WkB5GtNlJMfO7FzkoG8IW # 3f1B3AkFBJtvsqKa1pkuQJkAVbXqP6UgdtOGNNQXzFU6x4Lu76i6vNgGnxVQ380W # e1I6AtcZGv2v8Hhc4EvFGN86JB7arLipWAQCBzDbsBJe/jG+8ARI9PBw+DpeVoPP # PfsNvPTF7ZedudTbpSeE4zibi6c1hkQgpDttpGoLoYP9KOva7yj2zIhd+wo7AKvg # IeviLzVsD440RZfroveZMzV+y5qKu0VN5z+fwtmK+mWybsd+Zf/okuEsMaL3sCc2 # SI8mbzvuTXYfecPlf5Y1vC0OzAGwjn//UYCAp5LUs0RGZIyHTxZjBzFLY7Df8zCC # BJ8wggOHoAMCAQICEhEh1pmnZJc+8fhCfukZzFNBFDANBgkqhkiG9w0BAQUFADBS # MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEoMCYGA1UE # AxMfR2xvYmFsU2lnbiBUaW1lc3RhbXBpbmcgQ0EgLSBHMjAeFw0xNjA1MjQwMDAw # MDBaFw0yNzA2MjQwMDAwMDBaMGAxCzAJBgNVBAYTAlNHMR8wHQYDVQQKExZHTU8g # R2xvYmFsU2lnbiBQdGUgTHRkMTAwLgYDVQQDEydHbG9iYWxTaWduIFRTQSBmb3Ig # TVMgQXV0aGVudGljb2RlIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK # AoIBAQCwF66i07YEMFYeWA+x7VWk1lTL2PZzOuxdXqsl/Tal+oTDYUDFRrVZUjtC # oi5fE2IQqVvmc9aSJbF9I+MGs4c6DkPw1wCJU6IRMVIobl1AcjzyCXenSZKX1GyQ # oHan/bjcs53yB2AsT1iYAGvTFVTg+t3/gCxfGKaY/9Sr7KFFWbIub2Jd4NkZrItX # nKgmK9kXpRDSRwgacCwzi39ogCq1oV1r3Y0CAikDqnw3u7spTj1Tk7Om+o/SWJMV # TLktq4CjoyX7r/cIZLB6RA9cENdfYTeqTmvT0lMlnYJz+iz5crCpGTkqUPqp0Dw6 # yuhb7/VfUfT5CtmXNd5qheYjBEKvAgMBAAGjggFfMIIBWzAOBgNVHQ8BAf8EBAMC # B4AwTAYDVR0gBEUwQzBBBgkrBgEEAaAyAR4wNDAyBggrBgEFBQcCARYmaHR0cHM6 # Ly93d3cuZ2xvYmFsc2lnbi5jb20vcmVwb3NpdG9yeS8wCQYDVR0TBAIwADAWBgNV # HSUBAf8EDDAKBggrBgEFBQcDCDBCBgNVHR8EOzA5MDegNaAzhjFodHRwOi8vY3Js # Lmdsb2JhbHNpZ24uY29tL2dzL2dzdGltZXN0YW1waW5nZzIuY3JsMFQGCCsGAQUF # BwEBBEgwRjBEBggrBgEFBQcwAoY4aHR0cDovL3NlY3VyZS5nbG9iYWxzaWduLmNv # bS9jYWNlcnQvZ3N0aW1lc3RhbXBpbmdnMi5jcnQwHQYDVR0OBBYEFNSihEo4Whh/ # uk8wUL2d1XqH1gn3MB8GA1UdIwQYMBaAFEbYPv/c477/g+b0hZuw3WrWFKnBMA0G # CSqGSIb3DQEBBQUAA4IBAQCPqRqRbQSmNyAOg5beI9Nrbh9u3WQ9aCEitfhHNmmO # 4aVFxySiIrcpCcxUWq7GvM1jjrM9UEjltMyuzZKNniiLE0oRqr2j79OyNvy0oXK/ # bZdjeYxEvHAvfvO83YJTqxr26/ocl7y2N5ykHDC8q7wtRzbfkiAD6HHGWPZ1BZo0 # 8AtZWoJENKqA5C+E9kddlsm2ysqdt6a65FDT1De4uiAO0NOSKlvEWbuhbds8zkSd # wTgqreONvc0JdxoQvmcKAjZkiLmzGybu555gxEaovGEzbM9OuZy5avCfN/61PU+a # 003/3iCOTpem/Z8JvE3KGHbJsE2FUPKA0h0G9VgEB7EYMIIFTDCCBDSgAwIBAgIQ # FtT3Ux2bGCdP8iZzNFGAXDANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJHQjEb # MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRow # GAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDEjMCEGA1UEAxMaQ09NT0RPIFJTQSBD # b2RlIFNpZ25pbmcgQ0EwHhcNMTUwNzE3MDAwMDAwWhcNMTgwNzE2MjM1OTU5WjCB # kDELMAkGA1UEBhMCREUxDjAMBgNVBBEMBTM1NTc2MQ8wDQYDVQQIDAZIZXNzZW4x # EDAOBgNVBAcMB0xpbWJ1cmcxGDAWBgNVBAkMD0JhaG5ob2ZzcGxhdHogMTEZMBcG # A1UECgwQS3JlYXRpdlNpZ24gR21iSDEZMBcGA1UEAwwQS3JlYXRpdlNpZ24gR21i # SDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK8jDmF0TO09qJndJ9eG # Fqra1lf14NDhM8wIT8cFcZ/AX2XzrE6zb/8kE5sL4/dMhuTOp+SMt0tI/SON6BY3 # 208v/NlDI7fozAqHfmvPhLX6p/TtDkmSH1sD8AIyrTH9b27wDNX4rC914Ka4EBI8 # sGtZwZOQkwQdlV6gCBmadar+7YkVhAbIIkSazE9yyRTuffidmtHV49DHPr+ql4ji # NJ/K27ZFZbwM6kGBlDBBSgLUKvufMY+XPUukpzdCaA0UzygGUdDfgy0htSSp8MR9 # Rnq4WML0t/fT0IZvmrxCrh7NXkQXACk2xtnkq0bXUIC6H0Zolnfl4fanvVYyvD88 # qIECAwEAAaOCAbIwggGuMB8GA1UdIwQYMBaAFCmRYP+KTfrr+aZquM/55ku9Sc4S # MB0GA1UdDgQWBBSeVG4/9UvVjmv8STy4f7kGHucShjAOBgNVHQ8BAf8EBAMCB4Aw # DAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDAzARBglghkgBhvhCAQEE # BAMCBBAwRgYDVR0gBD8wPTA7BgwrBgEEAbIxAQIBAwIwKzApBggrBgEFBQcCARYd # aHR0cHM6Ly9zZWN1cmUuY29tb2RvLm5ldC9DUFMwQwYDVR0fBDwwOjA4oDagNIYy # aHR0cDovL2NybC5jb21vZG9jYS5jb20vQ09NT0RPUlNBQ29kZVNpZ25pbmdDQS5j # cmwwdAYIKwYBBQUHAQEEaDBmMD4GCCsGAQUFBzAChjJodHRwOi8vY3J0LmNvbW9k # b2NhLmNvbS9DT01PRE9SU0FDb2RlU2lnbmluZ0NBLmNydDAkBggrBgEFBQcwAYYY # aHR0cDovL29jc3AuY29tb2RvY2EuY29tMCMGA1UdEQQcMBqBGGhvY2h3YWxkQGty # ZWF0aXZzaWduLm5ldDANBgkqhkiG9w0BAQsFAAOCAQEASSZkxKo3EyEk/qW0ZCs7 # CDDHKTx3UcqExigsaY0DRo9fbWgqWynItsqdwFkuQYJxzknqm2JMvwIK6BtfWc64 # WZhy0BtI3S3hxzYHxDjVDBLBy91kj/mddPjen60W+L66oNEXiBuIsOcJ9e7tH6Vn # 9eFEUjuq5esoJM6FV+MIKv/jPFWMp5B6EtX4LDHEpYpLRVQnuxoc38mmd+NfjcD2 # /o/81bu6LmBFegHAaGDpThGf8Hk3NVy0GcpQ3trqmH6e3Cpm8Ut5UkoSONZdkYWw # rzkmzFgJyoM2rnTMTh4ficxBQpB7Ikv4VEnrHRReihZ0zwN+HkXO1XEnd3hm+08j # LzGCBNkwggTVAgEBMIGRMH0xCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVy # IE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBD # QSBMaW1pdGVkMSMwIQYDVQQDExpDT01PRE8gUlNBIENvZGUgU2lnbmluZyBDQQIQ # FtT3Ux2bGCdP8iZzNFGAXDAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAig # AoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgEL # MQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUq0PmLn7+NlCWtwKlTwY4 # kJiJhvkwDQYJKoZIhvcNAQEBBQAEggEAeWMWUU7BXk1AD9doVHL6tCxyNBjYIav1 # v0kYnAFrUWwfpPlOwciTQObrYp2JP0YGhg5GFYQFnTtki+s+clBGi/MktBeRu/Gw # FRuZtbgVuMKDT+zfIhMo58g0B4COVQ9e89do1SHrOEmkB74Sad/rtLaBBDEXPmgY # CyLU0OCY1xkisK2SvP1KmmqKpSbZ9P9roGkVHCmz47KpBl8H6MpCUJVw9ihSvjXF # PawzWajFbOuKBFidkD04VJU5wSFeTBoVGZS/sYekabyCAuxY9ra8NY1tkS1aZC5I # 0yfhCykkPbNTZ3ToNG4ONXTqOfT9SV0bQo7kRGFQ4Ja7RWVTtNa6qKGCAqIwggKe # BgkqhkiG9w0BCQYxggKPMIICiwIBATBoMFIxCzAJBgNVBAYTAkJFMRkwFwYDVQQK # ExBHbG9iYWxTaWduIG52LXNhMSgwJgYDVQQDEx9HbG9iYWxTaWduIFRpbWVzdGFt # cGluZyBDQSAtIEcyAhIRIdaZp2SXPvH4Qn7pGcxTQRQwCQYFKw4DAhoFAKCB/TAY # BgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xNzA2MTUy # MTIzMTNaMCMGCSqGSIb3DQEJBDEWBBR53Oxiyoikdq5J70V279iZ6O/ObDCBnQYL # KoZIhvcNAQkQAgwxgY0wgYowgYcwgYQEFGO4L6th9YOQlpUFCwAknFApM+x5MGww # VqRUMFIxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSgw # JgYDVQQDEx9HbG9iYWxTaWduIFRpbWVzdGFtcGluZyBDQSAtIEcyAhIRIdaZp2SX # PvH4Qn7pGcxTQRQwDQYJKoZIhvcNAQEBBQAEggEAmHDFOEv1yC72p/bJNPM/6qWg # yOFZJgH6z6g2w4cfY4RwO5T6qvjtWwp0FApqV0OzxE7lYBNiQO+HY7lNvot568x3 # GBZ07FXFwz7ly7shZcVz/NZJ4MAQhBkhrQwc6otklDuOwZbuv5I5CRbFD3YzTmDY # f/Z2nE47rEPxTplLk85R8YEvB0ZoWAspmO6RmxGWQzVhp6wVTIBVZc7+QFE19xdW # wmdRs/HSMPV8a2bdQF8QGEU6HL9Cy9PDHnz0+fwrm4ykMT36qpUrc73ltQ9j8osJ # wqE3b27dHgiatV8C+NGu8cixyc5o4Q9SE+mTTsMezj6zJSssyXwaVEDL2itzUQ== # SIG # End signature block |