Unfortunately in IIS if you define the same setting in wwwroot\web.config and wwwroot\myapp\web.config, certain types of settings will collide with each other resulting in a 500.19 error.
e.g. permitted verbs:
<security>
<requestFiltering>
<verbs allowUnlisted="false">
<add verb="HEAD" allowed="true" />
<add verb="POST" allowed="true" />
<add verb="GET" allowed="true" />
</verbs>
</requestFiltering>
</security>
Also unfortunately the PowerShell Set-WebConfiguration does not validate this before making the change and once corrupted you cannot remove the bad configuration.
I need a way to validate the configuration before/after a change so i can either roll it back or take action.
I found this solution: https://serverfault.com/questions/708079/is-there-a-cmd-tool-to-check-a-web-config-file-for-validity however it only validates SYNTAX failures or only very major configuration issues.
It does not detect collisions at other filter paths: e.g. Cannot add duplicate collection entry of type 'add' with unique key attribute 'verb' set to 'HEAD'
I found the solution to this is to create a function which reads in the web.config, compiles the list of filters by parsing the xml and then perform a get-webconfiguration for each filter, either the filter will return something, nothing (if no settings to read) or an exception (what we care about)
Code:
function Test-IISWebAppConfigIsValid
{
param (
[Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineBYPropertyName=$true)]
[string]$AppName,
[string]$SiteName='Default Web Site'
)
process
{
$Result = @{
IsValid=$false;
SiteName=$SiteName
AppName=$AppName
}
try
{
$result.Add("FileInfo",(Get-WebConfigFile -PSPath "IIS:\Sites\$SiteName\$AppName"))
$Result.Add("FileExists",$result.FileInfo.Exists)
$result.Add("IsXML",$False)
#load the web.config
[xml]$ConfigXML = $result.FileInfo | Get-Content
$result.IsXML = $true
#find all the elements in the config file
$Elements = $ConfigXML.SelectNodes("//node()[name() != 'add' and name() != 'remove' and name() != 'clear']")
#extract the filters from the xpath by finding all the configured elements
$FilterList = @()
foreach ($el in $Elements)
{
$FilterStack = @()
$tempel = $el
while ($tempel.ParentNode -and $tempel -ne $ConfigXML.DocumentElement -and $tempel -ne $ConfigXML)
{
$name = $tempel.get_name()
if ($tempel.NodeType -eq 'Element')
{
$FilterSTack += $name
}
$tempel = $tempel.ParentNode
}
if ($FilterStack.Count -gt 0) {
[array]::Reverse($FilterStack)
$FilterList += "/"+[string]::Join("/",$FilterStack)
}
}
$Result.Add("FilterList", ($FilterList | Sort-Object -Unique))
#load the configuration for each xpath
if (($result.FilterList | Measure-Object).Count -gt 0) {
Get-WebConfiguration -PSPath "IIS:\Sites\$SiteName\$AppName" -Filter $result.FilterList | Out-Null
}
$result.IsValid=$true
}
catch [System.Exception]
{
$result.Add("Exception",$_.Exception)
}
finally
{
write-output ([PSCustomObject]$result)
}
}#process
}#function Test-IISWebAppConfigIsValid
'myapp1','myapp2' | Test-IISWebAppConfigIsValid |ft -Property AppName,FileExists,IsValid,Exception -AutoSize
output:
AppName FileExists IsValid Exception
------- ---------- ------- ---------
myapp1 True False System.Runtime.InteropServices.COMException (0x800700B7): Filename: \\?\C:\inetpub\wwwroot\myapp1\web.config...
myapp2 True True