diff --git a/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerFrequentConfigurationIssues.ps1 b/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerFrequentConfigurationIssues.ps1 index 12cd4301b..4a9a2ce2d 100644 --- a/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerFrequentConfigurationIssues.ps1 +++ b/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerFrequentConfigurationIssues.ps1 @@ -23,6 +23,12 @@ function Invoke-AnalyzerFrequentConfigurationIssues { $tcpKeepAlive = $osInformation.RegistryValues.TCPKeepAlive $organizationInformation = $HealthServerObject.OrganizationInformation + # cSpell:disable + $eopDomainRegExPattern = "^[^.]+\.mail\.protection\.(outlook\.com|partner\.outlook\.cn|office365\.us)$" + $remoteRoutingDomainRegExPattern = "^[^.]+\.mail\.(onmicrosoft\.com|partner\.onmschina\.cn|onmicrosoft\.us)$" + $serviceDomainRegExPattern = "^mail\.protection\.(outlook\.com|partner\.outlook\.cn|office365\.us)$" + # cSpell:enable + $baseParams = @{ AnalyzedInformation = $AnalyzeResults DisplayGroupingKey = (Get-DisplayResultsGroupingKey -Name "Frequent Configuration Issues" -DisplayOrder $Order) @@ -299,8 +305,8 @@ function Invoke-AnalyzerFrequentConfigurationIssues { $sendConnectors = $exchangeInformation.ExchangeConnectors | Where-Object { $_.ConnectorType -eq "Send" } foreach ($sendConnector in $sendConnectors) { - $smartHostMatch = ($sendConnector.SmartHosts -like "*.mail.protection.outlook.com").Count -gt 0 - $dnsMatch = $sendConnector.SmartHosts -eq 0 -and ($sendConnector.AddressSpaces.Address -like "*.mail.onmicrosoft.com").Count -gt 0 + $smartHostMatch = ($sendConnector.SmartHosts -match $eopDomainRegExPattern).Count -gt 0 + $dnsMatch = $sendConnector.SmartHosts -eq 0 -and ($sendConnector.AddressSpaces.Address -match $remoteRoutingDomainRegExPattern).Count -gt 0 if ($dnsMatch -or $smartHostMatch) { $exoConnector.Add($sendConnector) @@ -315,12 +321,17 @@ function Invoke-AnalyzerFrequentConfigurationIssues { $showMoreInfo = $false foreach ($connector in $exoConnector) { - # Misconfigured connector is if TLSCertificateName is not set or CloudServicesMailEnabled not set to true - if ($connector.CloudEnabled -eq $false -or - $connector.CertificateDetails.TlsCertificateNameStatus -eq "TlsCertificateNameEmpty") { + + # If CloudServiceMailEnabled is not set to true it means the connector is misconfigured + # If no Fqdn is set on the connector, the Fqdn of the computer is used to perform best matching certificate selection + # There is a risk that the Fqdn is an internal one (e.g., server.contoso.local) which will lead to a broken hybrid mail flow in case that TlsCertificateName is not set + if (($connector.CloudEnabled -eq $false) -or + ($null -eq $connector.Fqdn -and + $connector.CertificateDetails.TlsCertificateNameStatus -eq "TlsCertificateNameEmpty")) { $params = $baseParams + @{ Name = "Send Connector - $($connector.Identity.ToString())" - Details = "Misconfigured to send authenticated internal mail to M365." + + Details = "Misconfigured to send authenticated internal mail to M365" + + "`r`n`t`t`tFqdn set: $($null -ne $connector.Fqdn)" + "`r`n`t`t`tCloudServicesMailEnabled: $($connector.CloudEnabled)" + "`r`n`t`t`tTLSCertificateName set: $($connector.CertificateDetails.TlsCertificateNameStatus -ne "TlsCertificateNameEmpty")" DisplayCustomTabNumber = 2 @@ -342,11 +353,11 @@ function Invoke-AnalyzerFrequentConfigurationIssues { $showMoreInfo = $true } - if ($connector.TlsDomain -ne "mail.protection.outlook.com" -and + if ($connector.TlsDomain -notmatch $serviceDomainRegExPattern -and $connector.TlsAuthLevel -eq "DomainValidation") { $params = $baseParams + @{ Name = "Send Connector - $($connector.Identity.ToString())" - Details = "TLSDomain not set to mail.protection.outlook.com" + Details = "TLSDomain not set to service domain (e.g.,mail.protection.outlook.com)" DisplayCustomTabNumber = 2 DisplayWriteType = "Yellow" } diff --git a/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerHybridInformation.ps1 b/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerHybridInformation.ps1 index 2017e3e32..8845421e2 100644 --- a/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerHybridInformation.ps1 +++ b/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerHybridInformation.ps1 @@ -24,6 +24,10 @@ function Invoke-AnalyzerHybridInformation { $exchangeInformation = $HealthServerObject.ExchangeInformation $getHybridConfiguration = $HealthServerObject.OrganizationInformation.GetHybridConfiguration + # Check if the server is configured as sending or receiving transport server - if it is, the certificate used for hybrid mail flow must exist on the machine + $certificateShouldExistOnServer = $getHybridConfiguration.SendingTransportServers.DistinguishedName -contains $exchangeInformation.GetExchangeServer.DistinguishedName -or + $getHybridConfiguration.ReceivingTransportServers.DistinguishedName -contains $exchangeInformation.GetExchangeServer.DistinguishedName + if ($exchangeInformation.BuildInformation.VersionInformation.BuildVersion -ge "15.0.0.0" -and $null -ne $getHybridConfiguration) { @@ -354,6 +358,9 @@ function Invoke-AnalyzerHybridInformation { } } else { $cloudConnectorTlsCertificateName = "Not set" + + Write-Verbose "Server is configured for hybrid mail flow and the transport certificate should exist on this server? $certificateShouldExistOnServer" + if ($null -ne $connector.CertificateDetails.TlsCertificateName) { $cloudConnectorTlsCertificateName = $connector.CertificateDetails.TlsCertificateName } @@ -365,87 +372,90 @@ function Invoke-AnalyzerHybridInformation { } Add-AnalyzedResultInformation @params - $params = $baseParams + @{ - Name = "Certificate Found On Server" - Details = $connector.CertificateDetails.CertificateMatchDetected - DisplayWriteType = $cloudConnectorWriteType - } - Add-AnalyzedResultInformation @params - - if ($connector.CertificateDetails.TlsCertificateNameStatus -eq "TlsCertificateNameEmpty") { + # Don't perform the following checks if the server is not a sending or receiving transport server configured for hybrid mail flow (there is a high chance that the certificate didn't exist which is by design) + if ($certificateShouldExistOnServer) { $params = $baseParams + @{ - Details = "There is no 'TlsCertificateName' configured for this cloud mail enabled connector.`r`n`t`tThis will cause mail flow issues in hybrid scenarios. More information: https://aka.ms/HC-HybridConnector" - DisplayWriteType = $cloudConnectorWriteType - DisplayCustomTabNumber = 2 - } - Add-AnalyzedResultInformation @params - } elseif ($connector.CertificateDetails.CertificateMatchDetected -eq $false) { - $params = $baseParams + @{ - Details = "The configured 'TlsCertificateName' was not found on the server.`r`n`t`tThis may cause mail flow issues. More information: https://aka.ms/HC-HybridConnector" - DisplayWriteType = $cloudConnectorWriteType - DisplayCustomTabNumber = 2 + Name = "Certificate Found On Server" + Details = $connector.CertificateDetails.CertificateMatchDetected + DisplayWriteType = $cloudConnectorWriteType } Add-AnalyzedResultInformation @params - } else { - Add-AnalyzedResultInformation -Name "Certificate Thumbprint(s)" @baseParams - foreach ($thumbprint in $($connector.CertificateDetails.CertificateLifetimeInfo).keys) { + if ($connector.CertificateDetails.TlsCertificateNameStatus -eq "TlsCertificateNameEmpty") { $params = $baseParams + @{ - Details = $thumbprint + Details = "There is no 'TlsCertificateName' configured for this cloud mail enabled connector.`r`n`t`tThis will cause mail flow issues in hybrid scenarios. More information: https://aka.ms/HC-HybridConnector" + DisplayWriteType = $cloudConnectorWriteType DisplayCustomTabNumber = 2 } Add-AnalyzedResultInformation @params - } - - Add-AnalyzedResultInformation -Name "Lifetime In Days" @baseParams - - foreach ($thumbprint in $($connector.CertificateDetails.CertificateLifetimeInfo).keys) { - switch ($($connector.CertificateDetails.CertificateLifetimeInfo)[$thumbprint]) { - { ($_ -ge 60) } { $certificateLifetimeWriteType = "Green"; break } - { ($_ -ge 30) } { $certificateLifetimeWriteType = "Yellow"; break } - default { $certificateLifetimeWriteType = "Red" } - } - + } elseif ($connector.CertificateDetails.CertificateMatchDetected -eq $false) { $params = $baseParams + @{ - Details = ($connector.CertificateDetails.CertificateLifetimeInfo)[$thumbprint] - DisplayWriteType = $certificateLifetimeWriteType + Details = "The configured 'TlsCertificateName' was not found on the server.`r`n`t`tThis may cause mail flow issues. More information: https://aka.ms/HC-HybridConnector" + DisplayWriteType = $cloudConnectorWriteType DisplayCustomTabNumber = 2 } Add-AnalyzedResultInformation @params - } + } else { + Add-AnalyzedResultInformation -Name "Certificate Thumbprint(s)" @baseParams + + foreach ($thumbprint in $($connector.CertificateDetails.CertificateLifetimeInfo).keys) { + $params = $baseParams + @{ + Details = $thumbprint + DisplayCustomTabNumber = 2 + } + Add-AnalyzedResultInformation @params + } - $connectorCertificateMatchesHybridCertificate = $false - $connectorCertificateMatchesHybridCertificateWritingType = "Yellow" - if (($connector.CertificateDetails.TlsCertificateSet) -and - (-not([System.String]::IsNullOrEmpty($getHybridConfiguration.TlsCertificateName))) -and - ($connector.CertificateDetails.TlsCertificateName -eq $getHybridConfiguration.TlsCertificateName)) { - $connectorCertificateMatchesHybridCertificate = $true - $connectorCertificateMatchesHybridCertificateWritingType = "Green" - } + Add-AnalyzedResultInformation -Name "Lifetime In Days" @baseParams + + foreach ($thumbprint in $($connector.CertificateDetails.CertificateLifetimeInfo).keys) { + switch ($($connector.CertificateDetails.CertificateLifetimeInfo)[$thumbprint]) { + { ($_ -ge 60) } { $certificateLifetimeWriteType = "Green"; break } + { ($_ -ge 30) } { $certificateLifetimeWriteType = "Yellow"; break } + default { $certificateLifetimeWriteType = "Red" } + } + + $params = $baseParams + @{ + Details = ($connector.CertificateDetails.CertificateLifetimeInfo)[$thumbprint] + DisplayWriteType = $certificateLifetimeWriteType + DisplayCustomTabNumber = 2 + } + Add-AnalyzedResultInformation @params + } - $params = $baseParams + @{ - Name = "Certificate Matches Hybrid Certificate" - Details = $connectorCertificateMatchesHybridCertificate - DisplayWriteType = $connectorCertificateMatchesHybridCertificateWritingType - } - Add-AnalyzedResultInformation @params + $connectorCertificateMatchesHybridCertificate = $false + $connectorCertificateMatchesHybridCertificateWritingType = "Yellow" + if (($connector.CertificateDetails.TlsCertificateSet) -and + (-not([System.String]::IsNullOrEmpty($getHybridConfiguration.TlsCertificateName))) -and + ($connector.CertificateDetails.TlsCertificateName -eq $getHybridConfiguration.TlsCertificateName)) { + $connectorCertificateMatchesHybridCertificate = $true + $connectorCertificateMatchesHybridCertificateWritingType = "Green" + } - if (($connector.CertificateDetails.TlsCertificateNameStatus -eq "TlsCertificateNameSyntaxInvalid") -or - (($connector.CertificateDetails.GoodTlsCertificateSyntax -eq $false) -and - ($null -ne $connector.CertificateDetails.TlsCertificateName))) { $params = $baseParams + @{ - Name = "TlsCertificateName Syntax Invalid" - Details = "True" - DisplayWriteType = $cloudConnectorWriteType + Name = "Certificate Matches Hybrid Certificate" + Details = $connectorCertificateMatchesHybridCertificate + DisplayWriteType = $connectorCertificateMatchesHybridCertificateWritingType } Add-AnalyzedResultInformation @params - $params = $baseParams + @{ - Details = "The correct syntax is: 'X.500IssuerX.500Subject'" - DisplayWriteType = $cloudConnectorWriteType - DisplayCustomTabNumber = 2 + if (($connector.CertificateDetails.TlsCertificateNameStatus -eq "TlsCertificateNameSyntaxInvalid") -or + (($connector.CertificateDetails.GoodTlsCertificateSyntax -eq $false) -and + ($null -ne $connector.CertificateDetails.TlsCertificateName))) { + $params = $baseParams + @{ + Name = "TlsCertificateName Syntax Invalid" + Details = "True" + DisplayWriteType = $cloudConnectorWriteType + } + Add-AnalyzedResultInformation @params + + $params = $baseParams + @{ + Details = "The correct syntax is: 'X.500IssuerX.500Subject'" + DisplayWriteType = $cloudConnectorWriteType + DisplayCustomTabNumber = 2 + } + Add-AnalyzedResultInformation @params } - Add-AnalyzedResultInformation @params } } } diff --git a/Diagnostics/HealthChecker/DataCollection/ExchangeInformation/Get-ExchangeConnectors.ps1 b/Diagnostics/HealthChecker/DataCollection/ExchangeInformation/Get-ExchangeConnectors.ps1 index 9ead7441e..30c1842a1 100644 --- a/Diagnostics/HealthChecker/DataCollection/ExchangeInformation/Get-ExchangeConnectors.ps1 +++ b/Diagnostics/HealthChecker/DataCollection/ExchangeInformation/Get-ExchangeConnectors.ps1 @@ -30,6 +30,7 @@ function Get-ExchangeConnectors { $exchangeFactoryConnectorReturnObject = [PSCustomObject]@{ Identity = $ConnectorObject.Identity Name = $ConnectorObject.Name + Fqdn = $ConnectorObject.Fqdn Enabled = $ConnectorObject.Enabled CloudEnabled = $false ConnectorType = $null