From a4a126c2ccea0bb0366d597919e1398311c5e88c Mon Sep 17 00:00:00 2001 From: David Paulson Date: Tue, 16 Jan 2024 12:11:34 -0600 Subject: [PATCH 1/7] Call out issue with url rewrite match to wild card --- .../Invoke-AnalyzerIISInformation.ps1 | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerIISInformation.ps1 b/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerIISInformation.ps1 index a6dfcd7286..441b41ac37 100644 --- a/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerIISInformation.ps1 +++ b/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerIISInformation.ps1 @@ -492,7 +492,9 @@ function Invoke-AnalyzerIISInformation { # Use 'DisplayKey' for the display results. $alreadyDisplayedUrlRewriteRules = @{} $alreadyDisplayedUrlKey = "DisplayKey" + $urlMatchProblem = "UrlMatchProblem" $alreadyDisplayedUrlRewriteRules.Add($alreadyDisplayedUrlKey, (New-Object System.Collections.Generic.List[object])) + $alreadyDisplayedUrlRewriteRules.Add($urlMatchProblem, (New-Object System.Collections.Generic.List[string])) foreach ($key in $urlRewriteRules.Keys) { $currentSection = $urlRewriteRules[$key] @@ -511,6 +513,7 @@ function Invoke-AnalyzerIISInformation { #multiple match type possibilities, but should only be one per rule. $propertyType = ($rule.match | Get-Member | Where-Object { $_.MemberType -eq "Property" }).Name + $isUrlMatchProblem = $propertyType -eq "url" -and $rule.match.$propertyType -eq "*" $matchProperty = "$propertyType - $($rule.match.$propertyType)" $displayObject = [PSCustomObject]@{ @@ -524,6 +527,10 @@ function Invoke-AnalyzerIISInformation { if (-not ($alreadyDisplayedUrlRewriteRules.ContainsKey((($displayObject.RewriteRuleName))))) { $alreadyDisplayedUrlRewriteRules.Add($displayObject.RewriteRuleName, $displayObject) $alreadyDisplayedUrlRewriteRules[$alreadyDisplayedUrlKey].Add($displayObject) + + if ($isUrlMatchProblem) { + $alreadyDisplayedUrlRewriteRules[$urlMatchProblem].Add($rule.Name) + } } } } @@ -538,6 +545,18 @@ function Invoke-AnalyzerIISInformation { AddHtmlDetailRow = $false } Add-AnalyzedResultInformation @params + + if ($alreadyDisplayedUrlRewriteRules[$urlMatchProblem].Count -gt 0) { + $params = $baseParams + @{ + Name = "Misconfigured URL Rewrite Rule - URL Match Problem Rules" + Details = "$([string]::Join(",", $alreadyDisplayedUrlRewriteRules[$urlMatchProblem]))" + + "`r`n`t`tURL Match is set only a wild card which will result in a HTTP 500." + + "`r`n`t`tIf the rule is required, the URL match should be '.*' to avoid issues." + DisplayWriteType = "Red" + } + + Add-AnalyzedResultInformation @params + } } if ($null -ne $missingWebApplicationConfigFile) { From 6175b3c4399f93d0623778abbd1360bd82ea95ee Mon Sep 17 00:00:00 2001 From: David Paulson Date: Tue, 16 Jan 2024 12:42:53 -0600 Subject: [PATCH 2/7] More information provided when InternalNLBBypassUrl is set --- .../Analyzer/Invoke-AnalyzerExchangeInformation.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerExchangeInformation.ps1 b/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerExchangeInformation.ps1 index f625d9ceac..bf1d043b9b 100644 --- a/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerExchangeInformation.ps1 +++ b/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerExchangeInformation.ps1 @@ -299,7 +299,9 @@ function Invoke-AnalyzerExchangeInformation { if (-not ([string]::IsNullOrWhiteSpace($getWebServicesVirtualDirectory.InternalNLBBypassUrl))) { $params = $baseParams + @{ Name = "EWS Internal Bypass URL Set" - Details = "$($getWebServicesVirtualDirectory.InternalNLBBypassUrl) - Can cause issues after KB 5001779" + Details = "$($getWebServicesVirtualDirectory.InternalNLBBypassUrl) - Can cause issues after KB 5001779" + + "`r`n`t`tThe Web Services Virtual Directory has a value set for InternalNLBBypassUrl which can cause problems with Exchange." + + "`r`n`t`tSet the InternalNLBBypassUrl to NULL to correct this." DisplayWriteType = "Red" } Add-AnalyzedResultInformation @params From fce9698a55798c39fd52a22b4277d0c876bfb6f9 Mon Sep 17 00:00:00 2001 From: David Paulson Date: Tue, 16 Jan 2024 15:57:04 -0600 Subject: [PATCH 3/7] Http Proxy logic to correctly pull out the right string values --- .../Get-HttpProxySetting.ps1 | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/Diagnostics/HealthChecker/DataCollection/ServerInformation/Get-HttpProxySetting.ps1 b/Diagnostics/HealthChecker/DataCollection/ServerInformation/Get-HttpProxySetting.ps1 index a6be0ee705..02cabbae95 100644 --- a/Diagnostics/HealthChecker/DataCollection/ServerInformation/Get-HttpProxySetting.ps1 +++ b/Diagnostics/HealthChecker/DataCollection/ServerInformation/Get-HttpProxySetting.ps1 @@ -15,25 +15,28 @@ function Get-HttpProxySetting { [Parameter(Mandatory = $true)][string]$RegistryLocation ) $connections = Get-ItemProperty -Path $RegistryLocation + $byteLength = 4 + $proxyStartLocation = 16 + $proxyLength = 0 $proxyAddress = [string]::Empty $byPassList = [string]::Empty if (($null -ne $connections) -and ($Connections | Get-Member).Name -contains "WinHttpSettings") { - $onProxy = $true + try { + $bytes = $Connections.WinHttpSettings + $proxyLength = [System.BitConverter]::ToInt32($bytes, $proxyStartLocation - $byteLength) - foreach ($Byte in $Connections.WinHttpSettings) { - if ($onProxy -and - $Byte -ge 42) { - $proxyAddress += [CHAR]$Byte - } elseif (-not $onProxy -and - $Byte -ge 42) { - $byPassList += [CHAR]$Byte - } elseif (-not ([string]::IsNullOrEmpty($proxyAddress)) -and - $onProxy -and - $Byte -eq 0) { - $onProxy = $false + if ($proxyLength -gt 0) { + $proxyAddress = [System.Text.Encoding]::UTF8.GetString($bytes, $proxyStartLocation, $proxyLength) + $byPassListLength = [System.BitConverter]::ToInt32($bytes, $proxyStartLocation + $proxyLength) + + if ($byPassListLength -gt 0) { + $byPassList = [System.Text.Encoding]::UTF8.GetString($bytes, $byteLength + $proxyStartLocation + $proxyLength, $byPassListLength) + } } + } catch { + Write-Verbose "Failed to properly get HTTP Proxy information. Inner Exception: $_" } } From ddbbb6da14aef60c084c12f5f5cdd56748c3596c Mon Sep 17 00:00:00 2001 From: David Paulson Date: Tue, 16 Jan 2024 16:18:48 -0600 Subject: [PATCH 4/7] Don't show mismatch if port 80 is used on proxy --- .../HealthChecker/Analyzer/Invoke-AnalyzerOsInformation.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerOsInformation.ps1 b/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerOsInformation.ps1 index e49fd53613..2b9f3680d0 100644 --- a/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerOsInformation.ps1 +++ b/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerOsInformation.ps1 @@ -332,7 +332,9 @@ function Invoke-AnalyzerOsInformation { if (($osInformation.NetworkInformation.HttpProxy.ProxyAddress -ne "None") -and ($exchangeInformation.GetExchangeServer.IsEdgeServer -eq $false) -and - ($osInformation.NetworkInformation.HttpProxy.ProxyAddress -ne $exchangeInformation.GetExchangeServer.InternetWebProxy.Authority)) { + ($null -ne $exchangeInformation.GetExchangeServer.InternetWebProxy) -and + ($osInformation.NetworkInformation.HttpProxy.ProxyAddress -ne + "$($exchangeInformation.GetExchangeServer.InternetWebProxy.Host):$($exchangeInformation.GetExchangeServer.InternetWebProxy.Port)")) { $params = $baseParams + @{ Details = "Error: Exchange Internet Web Proxy doesn't match OS Web Proxy." DisplayWriteType = "Red" From 034214cc2525258974956e93032b72cfd27851fc Mon Sep 17 00:00:00 2001 From: David Paulson Date: Wed, 17 Jan 2024 20:25:11 -0600 Subject: [PATCH 5/7] Check for correct EWS InternalNLBBypassUrl on BE --- .../Invoke-AnalyzerExchangeInformation.ps1 | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerExchangeInformation.ps1 b/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerExchangeInformation.ps1 index bf1d043b9b..b1162a0225 100644 --- a/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerExchangeInformation.ps1 +++ b/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerExchangeInformation.ps1 @@ -23,6 +23,8 @@ function Invoke-AnalyzerExchangeInformation { $hardwareInformation = $HealthServerObject.HardwareInformation $getWebServicesVirtualDirectory = $exchangeInformation.VirtualDirectories.GetWebServicesVirtualDirectory | Where-Object { $_.Name -eq "EWS (Default Web Site)" } + $getWebServicesVirtualDirectoryBE = $exchangeInformation.VirtualDirectories.GetWebServicesVirtualDirectory | + Where-Object { $_.Name -eq "EWS (Exchange Back End)" } $baseParams = @{ AnalyzedInformation = $AnalyzeResults @@ -307,6 +309,23 @@ function Invoke-AnalyzerExchangeInformation { Add-AnalyzedResultInformation @params } + if ($null -ne $getWebServicesVirtualDirectoryBE -and + $null -ne $getWebServicesVirtualDirectoryBE.InternalNLBBypassUrl) { + Write-Verbose "Checking EWS Internal NLB Bypass URL for the BE" + $expectedValue = "https://$($exchangeInformation.GetExchangeServer.Fqdn.ToString()):444/ews/exchange.asmx" + + if ($getWebServicesVirtualDirectoryBE.InternalNLBBypassUrl.ToString() -ne $expectedValue) { + $params = $baseParams + @{ + Name = "EWS Internal Bypass URL Incorrectly Set on BE" + Details = "Error: '$expectedValue' is the expected value for this." + + "`r`n`t`tAnything other than the expected value, will result in connectivity issues." + DisplayWriteType = "Red" + } + + Add-AnalyzedResultInformation @params + } + } + Write-Verbose "Working on results from Test-ServiceHealth" $servicesNotRunning = $exchangeInformation.ExchangeServicesNotRunning From e78dbbda28c857da65b16a8273f9b5a4c518f415 Mon Sep 17 00:00:00 2001 From: David Paulson Date: Wed, 17 Jan 2024 12:53:28 -0600 Subject: [PATCH 6/7] Add Exchange IIS Information docs --- .../HealthChecker/IISInformation.md | 40 +++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 41 insertions(+) create mode 100644 docs/Diagnostics/HealthChecker/IISInformation.md diff --git a/docs/Diagnostics/HealthChecker/IISInformation.md b/docs/Diagnostics/HealthChecker/IISInformation.md new file mode 100644 index 0000000000..d1640e61a6 --- /dev/null +++ b/docs/Diagnostics/HealthChecker/IISInformation.md @@ -0,0 +1,40 @@ +# Exchange IIS Information + +## Description + +We show some general information about your Exchange Server from the IIS perspective. This goes into detail to make sure that Sites and App Pools are started, which might not be easy to spot at a quick look a the server. It will also call out some common misconfiguration issues, that will cause problems with client connectivity. + + +## Sites + +This provides the sites that we found and the following information: + +- State (Started or Stopped) +- HSTS Enabled (Only supported on `Default Web Site`) +- Protocol - Binding - Certificate ( Which protocol is binding to which port and with what certificate if any) + +**NOTE:** HSTS if enabled on the Back End will call out an issue. + +## App Pools + +This provides the application pools on the server with the following information: + +- State (Started or Stopped) +- GCServerEnabled (Garbage Collection Server Enabled - Depends on the RAM on the server if this should be enabled or not on the server. If it should be, Health Checker should call it out.) +- RestartConditionSet ( If there is an IIS setting that will automatically restart the App Pool. This is not recommended and will cause issues with client connectivity ) + +## Virtual Directory Locations + +This provides the different locations that you use for different connection endpoints with the following information: + +- Extended Protection ( The current value ) +- Ssl Flags ( If enabled and/or Cert based ) +- IP Filtering Enabled ( If any IP filtering is enabled ) +- URL Rewrite ( Names of each rule applied at the location ) +- Authentication ( Provides each type of authentication that is enabled for the location. If anonymous `default setting` will be provided if that is enabled Out of the Box on the server ) + +**NOTE:** For each of the URL Rewrite rules, we will display additional information about the rule to let you know what it is doing. It is also recommended to remove any mitigation rules that you might have applied if you have the security fix installed on the server. + +### Included in HTML Report? + +Yes diff --git a/mkdocs.yml b/mkdocs.yml index 69eac08311..c1b7eb746c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -63,6 +63,7 @@ nav: - DownloadDomainCheck: Diagnostics/HealthChecker/DownloadDomainCheck.md - OpenRelayDomainCheck: Diagnostics/HealthChecker/OpenRelayDomain.md - FIPFSCheck: Diagnostics/HealthChecker/FIPFSCheck.md + - IISInformation: Diagnostics/HealthChecker/IISInformation.md - IISWebConfigCheck: Diagnostics/HealthChecker/IISWebConfigCheck.md - HCScheduledTask: Diagnostics/HealthChecker/RunHCViaSchedTask.md - ADSiteCount: Diagnostics/HealthChecker/ADSiteCount.md From 188d12bc25421315b26ccf20909fdebfe098277e Mon Sep 17 00:00:00 2001 From: David Paulson Date: Wed, 17 Jan 2024 13:33:43 -0600 Subject: [PATCH 7/7] Add Exchange IIS Information to HTML Report --- .../Invoke-AnalyzerIISInformation.ps1 | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerIISInformation.ps1 b/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerIISInformation.ps1 index 441b41ac37..11ad4b3b62 100644 --- a/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerIISInformation.ps1 +++ b/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerIISInformation.ps1 @@ -112,12 +112,13 @@ function Invoke-AnalyzerIISInformation { $sbStarted = { param($o, $p) if ($p -eq "State") { if ($o."$p" -eq "Started") { "Green" } else { "Red" } } } $params = $baseParams + @{ - OutColumns = ([PSCustomObject]@{ + OutColumns = ([PSCustomObject]@{ DisplayObject = $outputObjectDisplayValue ColorizerFunctions = @($sbStarted) IndentSpaces = 8 }) - AddHtmlDetailRow = $false + OutColumnsColorTests = @($sbStarted) + HtmlName = "IIS Sites Information" } Add-AnalyzedResultInformation @params @@ -270,12 +271,13 @@ function Invoke-AnalyzerIISInformation { $sbRestart = { param($o, $p) if ($p -eq "RestartConditionSet") { if ($o."$p") { "Red" } else { "Green" } } } $params = $baseParams + @{ - OutColumns = ([PSCustomObject]@{ + OutColumns = ([PSCustomObject]@{ DisplayObject = $outputObjectDisplayValue ColorizerFunctions = @($sbStarted, $sbRestart) IndentSpaces = 8 }) - AddHtmlDetailRow = $false + OutColumnsColorTests = @($sbStarted, $sbRestart) + HtmlName = "Application Pool Information" } Add-AnalyzedResultInformation @params @@ -319,19 +321,19 @@ function Invoke-AnalyzerIISInformation { } $params = $baseParams + @{ - OutColumns = ([PSCustomObject]@{ + OutColumns = ([PSCustomObject]@{ DisplayObject = $outputObjectDisplayValue ColorizerFunctions = @($sbColorizer) IndentSpaces = 8 }) - AddHtmlDetailRow = $false + OutColumnsColorTests = @($sbColorizer) + HtmlName = "Application Pools Restarts" } Add-AnalyzedResultInformation @params $params = $baseParams + @{ Details = "Error: The above app pools currently have the periodic restarts set. This restart will cause disruption to end users." DisplayWriteType = "Red" - AddHtmlDetailRow = $false } Add-AnalyzedResultInformation @params } @@ -429,11 +431,11 @@ function Invoke-AnalyzerIISInformation { } $params = $baseParams + @{ - OutColumns = ([PSCustomObject]@{ + OutColumns = ([PSCustomObject]@{ DisplayObject = $iisVirtualDirectoriesDisplay IndentSpaces = 8 }) - AddHtmlDetailRow = $false + HtmlName = "Virtual Directory Locations" } Add-AnalyzedResultInformation @params