Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 75 additions & 26 deletions Diagnostics/HealthChecker/Analyzer/Get-URLRewriteRule.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@

<#
.SYNOPSIS
Pulls out URL Rewrite Rules from the web.config and applicationHost.config file to return a Hashtable of those settings.
Pulls out URL Rewrite Rules from the web.config and applicationHost.config file to return inbound and outbound rules.
.DESCRIPTION
This is a function that is designed to pull out the URL Rewrite Rules that are set on a location of IIS.
It extracts both inbound rules (from rewrite/rules) and outbound rules (from rewrite/outboundRules).
Because you can set it on an individual web.config file or the parent site(s), or the ApplicationHostConfig file for the location
We need to check all locations to properly determine what is all set.
The ApplicationHostConfig file must be able to be converted to Xml, but the web.config file doesn't.
Expand All @@ -14,11 +15,12 @@
2. ApplicationHost.config file for the same location
3. Then move up one level (Default Web Site/mapi -> Default Web Site) and repeat 1 and 2 till no more locations.
a. If the 'clear' flag was set at any point, we stop at that location in the process.
b. Inbound and outbound rules track the 'clear' flag independently.
4. Then there is a global setting in the ApplicationHost.config file.
#>
function Get-URLRewriteRule {
[CmdletBinding()]
[OutputType([hashtable])]
[OutputType([PSCustomObject])]
param(
[Parameter(Mandatory = $true)]
[System.Xml.XmlNode]$ApplicationHostConfig,
Expand All @@ -31,51 +33,82 @@ function Get-URLRewriteRule {
begin {
Write-Verbose "Calling: $($MyInvocation.MyCommand)"
$urlRewriteRules = @{}
$urlOutboundRewriteRules = @{}
$appHostConfigLocations = $ApplicationHostConfig.configuration.Location.path
}
process {
foreach ($key in $WebConfigContent.Keys) {
Write-Verbose "Working on key: $key"
$continue = $true
$clear = $false
$clearInbound = $false
$clearOutbound = $false
$currentKey = $key
$urlRewriteRules.Add($key, (New-Object System.Collections.Generic.List[object]))
$urlOutboundRewriteRules.Add($key, (New-Object System.Collections.Generic.List[object]))

do {
Write-Verbose "Working on currentKey: $currentKey"
try {
# the Web.config is looked at first
[xml]$content = $WebConfigContent[$currentKey]
$rules = $content.configuration.'system.webServer'.rewrite.rules

if ($null -ne $rules) {
$clear = $null -ne $rules.clear
$urlRewriteRules[$key].Add($rules)
} else {
Write-Verbose "No rewrite rules in the config file"
if (-not $clearInbound) {
$rules = $content.configuration.'system.webServer'.rewrite.rules

if ($null -ne $rules) {
$clearInbound = $null -ne $rules.clear
$urlRewriteRules[$key].Add($rules)
} else {
Write-Verbose "No inbound rewrite rules in the config file"
}
}

if (-not $clearOutbound) {
$outboundRules = $content.configuration.'system.webServer'.rewrite.outboundRules

if ($null -ne $outboundRules) {
$clearOutbound = $null -ne $outboundRules.clear
$urlOutboundRewriteRules[$key].Add($outboundRules)
} else {
Write-Verbose "No outbound rewrite rules in the config file"
}
}
} catch {
Write-Verbose "Failed to convert to xml"
Invoke-CatchActions
}

if (-not $clear) {
if (-not $clearInbound -or -not $clearOutbound) {
# Now need to look at the applicationHost.config file to determine what is set at that location.
# need to do this because of the case sensitive query to get the xmlNode
Write-Verbose "clear not set on config. Looking at the applicationHost.config file"
Write-Verbose "Looking at the applicationHost.config file"
$appKey = $appHostConfigLocations | Where-Object { $_ -eq $currentKey }

if ($appKey.Count -eq 1) {
$location = $ApplicationHostConfig.SelectNodes("/configuration/location[@path = '$appKey']")

if ($null -ne $location) {
$rules = $location.'system.webServer'.rewrite.rules

if ($null -ne $rules) {
$clear = $null -ne $rules.clear
$urlRewriteRules[$key].Add($rules)
} else {
Write-Verbose 'No rewrite rules in the applicationHost.config file'
if (-not $clearInbound) {
$rules = $location.'system.webServer'.rewrite.rules

if ($null -ne $rules) {
$clearInbound = $null -ne $rules.clear
$urlRewriteRules[$key].Add($rules)
} else {
Write-Verbose "No inbound rewrite rules in the applicationHost.config file"
}
}

if (-not $clearOutbound) {
$outboundRules = $location.'system.webServer'.rewrite.outboundRules

if ($null -ne $outboundRules) {
$clearOutbound = $null -ne $outboundRules.clear
$urlOutboundRewriteRules[$key].Add($outboundRules)
} else {
Write-Verbose "No outbound rewrite rules in the applicationHost.config file"
}
}
} else {
Write-Verbose "We didn't find the location for '$appKey' in the applicationHostConfig. This shouldn't occur."
Expand All @@ -85,21 +118,34 @@ function Get-URLRewriteRule {
}
}

if ($clear) {
Write-Verbose "Clear was set, don't need to know what else was set."
if ($clearInbound -and $clearOutbound) {
Write-Verbose "Clear was set for both inbound and outbound, don't need to know what else was set."
$continue = $false
} else {
$index = $currentKey.LastIndexOf("/")

if ($index -eq -1) {
$continue = $false
# look at the global configuration of the applicationHost.config file
$rules = $ApplicationHostConfig.configuration.'system.webServer'.rewrite.rules

if ($null -ne $rules) {
$urlRewriteRules[$key].Add($rules)
} else {
Write-Verbose "No global configuration for rewrite rules."
if (-not $clearInbound) {
# look at the global configuration of the applicationHost.config file
$rules = $ApplicationHostConfig.configuration.'system.webServer'.rewrite.rules

if ($null -ne $rules) {
$urlRewriteRules[$key].Add($rules)
} else {
Write-Verbose "No global configuration for inbound rewrite rules."
}
}

if (-not $clearOutbound) {
$outboundRules = $ApplicationHostConfig.configuration.'system.webServer'.rewrite.outboundRules

if ($null -ne $outboundRules) {
$urlOutboundRewriteRules[$key].Add($outboundRules)
} else {
Write-Verbose "No global configuration for outbound rewrite rules."
}
}
} else {
$currentKey = $currentKey.Substring(0, $index)
Expand All @@ -111,6 +157,9 @@ function Get-URLRewriteRule {
}
}
end {
return $urlRewriteRules
return [PSCustomObject]@{
Inbound = $urlRewriteRules
Outbound = $urlOutboundRewriteRules
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -416,10 +416,12 @@ function Invoke-AnalyzerIISInformation {
WebConfigContent = $iisWebConfigContent
}

$urlRewriteRules = $null
$urlRewriteResult = $null
$ipFilterSettings = $null
$authTypeSettings = $null
Get-URLRewriteRule @ruleParams | Invoke-RemotePipelineHandler -Result ([ref]$urlRewriteRules)
Get-URLRewriteRule @ruleParams | Invoke-RemotePipelineHandler -Result ([ref]$urlRewriteResult)
$urlRewriteRules = $urlRewriteResult.Inbound
$urlOutboundRewriteRules = $urlRewriteResult.Outbound
Get-IPFilterSetting -ApplicationHostConfig ([xml]$applicationHostConfig) | Invoke-RemotePipelineHandler -Result ([ref]$ipFilterSettings)
Get-IISAuthenticationType -ApplicationHostConfig ([xml]$applicationHostConfig) | Invoke-RemotePipelineHandler -Result ([ref]$authTypeSettings)
$failedLocationsForAuth = @()
Expand All @@ -440,6 +442,7 @@ function Invoke-AnalyzerIISInformation {
$epValue = "None"
$ep = $extendedProtectionConfiguration | Where-Object { $_.VirtualDirectoryName -eq $location.Path }
$currentRewriteRules = $urlRewriteRules[$location.Path]
$currentOutboundRewriteRules = $urlOutboundRewriteRules[$location.Path]
$authentication = $authTypeSettings[$location.Path]

if ($currentRewriteRules.Count -ne 0) {
Expand All @@ -455,7 +458,23 @@ function Invoke-AnalyzerIISInformation {
}

$displayRewriteRules = ($currentRewriteRules.rule | Where-Object { $_.enabled -ne "false" }).name |
Where-Object { $_ -notcontains $excludeRules }
Where-Object { $_ -notin $excludeRules }
}

$displayOutboundRewriteRules = [string]::Empty

if ($currentOutboundRewriteRules.Count -ne 0) {
$excludeOutboundRules = @()
foreach ($rule in $currentOutboundRewriteRules) {
$remove = $rule.Remove

if ($null -ne $remove) {
$excludeOutboundRules += $remove.Name
}
}

$displayOutboundRewriteRules = ($currentOutboundRewriteRules.rule | Where-Object { $_.enabled -ne "false" }).name |
Where-Object { $_ -notin $excludeOutboundRules }
}

if ($null -ne $ep) {
Expand Down Expand Up @@ -484,7 +503,8 @@ function Invoke-AnalyzerIISInformation {
ExtendedProtection = $epValue
SslFlags = $sslFlag
IPFilteringEnabled = $ipFilterEnabled
URLRewrite = $displayRewriteRules
InURLRewrite = $displayRewriteRules
OutURLRewrite = $displayOutboundRewriteRules
Authentication = $authentication
})
}
Expand Down Expand Up @@ -635,6 +655,7 @@ function Invoke-AnalyzerIISInformation {
IndentSpaces = 8
})
AddHtmlDetailRow = $false
TestingName = "Inbound URL Rewrite Rules"
}
Add-AnalyzedResultInformation @params

Expand All @@ -651,6 +672,68 @@ function Invoke-AnalyzerIISInformation {
}
}

# Display Outbound URL Rewrite Rules.
# Same deduplication pattern as inbound - don't display rules on multiple vDirs by same name.
$alreadyDisplayedOutboundRules = @{}
$outboundDisplayKey = "DisplayKey"
$alreadyDisplayedOutboundRules.Add($outboundDisplayKey, (New-Object System.Collections.Generic.List[object]))

foreach ($key in $urlOutboundRewriteRules.Keys) {
$currentSection = $urlOutboundRewriteRules[$key]

if ($currentSection.Count -ne 0) {
foreach ($rule in $currentSection.rule) {

if ($null -eq $rule) {
Write-Verbose "Outbound rule is NULL skipping."
continue
} elseif ($rule.enabled -eq "false") {
Write-Verbose "skipping over disabled outbound rule: $($rule.Name) for vDir '$key'"
continue
}

$displayObject = [PSCustomObject]@{
RewriteRuleName = $rule.name
ServerVariable = $rule.match.serverVariable
MatchPattern = $rule.match.pattern
PreCondition = $rule.preCondition
ActionType = $rule.action.type
}

if (-not ($alreadyDisplayedOutboundRules.ContainsKey((($displayObject.RewriteRuleName))))) {
$alreadyDisplayedOutboundRules.Add($displayObject.RewriteRuleName, $displayObject)
$alreadyDisplayedOutboundRules[$outboundDisplayKey].Add($displayObject)
}
}
}
}

if ($alreadyDisplayedOutboundRules[$outboundDisplayKey].Count -gt 0) {
$params = $baseParams + @{
OutColumns = ([PSCustomObject]@{
DisplayObject = $alreadyDisplayedOutboundRules[$outboundDisplayKey]
IndentSpaces = 8
})
AddHtmlDetailRow = $false
TestingName = "Outbound URL Rewrite Rules"
}
Add-AnalyzedResultInformation @params
}

$globalRules = ([xml]$applicationHostConfig).configuration.'system.webServer'.rewrite.globalRules

if ($null -ne $globalRules -and
$null -ne $globalRules.rule) {
$params = $baseParams + @{
Name = "Global IIS Rewrite Rules Detected"
Details = "Global URL Rewrite rules are defined in applicationHost.config and apply to all sites on this server." +
"`r`n`t`tReview these rules to ensure they are expected and not interfering with Exchange traffic."
DisplayWriteType = "Yellow"
TestingName = "Global IIS Rewrite Rules"
}
Add-AnalyzedResultInformation @params
}

foreach ($webApp in $iisWebApplications) {
if ($correctLocations.ContainsKey($webApp.FriendlyName)) {
if ($webApp.PhysicalPath -notlike "*$($correctLocations[$webApp.FriendlyName])") {
Expand Down
Loading