Accessing Configuration Sections Using Microsoft.Web.Administration (MWA)

by Saad Ladki

Abstract

This article shows how configuration sections in the IIS configuration files (applicationHost.config and web.config) can be accessed programmatically using the Microsoft.Web.Administration APIs. It deals with more complex sections that have nested elements and collections. There are examples using the real IIS configuration sections defined in the IIS_schema.xml file. This article show how these sections can be accessed and manipulated in a generic way using the base classes provided by Microsoft.Web.Administration and also by using strongly-typed classes to represent these sections.

Introduction

This article provides examples of programmatic access to the IIS configuration sections using the Microsoft.Web.Administration APIs. This deals with sections that have nested elements (system.webServer/asp) and collections (system.webServer/security/isapiCgiRestriction). The first section shows how these sections can be manipulated using the generic base classes provided by Microsoft.Web.Administration and the second section gives examples of how strongly typed wrapper classes representing these sections are used.

Schema for Sections with Nested Elements and Collections

Sections with nested elements

An example of a section that has nested elements is the system.webServer/asp section. The "session" element is defined within the "asp" section. The schema for this section is as shown below. There are other properties in the schema and other nested elements which are not shown in the example.

<sectionSchema name="system.webServer/asp"> 
  <attribute name="appAllowClientDebug" type="bool" defaultValue="false" /> 
  <attribute name="appAllowDebugging" type="bool" defaultValue="false" /> 
  <!-- Omitted to simplify --> 
  <element name="session"> 
    <attribute name="allowSessionState" type="bool" defaultValue="true" /> 
    <attribute name="keepSessionIdSecure" type="bool" defaultValue="true" /> 
  </element> 
</sectionSchema>

Sections with a collection

A section having a collection of elements indexed by a key is the system.webServer/security/isapiCgiRestriction section. The schema for the section is as shown.

<sectionSchema name="system.webServer/security/isapiCgiRestriction"> 
  <collection addElement="add" clearElement="clear" removeElement="remove"> 
    <attribute name="path" type="string" expanded="true" required="true" isUniqueKey="true" validationType="nonEmptyString" /> 
      <attribute name="allowed" type="bool" required="true" defaultValue="false" /> 
      <attribute name="groupId" type="string" required="false" /> 
      <attribute name="description" type="string" /> 
  </collection> 
  <attribute name="notListedIsapisAllowed" type="bool" defaultValue="false" /> 
  <attribute name="notListedCgisAllowed" type="bool" defaultValue="false" /> 
</sectionSchema>

Generic Access to the Sections

Microsoft.Web.Administration provides several generic base classes which represents different components in the configuration system.

  • Configuration: represents a single configuration file (applicationHost.config or the web.config files for sites and applications)
  • ConfigurationElement: a generic entity used to represent an element in a configuration file. This is the base class for configuration sections, collection entries, nested elements in a section, etc.
  • ConfigurationAttribute: represents a property within a ConfigurationElement
  • ConfigurationSection: derives from ConfigurationElement and represents a section defined in the IIS schema files. Used to access the various properties of a section.
  • ConfigurationElementCollection: A collection class comprising of ConfigurationElements. Also derives from ConfigurationElement.

The example below shows how the nested elements can be accessed in the system.webServer/asp section.

static void Main(string[] args) {  
    ServerManager serverManager = new ServerManager();  
    Configuration config = serverManager.GetApplicationHostConfiguration();  
          ConfigurationSection section = config.GetSection("system.webServer/asp");  
          ConfigurationElement element = section.GetChildElement("session"); 
  Console.Write("allowSessionState attribute value: ");  
          Console.WriteLine(element.GetAttributeValue("allowSessionState"));  
          Console.WriteLine("Set allowSessionState value to false"); 
  element.SetAttributeValue("allowSessionState", false); 
  Console.Write("allowSessionState attribute value: ");  
          Console.WriteLine(element.GetAttributeValue("allowSessionState"));  
  serverManager.CommitChanges();  
    }

The ServerManager class is the entry point to access the various server settings. We set the properties in the applicationHost.config files, but these properties can also be accessed and changed in the web.config files of individual sites and applications. The CommitChanges call saves the changed settings.

The next example shows how to iterate through elements in a collection and add elements to it.

static void Main(string[] args) {         
          ServerManager serverManager = new ServerManager();     
  Configuration config = serverManager.GetApplicationHostConfiguration();         
ConfigurationSection section = 
      config.GetSection("system.webServer/security/isapiCgiRestriction");
    ConfigurationElementCollection collection = section.GetCollection(); 

      // Iterating through the elements in the collection foreach (ConfigurationElement element in collection) 
 { 
  Console.Write("Path: " + element.GetAttributeValue("path")); 
Console.WriteLine(" Allowed: " + element.GetAttributeValue("allowed")); 
        } 
       // Adding a new element to the collection        ConfigurationElement newElement = collection.CreateElement(); 
      newElement.SetAttributeValue("path", @"%SystemDir%\someDll.dll"); 
      newElement.SetAttributeValue("allowed", false); 
      collection.Add(newElement); 
      serverManager.CommitChanges();             
    }

Strongly Typed Classes to Access the Sections

Strongly typed classes derived from the generic base classes can be written in order to make accessing properties easier and more intuitive. This is especially useful when you might have to access the same section many times for different operations. These classes also ensure that you have compile time checking for property types. They avoid the tedious calls to methods like GetChildElement and GetAttributeValue.

The example below shows the wrapper for the system.webServer/asp section and the "session" element for that section. All the properties in that section are not included in this snippet.

public sealed class AspSection : ConfigurationSection { 
   private AspSession _session; 
    public AspSection() 
    { 
    } 

    public AspSession Session 
    {          
        get 
    { 
        if (_session == null) 
         {             
              _session = (AspSession)GetChildElement("session", 
typeof(AspSession)); 
         } 
       return _session; 
    }         
 }         
   } 

    public sealed class AspSession : ConfigurationElement { 
    public AspSession() 
    { 
    } 
     public bool AllowSessionState 
     { 
        get { 
       return (bool)base["allowSessionState"]; 
          } 
         set 
    { 
           base["allowSessionState"] = value; 
         } 
      } 
    }

The GetChildElement call in the above example is used to access a nested element in a configuration element (a section, collection element etc.). Calling base[...] wraps the GetAttributeValue and SetAttributeValue classes which retrieves and sets the values for those attributes.

The next example shows how the properties in a nested element can be accessed in a strongly typed manner.

static void Main(string[] args) { 
    ServerManager serverManager = new ServerManager(); 
    Configuration config = serverManager.GetApplicationHostConfiguration(); 
    AspSection section = (AspSection)config.GetSection("system.webServer/asp", 
        typeof(AspSection)); 
    Console.WriteLine(section.Session.AllowSessionState); 
    section.Session.AllowSessionState = false; 
    serverManager.CommitChanges(); 
}

Observe the call to config.GetSection which takes a section path and also the type of section you want to create. By default calls to config.GetSection create a ConfigurationSection and in order to get an instance of a strongly typed wrapper you need to pass in a type.

Here is an example of how to create strongly typed classes for a section which has a collection. This example uses the system.webServer/security/isapiCgiRestriction section. This snippet does not include all the properties that are present on this section.

public sealed class IsapiCgiRestrictionSection : ConfigurationSection {  
    private IsapiCgiRestrictionCollection _collection;             
    public IsapiCgiRestrictionSection() { 
      }         
    public IsapiCgiRestrictionCollection IsapiCgiRestrictions { 
        get { 
          if (_collection == null) { 
            _collection = 
              (IsapiCgiRestrictionCollection)GetCollection(typeof(IsapiCgiRestrictionCollection)); 
          } 
        return _collection; 
        } 
      } 
    }

The GetCollection method of the base class (ConfigurationElement) is used to access the default collection for that element.

The next example shows the strongly typed classes for the collection itself and the elements in the collection.

public sealed class IsapiCgiRestrictionCollection : ConfigurationElementCollectionBase<IsapiCgiRestrictionElement> { 
   public IsapiCgiRestrictionCollection() {  
      } 
   public new IsapiCgiRestrictionElement this[string path] {  
        get { 
          for (int i = 0; i< Count; i++) { 
            IsapiCgiRestrictionElement restriction = base[i]; 
            if (String.Equals=(Environment.ExpandEnvironmentVariables(restriction.Path),  
              Environment.ExpandEnvironmentVariables(path), StringComparison.OrdinalIgnoreCase)) {  
              return restriction; 
            } 
          } 
        return null; 
      } 
    } 
    public IsapiCgiRestrictionElement Add(string path, bool allowed) { 
        IsapiCgiRestrictionElement element = CreateElement(); 
        element.Path = path; 
        element.Allowed = allowed; 
        return Add(element); 
    } 
    protected override IsapiCgiRestrictionElement CreateNewElement(string elementTagName) { 
        return new IsapiCgiRestrictionElement(); 
      } 
    } 
    public sealed class IsapiCgiRestrictionElement : ConfigurationElement { 
  public IsapiCgiRestrictionElement() { 
      } 
     public bool Allowed { 
      get { 
        return (bool)base["allowed"]; 
      } 
      set { 
        base["allowed"] = value; 
      } 
    }         
     public string Path { 
      get { 
        return (string)base["path"]; 
      } 
      set { 
        base["path"] = value; 
      } 
     } 
   }

For the strongly typed collection class it is very useful to be able to index into the collection using the collection key, which in this case is the "path" attribute. The parameter to the Add method takes the required properties for that element. In this case the Add could have only taken the "path" attribute and then the "allowed" attribute would then have its default value.

Here is an example using these strongly typed classes to iterate through the entries in a collection and to add an element to it.

static void Main(string[] args) { 
  ServerManager serverManager = new ServerManager();             
  Configuration config = serverManager.GetApplicationHostConfiguration(); 
  IsapiCgiRestrictionSection section =  
    (IsapiCgiRestrictionSection)config.GetSection("system.webServer/security/isapiCgiRestriction", typeof(IsapiCgiRestrictionSection));
  // Iterating through the elements in the collection 
  foreach (IsapiCgiRestrictionElement element in section.IsapiCgiRestrictions) { 
    Console.Write("Path: " + element.Path); 
    Console.WriteLine(" Allowed: " + element.Allowed); 
  } 
  // Adding a new element to the collection 
  section.IsapiCgiRestrictions.Add(@"%SystemDir%\someDll.dll", false); 
  serverManager.CommitChanges(); 
}