0

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'

Justin
  • 1,303
  • 15
  • 30
  • 2
    If you add lines like `` before adding the items, then you can find your `web.config` works everywhere. It is good manner to expect conflicts and handle them well. – Lex Li Jul 05 '21 at 20:34
  • yes, however it does require getting all the developers onboard and ensuring they do it properly. We have multiple different versions with varying level of compliance so detecting the issues is very important. and in general its a good idea to know if there is a conflict unexpectedly. – Justin Jul 07 '21 at 02:48
  • Probably they thought that you should have accepted your own answer to finish the discussion. – Lex Li Jul 21 '21 at 17:19

1 Answers1

2

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                                                                                                                                            
Justin
  • 1,303
  • 15
  • 30