Accessing iis 7.0 features programmatically from configuration file(S) (C#)

IIS 7.0 has great features for developers now. It is integrated with .Net. UI has completely changed from the previous versions. It has entirely a new look and feel. I feel there would be a steep learning curve for IIS users, but nothing to worry, unlike previous versions this time we have done our homework well. www.iis.net has lot of important material on IIS 7.0 should a user want to know. Our IIS developers/managers have blogged extensively on various aspects of the new features coming up with IIS 7.0. Time has really changed now when it comes to IIS :-)

To continue with where I left earlier, I will show how extensively you can play with IIS features programmatically, which was really difficult with earlier versions. Now you have .Net, WMI scripts with which you can code in C++, C#, VB Script for example.

I will use C# here and take advantage of the new API for accessing IIS configurations.

In my demo, I will dig into the ApplicationHost.config file through code to find out information of our interest. Here I will get information about application pool settings etc and later on default document settings for a specific web application.

My aim is not to have a very reliable and error free code. I haven't handled any exceptions etc. that may arise. The objective here is how you can dig deep to any level in the config file to get information on various features related to IIS, your web site or application.

  
     using (ServerManager localServer = new ServerManager())
                 {
                     Configuration config = localServer.GetApplicationHostConfiguration();
  
                     // Get or Set Application Pool settings on the IIS 7.0 server
  
                     // Here we look for ApplicationPools section in the config file. 
  
                     // IIS_Schema.xml is the file that contains the schema for all the settings we find in ApplicationHost.config file.
  
                     ConfigurationSection appPoolConfigSection = config.GetSection("system.applicationHost/applicationPools");
  
                    ConfigurationElementCollection appPoolCollection = appPoolConfigSection.GetCollection();
  
                    //Since there is a collection "add" within system.applicationHost/applicationPools section, 
  
                    // we will reiterate through few of its attributes and child elements only.
  
                    Console.WriteLine("Application Pool settings");
  
                    foreach (ConfigurationElement element in appPoolCollection) // Iterates through all the Application Pools.
  
                    {
                        Console.WriteLine("=========================");
  
                        Console.WriteLine("Name: " + element.GetAttributeValue("name").ToString());
  
                        Console.WriteLine("Pipleline Mode: " + element.GetAttributeValue("managedPipelineMode").ToString());
  
                        //'processModel' is a child element within the collection 'add'
  
                        ConfigurationElement processModel = element.GetChildElement("processModel");
  
                        Console.WriteLine("Process Model: " + processModel.GetAttributeValue("identityType"));
  
                        Console.WriteLine("App pool Identity: " + processModel.GetAttributeValue("userName"));
  
                        Console.WriteLine("\n\n");
  
                        ConfigurationElement recycling = element.GetChildElement("recycling");
  
                        ConfigurationElement periodicRecyclingSettings = recycling.GetChildElement("periodicRestart");
  
                        Console.WriteLine("Periodic Recycling Parameters: ");
  
                        Console.WriteLine("Memory: " + periodicRecyclingSettings.GetAttributeValue("memory"));
  
                        Console.WriteLine("Private Memory: " + periodicRecyclingSettings.GetAttributeValue("privateMemory"));
  
                        Console.WriteLine("Requests: " + periodicRecyclingSettings.GetAttributeValue("requests"));
  
                        Console.WriteLine("Time: " + periodicRecyclingSettings.GetAttributeValue("time"));
  
                    }

If you notice here we can query all the IIS related configurations from the ApplicationHost.config file. If you have some Web site related settings you can query that from the web.config file declared for the web site or else from the Applicationhost.config file (it is set using Location tag, like <Location Path="Default Web site/Virtdir">).
In the above code we queried the Applicationhost.config file to get the settings. If you notice you can dig deep into the hierarchy and get the information related to a specific collection, element, attribute etc. All these tags basically contain some IIS related settings.
The information related to various tags like whether a specific tag is a collection or child element or an attribute etc, are defined in the IIS_Schema.xml file. If you want to use your own custom properties you can create your own schema file and save it to the schema folder. Never modify the IIS_Schema.xml file. Here is a snippet of how IIS_schema.xml file looks like.

 <sectionSchema name="system.applicationHost/applicationPools">
     <collection addElement="add" defaultElement="applicationPoolDefaults">
       <attribute name="name" type="string" required="true" isUniqueKey="true" validationType="applicationPoolName" />
       <attribute name="queueLength" type="uint" defaultValue="1000" validationType="integerRange" validationParameter="10,65535"/>
       <attribute name="autoStart" type="bool" defaultValue="true" />
       <attribute name="enable32BitAppOnWin64" type="bool" defaultValue="false" />
       <attribute name="managedRuntimeVersion" type="string" defaultValue="v2.0" />
       <attribute name="managedPipelineMode" type="enum" defaultValue="Integrated">
         <enum name="Integrated" value="0" />
         <enum name="Classic" value="1" />
       </attribute>
       .....
       <element name="processModel">
         <attribute name="identityType" type="enum" defaultValue="NetworkService">
           <enum name="LocalSystem" value="0"/>
           <enum name="LocalService" value="1"/>
           <enum name="NetworkService" value="2"/>
           <enum name="SpecificUser" value="3"/>
         </attribute>
         <attribute name="userName" type="string"/>
         <attribute name="password" type="string" encrypted="true" defaultValue="[enc:IISWASOnlyRsaProvider::enc]" />
       ........
       ........
       <element name="recycling">
         <attribute name="disallowOverlappingRotation" type="bool" defaultValue="false"/>
         <attribute name="disallowRotationOnConfigChange" type="bool" defaultValue="false"/>
         .......
         .......
         <element name="periodicRestart">
           <attribute name="memory" type="uint" defaultValue="0" />
           <attribute name="privateMemory" type="uint" defaultValue="0" />
           <attribute name="requests" type="uint" defaultValue="0" />
           <attribute name="time" type="timeSpan" defaultValue="29:00:00" validationType="timeSpanRange" validationParameter="0,25920000,60"/>
 .........
 .........         
 </sectionSchema>

 

Now let's list/add/remove one of the values in the existing configuration of our server programmatically. Let's add a file "mypage.html" to the default Document list of my application 'App1' under the Default Web site.
             One thing you may notice is that if you modify the default document setting through IIS mmc UI, there will be a web.config file created in your application folder with the reflected changes.

 using (ServerManager localServer = new ServerManager())
 {
  
     Configuration config = localServer.GetApplicationHostConfiguration();
  
     // IIS_Scema.xml is the file that contains the schema for all the settings we find in 
     // ApplicationHost.config file.
     
  ConfigurationSection defaultDocumentSection = config.GetSection("system.webServer/defaultDocument", "Default Web Site/App1");
  
  // Now since defaultDocument is a section in itself querying or modifying the settings should be only a few lines. 
  // Remember we are not digging inside the hierarchy to get any collection, child, element etc. Its defined as a section itself.
  // here we intend to set the default document for the application App1 under Default Web site.
  
  Console.WriteLine("Default Document is enabled: " + defaultDocumentSection.GetAttributeValue("enabled"));
  ConfigurationElement files = defaultDocumentSection.GetChildElement("files");
  //Since in IIS_Schema, you will notice files is an element within Section "system.webServer/defaultDocument"
  ConfigurationElementCollection filesCollection = files.GetCollection();
  //Since Add, Clear and Remove form a part of the Collection within "files" withinn Section "system.webServer/defaultDocument".
  foreach (ConfigurationElement defaultPage in filesCollection)
      Console.WriteLine(defaultPage.GetAttributeValue("value").ToString());
  
  // Now let's programmatically add a file to the list of default Documents. We can also enable/disable the feature at any site/app level.
 ConfigurationElement newfile = filesCollection.CreateElement();
  newfile.SetAttributeValue("value", "mypage1.html");
  filesCollection.Add(newfile);
  localServer.CommitChanges(); // in order to reflect the changes in the physical ApplicationHost.config file.
 }

Let's remove the file from the list that we added through code.

 //Removing a file from the default Document list
          using (ServerManager localServer = new ServerManager())
          {
  
              Configuration config = localServer.GetApplicationHostConfiguration();
              ConfigurationSection defaultDocumentSection = config.GetSection("system.webServer/defaultDocument", "Default Web Site/App1");
              ConfigurationElement files = defaultDocumentSection.GetChildElement("files");
              ConfigurationElementCollection filesCollection = files.GetCollection();
              foreach (ConfigurationElement file in filesCollection)
              {
                  if (String.Equals(file.GetAttributeValue("value").ToString(), "mypage1.html", StringComparison.OrdinalIgnoreCase))
                  {
                      filesCollection.Remove(file);
                      break; // Since you cannot have duplicate files as Default document. 
                  }
              }
              localServer.CommitChanges();
          }

You should see the following tree hierarchy for this setting in the config file, either at the Application root (as web.config) or in the ApplicationHost.config file with a location tag.

 <system.webServer>
          <defaultDocument>
              <files>
                  <clear />
                  <add value="Default.htm" />
                  <add value="Default.asp" />
                  <add value="index.htm" />
                  <add value="index.html" />
                  <add value="iisstart.htm" />
                  <add value="default.aspx" />
                  <add value="mypage1.html" />
              </files>
          </defaultDocument>
      </system.webServer>

There are a lot of thing that you can do through code in order to manage IIS 7.0.

One can write their own custom module, handler and plug into the IIS pipeline. This module need not be written any more in C++ to act as a native ISAPI. One can write a managed module/handler and plug it to IIS configuration and it will serve both managed and non-managed resources.

This is a big news for .Net based developers!!!