Posts
  • CarlosAg Blog

    Search your configuration sections in web.config files using IIS 7.0 API's

    • 1 Comments

    Background

    In IIS 7.0 we have the great functionality to allow you to configure the Web Server settings in a distributed way, including the IIS configuration along with the ASP.NET configuration in the web.config files by using Configuration Sections. For example, the following shows a web.config adding a default document (home.aspx) to a Web Application inside my Default Web Site:

    <configuration>
     
    <system.webServer>
       
    <defaultDocument>
         
    <add value="home.aspx" />
        </
    defaultDocument>
     
    </system.webServer>
     
    <system.web>
       
    <authentication mode="Forms" />
      </
    system.web>
    </configuration>

    Now, that is great but it does come with a price, specially for server administrators it means that now you need to deal with a distributed configuration environment where certain settings are applied at the server level and certain settings are applied along with the application or even folders.

    Another interesting challenge is that given the nature of distributed configuration, we've added the functionality to lock certain configuration sections so that they can only be set by a server administrator. Again this is good, however before the server administrator locks any section in order to prevent breaking applications they should search configuration and see if anyone is using that configuration section underneath.

    Searching Configuration

    The IIS 7.0 configuration system has a not so well-known feature that allows you to "query" the configuration system to get an overview of the configuration files in the system as well as the configuration sections that are used in each of them. This feature is implemented as a magical section called configPaths, that has the following schema:

      <sectionSchema name="configPaths">
       
    <collection addElement="searchResult">
         
    <attribute name="path" type="string" />
          <
    attribute name="locationPath" type="string" />
          <
    attribute name="status" type="uint" />
          <
    collection addElement="section">
           
    <attribute name="name" type="string" isUniqueKey="true" /> 
          </
    collection> 
       
    </collection>
     
    </sectionSchema>

    How to use it

    One thing that is pretty cool is that you can consume this section from all of our tools, including a command line (AppCmd.exe), a script (such as vbscript or javascript using Microsoft.ApplicationHost.AdminManager), or using Managed Code (Microsoft.Web.Administration) including the ability to use it under PowerShell. For this exercise we will only use Microsoft.Web.Administration,  but the concepts are exactly the same in the other tools.

    In its simplest form you can use the following function to display all the configuration files in your server as well as the sections included on them (just add a reference to Windows\System32\Inetsrv\Microsoft.Web.Administration.dll):

        /// <summary>
        /// Displays the list of configuration files as well as the 
        ///     sections being used in each file
        /// </summary>
        private static void DisplaySections() {
           
    using (ServerManager serverManager = new ServerManager()) {
               
    Configuration appHost = serverManager.GetApplicationHostConfiguration();
               
    ConfigurationSection configPaths = appHost.GetSection("configPaths");

               
    foreach (ConfigurationElement configPath in configPaths.GetCollection()) {
                   
    string path = (string)configPath["path"];
                   
    string locationPath = (string)configPath["locationPath"];

                   
    Console.WriteLine();

                   
    Console.WriteLine("Config Path:" + path);
                   
    if (!String.IsNullOrEmpty(locationPath)) {
                       
    Console.WriteLine("  <locationPath path=" + locationPath + "'>");
                   
    }

                   
    foreach (ConfigurationElement section in configPath.GetCollection()) {
                       
    Console.WriteLine("    " + section["name"]);
                   
    }
               
    }
           
    }
       
    }
    An example result of running this is:
    Config Path:MACHINE/WEBROOT/APPHOST
        system.applicationHost/applicationPools
        system.webServer/defaultDocument
        system.webServer/ImageCopyright
        system.applicationHost/sites
        system.webServer/security/authentication/windowsAuthentication
        system.webServer/tracing/traceProviderDefinitions
        ...
    
    Config Path:MACHINE/WEBROOT/APPHOST
      <locationPath="Default Web Site/BlogApp">
        system.webServer/security/authentication/windowsAuthentication
    
    Config Path:MACHINE/WEBROOT/APPHOST
      <locationPath path="Default Web Site/aspnet_client">
        system.webServer/directoryBrowse
        system.webServer/handlers
    
    Config Path:MACHINE/WEBROOT/APPHOST
      <locationPath path="Site2/aspnet_client">
        system.webServer/directoryBrowser
        system.webServer/handlers
        system.webServer/defaultDocument
    
    Config Path:MACHINE/WEBROOT/APPHOST
      <locationPath path="Default Web Site">
        system.webServer/security/authentication/anonymousAuthentication
        system.webServer/directoryBrowse
        system.webServer/security/authentication/windowsAuthentication
    
    Config Path:MACHINE/WEBROOT/APPHOST/Default Web Site
        appSettings
    ...

    This tells us that in ApplicationHost.config we have a lot of sections begin used including applicationPools and many more.

    Now, lets focus on the last two set of entries, the one with "MACHINE/WEBROOT/APPHOST" with locationPath set to "Default Web Site" tells us that anonymousAuthentication was used as well as windowAuthentication. The locationPath basically tells the configuration that even though this is set in ApplicationHost.config this configuration should only be applied to Default Web Site and its children. The next entry with path "MACHINE/WEBROOT/APPHOST/Default Web Site", basiclally tells you that in the Web.config inside the Default Web Site (in other words in c:\inetpub\wwwroot\web.config) the section appSettings is being used.

    Now, what is interesting is that this is walking the entire server to find configuration files and do a lot of processing, however if you already know that you only want to search within a Site, or a particular application, then you can scope it down by using the GetWebConfiguration() method instead and this will give you only the configuration sections that apply for that site or application. Note that this will also report the sections that are specifically set for that object inside ApplicationHost.config making it much more than just a "findstr" inside the site folder and their virtual directories.

        DisplaySectionsForSite("Default Web Site");

       
    private static void DisplaySectionsForSite(string siteName) {
           
    using (ServerManager serverManager = new ServerManager()) {
               
    Configuration appHost = serverManager.GetWebConfiguration(siteName);
               
    ConfigurationSection configPaths = appHost.GetSection("configPaths");

               
    foreach (ConfigurationElement configPath in configPaths.GetCollection()) {
                   
    string path = (string)configPath["path"];
                   
    string locationPath = (string)configPath["locationPath"];

                   
    Console.WriteLine();

                   
    Console.WriteLine("Config Path:" + path);
                   
    if (!String.IsNullOrEmpty(locationPath)) {
                       
    Console.WriteLine("  <locationPath path=" + locationPath + "'>");
                   
    }

                   
    foreach (ConfigurationElement section in configPath.GetCollection()) {
                       
    Console.WriteLine("    " + section["name"]);
                   
    }
               
    }
           
    }
       
    } 

    PowerShell

    Now, lets look at other examples, lets consider that we are a server administrator and I want to lock the defaultDocument section, but as a good citizen I first want to see if I would be breaking any application in my entire server if I do this. Just for fun lets do this using PowerShell instead, to test this just copy the entire code below and paste in inside an elevated PowerShell window.

    $sectionToLookFor = "*defaultdocument*"

    [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Web.Administration")
    $iis = new-object Microsoft.Web.Administration.ServerManager

    $configPaths = $iis.GetApplicationHostConfiguration().GetSection("configPaths").GetCollection()

    foreach($path in $configPaths.GetCollection()) {
     
    foreach($section in $path.GetCollection()) {
       
    if ($section.GetAttributeValue("name") -like $sectionToLookFor) {
         
    write ($path.GetAttributeValue("path") + " LocationPath:" + $path.GetAttributeValue("locationPath"))
         
    write ("    " + $section.GetAttributeValue("name"))
       
    }
     
    }
    }

    The result in my machine gives you something like follows:

    MACHINE/WEBROOT/APPHOST
        system.webServer/defaultDocument
    MACHINE/WEBROOT/APPHOST/Default Web Site/aspnet_client
        system.webServer/defaultDocument
    MACHINE/WEBROOT/APPHOST/Site2/aspnet_client
        system.webServer/defaultDocument

    This tells us that inside the c:\inetpub\wwwroot\aspnet_client\web.config file we are actually using that section so if we end up locking this in the server we would break that application.

    Summary

    The configPaths section is a very useful section that allows you to search configuration files and the configuration sections being used in each of them, making it an invaluable tool for scenarios like understanding the configuration usage as well as locking and many others.

  • CarlosAg Blog

    Using LINQ with Microsoft.Web.Administration

    • 10 Comments

    With the upcoming release of .NET 3.5 and LINQ I thought it would be interesting to show some of the cool things you can do with IIS 7 and LINQ. Everything that I will do can be done with C# 2.0 code but it would take me several lines of code to write them but thanks to LINQ you can do them in about a line or two.

    Let's start with a very basic example that does not use LINQ but just M.W.A (Microsoft.Web.Administration) and then start adding interesting things to it.

    The following code just iterates the sites in IIS and displays their name.

    using System;
    using System.Linq;

    using Microsoft.Web.Administration;
    class Program {
        static void Main(string[] args) {
            using (ServerManager serverManager = new ServerManager()) {

                var sites = serverManager.Sites;
                foreach (Site site in sites) {
                    Console.WriteLine(site.Name);
                }
            }
        }
    }
    Now, let's say I wanted to have them sorted by their name. This is where LINQ starts being useful
        using (ServerManager serverManager = new ServerManager()) {

            var sites = (from site in serverManager.Sites
                orderby site.Name
                select site);

            foreach (Site site in sites) {
                Console.WriteLine(site.Name);
            }
        }
    Say you want to start all the sites that are stopped:
        using (ServerManager serverManager = new ServerManager()) {

            var sites = (from site in serverManager.Sites
                where site.State == ObjectState.Stopped
                orderby site.Name
                select site);

            foreach (Site site in sites) {
                site.Start();
            }
        }
    OK, now let's imagine I want to find all the applications that are configured to run in the Default ApplicationPool and move them to run in my NewAppPool. This would take me a lot more lines of code but now I can just do:
        using (ServerManager serverManager = new ServerManager()) {

            var apps = (from site in serverManager.Sites
                from app in site.Applications
                where app.ApplicationPoolName.Equals("DefaultAppPool", StringComparison.OrdinalIgnoreCase)
                select app);

            foreach (Application app in apps) {
                app.ApplicationPoolName = "NewAppPool";
            }

            serverManager.CommitChanges();
        }
    Now let's say I want to find the top 20 distinct URL's of all the requests running in all my worker processes that has taken more than 1 second.
        using (ServerManager serverManager = new ServerManager()) {

            var requests = (
                from wp in serverManager.WorkerProcesses
                from request in wp.GetRequests(1000)
                orderby request.TimeElapsed descending
                select request).Distinct().Take(20);

            foreach (Request request in requests) {
                Console.WriteLine(request.Url);
        }
    }
    OK, finally let's say I want to display a table of all the applications running under DefaultAppPool and display if Anonymous authentication is enabled or not. (Now this one is almost on the edge of "you should do it differently, but it is Ok if you are only reading a single value from the section):
        using (ServerManager serverManager = new ServerManager()) {

            var items = from site in serverManager.Sites
                from app in site.Applications
                where app.ApplicationPoolName.Equals("DefaultAppPool", StringComparison.OrdinalIgnoreCase)
                orderby site.Name, app.Path
                select new {
                    Site = site,
                    Application = app,
                    AnoymousEnabled = ((bool)app.GetWebConfiguration().GetSection("system.webServer/security/authentication/anonymousAuthentication")["enabled"])
                };

            foreach (var item in items) {
                Console.WriteLine("Site:{0,-18} App:{1, -10} Anonymous Enabled:{2}",
                    item.Site.Name, item.Application.Path, item.AnoymousEnabled);
            }
        }
    As you can see LINQ is an incredibly useful feature in C# 3.0 and in conjunction with Microsoft.Web.Administration allows you to do incredibly complex operations in IIS with just few lines of code.
  • CarlosAg Blog

    Sudoku and Tetris Game for Windows Mobile

    • 3 Comments

    During my last two business trips (to Barcelona for TechEd and Mexico for ReMix) I was way too bored on the plane and since I recently got my Motorola Q9 (which is a sweet Windows Mobile Phone) decided to write myself a Tetris game and to port my Sudoku game to Windows Mobile as a way to do my "first steps" in the .NET Compact Framework.

    To my surprise it was really easy to write them and even more to port the desktop version of Sudoku to run in all the .NET Compact Framework platforms.

    Since holidays are coming I thought of share them as a gift for this holiday's season.

    Bottom line (with the risk of sounding like a marketing dude, which I'm not) .NET is a cool technology that makes it really easy to code for many devices, from high-end servers to hand held devices to mobile phones. In this case, I have tested these applications with a Pocket PC, Smartphone 2003, Windows Mobile 5 and Windows Mobile 6. And best of all, the code base is pretty much the same as the Desktop version.

    You can install both games by browsing from your mobile device to http://www.carlosag.net/mobile/ where you will find instructions on how to install the .cab files, or just click the images below to go to the download page for each game.

    Sudoku for Windows Mobile            Tetris for Windows Mobile

     Happy holidays!

     

     

  • CarlosAg Blog

    Announcing: IIS Search Engine Optimization Toolkit Beta 1

    • 20 Comments

    Today we are releasing the IIS Search Engine Optimization Toolkit. The IIS SEO Toolkit is a set of features that aim to help you keep your Web site and its content in good shape for both Users and Search Engines.

    The features that are included in this Beta release include:

    • Site Analysis. This feature includes a crawler that starts looking at your Web site contents, discovering links, downloading the contents and applying a set of validation rules aimed to help you easily troubleshoot common problems such as broken links, duplicate content, keyword analysis, route analysis and many more features that will help you improve the overall quality of your Web site.
    • Robots Exclusion Editor. This includes a powerful editor to author Robots Exclusion files. It can leverage the output of a Site Analysis crawl report and allow you to easily add the Allow and Disallow entries without having to edit a plain text file, making it less error prone and more reliable. Furthermore, you can run the Site Analysis feature again and see immediately the results of applying your robots files.
    • Sitemap and Sitemap Index Editor. Similar to the Robots editor, this allows you to author Sitemap and Sitemap Index files with the ability to discover both physical and logical (Site Analysis crawler report) view of your Site.

    Checkout the great blog about IIS SEO Toolkit by ScottGu, or this IIS SEO simple video of some of its capabilities.

    Run it in your Development, Staging, or Production Environments

    One of the problems with many similar tools out there is that they require you to publish the updates to your production sites before you can even use the tools, and of course would never be usable for Intranet or internal applications that are not exposed to the Web. The IIS Search Engine Optimization Toolkit can be used internally in your own development or staging environments giving you the ability to clean up the content before publishing to the Web. This way your users do not need to pay the price of broken links once you publish to the Web and you will not need to wait for those tools or Search Engines to crawl your site to finally discover you broke things.

    For developers this means that they can now easily look at the potential impact of removing or renaming a file, easily check which files are referring to this page and which files he can remove because of only being referenced by this page.

    Run it against any Web application built on any framework running in any server

    One thing that is important to clarify is that you can target and analyze your production sites if you want to, and you can target Web applications running in any platform, whether its ASP.NET, PHP, or plain HTML text files running in your local IIS or on any other remote server.

    Bottom line, try it against your Web site, look at the different features and give us feedback for additional reports, options, violations, content to parse, etc, post any comments or questions at the IIS Search Engine Optimization Forum.

    The IIS SEO Toolkit documentation can be found at http://learn.iis.net/page.aspx/639/using-iis-search-engine-optimization-toolkit/, but remember this is only Beta 1 so we will be adding more features and content.

    IIS Search Engine Optimization Toolkit

  • CarlosAg Blog

    "EXCEL.EXE is not a valid Win32 application" problem in Windows Vista

    • 3 Comments

    Every now and then after leaving my computer running for several weeks I would get a weird error message when trying to launch Excel saying something like:

    C:\PROGRA~1\MICROS~1\Office12\EXCEL.EXE is not a valid Win32 application.

    or

    This file does not have a program associated with it for performing this action. Create an association in the Set Associations control panel.

    I tried several things to make it run again, but only a restarting would solve the problem. Finally, I decided to investigate a bit more and turns out there is a fix that solves the problem that you can download from Microsoft support:

    http://support.microsoft.com/kb/952709

    This update improves the reliability of Windows Vista SP1-based computers that experience issues in which large applications cannot run after the computer is turned on for extended periods of time. For example, when you try to start Excel 2007 after the computer is turned on for extended periods of time, a user may receive an error message that resembles the following:

    EXCEL.EXE is not a valid Win32 application

    I just installed it and so far so good, no more weird errors but I guess I need to wait a few weeks before I can testify it works. Either way I though this could be helpful for others.

    Direct links for the fix download are:

    Windows Vista, 32-bit versions
    Download the Update for Windows Vista (KB952709) package now. (http://www.microsoft.com/downloads/details.aspx?FamilyId=DF72A9B0-564E-4326-894E-05CBA709CB39)
    Windows Vista, 64-bit versions
    Download the Update for Windows Vista for x64-based Systems (KB952709) package now. (http://www.microsoft.com/downloads/details.aspx?FamilyId=C3536CAA-7B71-4525-9D23-21A5B3D4507F)

  • CarlosAg Blog

    Using Microsoft.Web.Administration in Windows PowerShell

    • 6 Comments

    A couple of months ago I wrote about using LINQ with Microsoft.Web.Administration to manage and query IIS 7.0 configuration. Somebody came back to me and said that LINQ was very cool but that it was very much Developer oriented and that in a production server without VS or .NET 3.5 it wouldn't be an option. Indeed that is a very valid comment and so I decided to show similar stuff with a tool that is available in Windows and its more IT oriented, Windows PowerShell.

    So in this blog I will quickly mention some of the things you can easily do with Microsoft.Web.Administration inside Windows PowerShell.

    To start working with Microsoft.Web.Administration the first thing you need to do is load the assembly so that you can start using it. It is quite easy using the methods from the Assembly type.

    [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Web.Administration")

    Once you have the assembly available then you will need to create an instance of our ServerManager class that gives you access to the entire configuration system.

    $iis = new-object Microsoft.Web.Administration.ServerManager

    The above line basically declares a variable called $iis that we will be able to use for all of our configuration tasks.

    Now to more interesting stuff.

    Getting the list of Sites

    Getting the list of sites is as easy as just accessing the Sites collection, this will output all the information about sites

    PS C:\ > $iis = new-object Microsoft.Web.Administration.ServerManager
    PS C:\ > $iis.sites
    ApplicationDefaults : Microsoft.Web.Administration.ApplicationDefaults
    Applications : {DefaultAppPool, DefaultAppPool, DefaultAppPool, DefaultAppPool...}
    Bindings : {}
    Id : 1
    Limits : Microsoft.Web.Administration.SiteLimits
    LogFile : Microsoft.Web.Administration.SiteLogFile
    Name : Default Web Site
    ServerAutoStart : True
    State : Started

    However, we can also specify the information we care and the format we want to use, for example:

    PS C:\ > $iis = new-object Microsoft.Web.Administration.ServerManager
    PS C:\ > $iis.sites | select-object Id, Name, State
    Id Name State
    -- ---- -----
    1 Default Web Site Started
    2 Site2 Started
    3 Site3 Started

    You can also use the where-object command to filter objects to get only the sites that are Stopped, and then we want to Start them.

    PS C:\ > $iis = new-object Microsoft.Web.Administration.ServerManager
    PS C:\ > $iis.sites | where-object {$_.State -eq "Stopped"} | foreach-object { $_.Start() }

    OK, now let's imagine I want to find all the applications that are configured to run in the Default ApplicationPool and move them to run in my NewAppPool. This is better to do it in three lines:

    PS C:\ > $iis = new-object Microsoft.Web.Administration.ServerManager
    PS C:\ > $iis.sites | foreach {
    $_.Applications | where { $_.ApplicationPoolName -eq 'DefaultAppPool' } |
    foreach { $_.ApplicationPoolName = 'NewAppPool' }
    }
    PS C:\ > $iis.CommitChanges()

    Now let's say I want to find the top 20 distinct URL's of all the requests running in all my worker processes that has taken more than 1 second.

    PS C:\ > $iis = new-object Microsoft.Web.Administration.ServerManager
    PS C:\ > $iis.WorkerProcesses | foreach {
    $_.GetRequests(1000) | sort TimeElapsed -descending |
    select-object Url -unique -first 20 }

    OK, finally let's say I want to display a table of all the applications running under DefaultAppPool and display if Anonymous authentication is enabled or not. (Now this one is almost on the edge of "you should do it differently, but it is Ok if you are only reading a single value from the section):

    PS C:\ > $iis = new-object Microsoft.Web.Administration.ServerManager
    PS C:\ > $iis.Sites | foreach {
    $_.Applications | where { $_.ApplicationPoolName -eq 'DefaultAppPool' } |
    select-object Path,@{Name="AnonymousEnabled"; Expression = {
    $_.GetWebConfiguration().GetSection("system.webServer/security/authentication/anonymousAuthentication").GetAttributeValue("enabled")
    }}
    }

    Again, the interesting thing is that now you can access all the functionality from M.W.A. from Windows PowerShell very easily without the need of compiling code or anything else. It does take some time to get used to the syntax, but once you do it you can do very fancy stuff.

  • CarlosAg Blog

    Using the IIS 7.0 Managed Configuration API

    • 3 Comments

    More than a year ago I wrote about Microsoft.Web.Administration.dll and how it was a new API we were creating for managed code developers to be able to easily set any configuration settings of IIS, however I purposely ignored the configuration part of the API.

    Later I talked about the way configuration was organized in IIS 7.0 and how configuration files inherited and worked.

    Recently I was asked about some samples on how to modify IIS configuration and decided it was about time to talk about the configuration part of Microsoft.Web.Administration.

    The first thing to really emphasize is that Microsoft.Web.Administration in a way has two different ways of reading configuration:

    1. Run time: This is a read-only version of configuration that is meant to be used when running inside a worker process in IIS. It is exposed through a class called Microsoft.Web.Administration.WebConfigurationManager and its GetSection() method with several overloads. Since this blog is about changing configuration I will not be mentioning this API, but suffice to say, it works the same except you get a read-only section.
    2. Design time: This API is our management story for modifying configuration from managed code, it is exposed through the class ServerManager and several of its methods listed below:
    public sealed class ServerManager : IDisposable { 

        
    // Note: some properties and methods were ignored since I'll focus on only these 

        
    public void CommitChanges()

        public void 
    Dispose()

        public 
    Configuration GetAdministrationConfiguration()

        public 
    Configuration GetApplicationHostConfiguration(...)

        public 
    Configuration GetRedirectionConfiguration(...)

        public 
    Configuration GetWebConfiguration(...)

        public static 
    ServerManager OpenRemote(string serverName)
    }

    Whenever you work with the configuration system in IIS you need to:

    1. Figure out which section you want to modify

      The entire configuration in IIS is organized in sections inside configuration files. Sections are composed of elements, attributes, collections and potentially even methods. If you want to know what is the section you are looking you can search it in %windir%\System32\Inetsrv\Config\schema which is the folder where we place all the "schema" files that entirely describe the configuration in IIS.

    2. Figure out for which object you want to manage

      The configuration systemin IIS 7.0 is distributed and as such each child object inherits the configuration of its parent, so for example an application inherits the configuration of the site and the site inherits configuration from the server. So now you need to decide which objects you want to manage, for example, do you want to enable Windows Authentication for the entire server or do you only want to enable it for a particular site or application.

    3. Determine in which place you want to make the change

      As the previous bullet mentions the configuration system is distributed so now you can actually make the changes in different levels for the same object, for example you can modify applicationHost.config with a locationPath "Default Web Site" or you can obtain the same behavior by modifying a web.config file inside wwwroot directory. The concept that really impacts this decision is configuration locking since based on the settings that the server administrator has configured it might be invalid to set authentication in the web.config and might only be possible to set it in applicationHost.config.

    OK, after all that talking lets go to the some actual examples and apply the 3 steps above.

    All the code below assumes you have added a reference to Microsoft.Web.Administration.dll (located at %windir%\system32\inetsrv\) and that you are adding a "using Microsoft.Web.Administration;" at the top of your C# file.

    Enable Directory Browsing for Default Web Site

    1. Looking into system32\inetsrv\config\schema\IIS_Schema.xml you will find that the correct name of the section I'm looking for is system.webServer/directoryBrowse
    2. I only want to modify Default Web Site
    3. Since I now that IIS by default is not locked then I'll make the change directly in the web.config of the site so that when you XCopy my site to another server it remains working the same way.

    Code:

        using (ServerManager manager = new ServerManager()) {

          Configuration webConfig 
    manager.GetWebConfiguration("Default Web Site");

          
    ConfigurationSection directoryBrowseSection =

          
    webConfig.GetSection("system.webServer/directoryBrowse");

          
    directoryBrowseSection.SetAttributeValue("enabled"true);

          
    manager.CommitChanges();

        

    The code uses ServerManager to get the web.config of the web site and then queries the directoryBrowse section and sets the attribute 'enabled' to true. If you open IIS_Schema.xml you will see that this section defines the 'enabled' attribute as a Boolean.

    <sectionSchema name="system.webServer/directoryBrowse">
      <attribute name="enabled" type="bool" defaultValue="false" />
      ...
    </sectionSchema>

    As you can see this API offers a loosely typed object model to ready and modify configuration, with the most important objects being Configuration, ConfigurationElement and ConfigurationAttribute.

    Adding a Handler for the application MyApp underneath Default Web Site

    1. Looking into system32\inetsrv\config\schema\IIS_Schema.xml you will find that the correct name of the section I'm looking for is system.webServer/handlers
    2. I only want to modify Default Web Site/MyApp
    3. In this example let's actually modify applicationHost.config.

    Code:

        using (ServerManager manager = new ServerManager()) {

          Configuration webConfig 
    manager.GetApplicationHostConfiguration();

          
    ConfigurationSection handlersSection =
              
    webConfig.GetSection("system.webServer/handlers""Default Web Site/MyApp");

          
    ConfigurationElementCollection handlersCollection handlersSection.GetCollection();

          
    ConfigurationElement handlerElement handlersCollection.CreateElement();

          
    handlerElement.SetAttributeValue("name""MyHandler");
          
    handlerElement.SetAttributeValue("path""*.myhandler");
          
    handlerElement.SetAttributeValue("verb""GET");
          
    handlerElement.SetAttributeValue("type""CarlosAg.IIS.Samples.MyHandler");

          
    handlersCollection.Add(handlerElement);

          
    manager.CommitChanges();
        

    In this case the handlers Section has a default collection which is where we want to add our handler. For that we use the CreateElement() method to get a new element that we can set the attributes and then add it.

    Removing our Handler

    1. Same system.webServer/handlers
    2. Same Default Web Site/MyApp
    3. Same applicationHost.config.

    Code:

        using (ServerManager manager = new ServerManager()) {

          Configuration webConfig 
    manager.GetApplicationHostConfiguration();

          
    ConfigurationSection handlersSection =
                  
    webConfig.GetSection("system.webServer/handlers""Default Web Site/MyApp");

          
    ConfigurationElementCollection handlersCollection handlersSection.GetCollection();

          foreach 
    (ConfigurationElement handlerElement in handlersCollection) {

            
    if (String.Equals((string)handlerElement.GetAttributeValue("name"), "MyHandler", StringComparison.OrdinalIgnoreCase)) {
              handlersCollection.Remove(handlerElement)
    ;
              break;
            
    }
          }

          manager.CommitChanges()
    ;

        
    }

    Unfortunately currently there is no way to search collections so your only option is to iterate through elements and find the match you are looking for, in this case I'm matching by the name and then removing it from the collection.

    Hopefully that should give a good initial steps on how to start working with configuration using Microsoft.Web.Administration, there are several other options I'll be mentioning in other post on how to lock configuration, how to set metadata, how to enumerate configurations and how to do much more advanced stuff for the few developers that will actually need advanced control of IIS configuration.

  • CarlosAg Blog

    IIS 7.0 Remote Administration and Database Manager Video

    • 5 Comments

    DiscountASP.net published a very nice video that shows how you can enable IIS Manager and Database Manager and other modules for their customers.

    If you don't use DiscountASP.net as your ISP at least its interesting to see how IIS 7.0 and its Remote Administration capabilities over HTTPS and Delegated Management look like. Also you can see the Database Manager in action that you can download for free from http://learn.iis.net/page.aspx/416/basics-of-database-manager/

    First couple of minutes show how they expose this functionality to their customers, but If you just care to see the IIS 7.0 features running seek to minute 2:00.

    http://iis7test.com/iis7_modules/iis7_modules.html

  • CarlosAg Blog

    Mapping a different file extension for ASPX Pages in IIS 7.0

    • 3 Comments

    Today I read a question in one of the IIS.NET forums - although I'm not sure if this is what they really wanted to know - I figured it might be useful to understand how to do this anyway. Several times users does not like exposing their ASP.NET pages using the default .aspx file extension (sometimes because of legacy reasons, where they try to minimize the risk of generating broken links when moving from a different technology, to preserve the validity of previous search-engines-indexes and sometimes for the false sense of security or whatever).

    Regardless of why, the bottom line, to map a different file extension so they behave just like any other ASP.NET page requires you to add a couple of entries in configuration, especially if you want those to be able to work in both Pipeline Modes "Classic and Integrated".

    For this exercise lets assume you want to assign the file extension .IIS so that they get processed as ASPX pages and that you only want this to be applicable for Default Web Site and its applications.

    The following Web.Config file will enable this to work for both Pipeline Modes:

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
       
    <system.webServer>
           
    <handlers>
               
    <add name="ASPNETLikeHandler-Classic" path="*.iis" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" requireAccess="Script" preCondition="classicMode,runtimeVersionv2.0,bitness32" />
                <
    add name="ASPNETLikeHandler" path="*.iis" verb="GET,HEAD,POST,DEBUG" type="System.Web.UI.PageHandlerFactory" modules="ManagedPipelineHandler" requireAccess="Script" preCondition="integratedMode" />
            </
    handlers>
           
    <validation validateIntegratedModeConfiguration="false" />
        </
    system.webServer>
       
    <system.web>
           
    <compilation>
               
    <buildProviders>
                   
    <add extension=".iis" type="System.Web.Compilation.PageBuildProvider" />
                </
    buildProviders>
           
    </compilation>
           
    <httpHandlers>
               
    <add path="*.iis" type="System.Web.UI.PageHandlerFactory" verb="*" />
            </
    httpHandlers>
       
    </system.web>
    </configuration>

    The following command lines uses AppCmd.exe to enable this for both Pipeline Modes and basically generate the .config file above.


    echo To make it work in integrated mode
    appcmd.exe set config "Default Web Site" -section:system.webServer/handlers /+"[name='ASPNETLikeHandler',path='*.iis',verb='GET,HEAD,POST,DEBUG',type='System.Web.UI.PageHandlerFactory',modules='ManagedPipelineHandler',requireAccess='Script',preCondition='integratedMode']"
    appcmd.exe set config "Default Web Site" -section:system.web/compilation /+"buildProviders.[extension='.iis',type='System.Web.Compilation.PageBuildProvider']"

    echo To make it work in classic mode
    appcmd.exe set config "Default Web Site" -section:system.webServer/handlers /+"[name='ASPNETLikeHandler-Classic',path='*.iis',verb='GET,HEAD,POST,DEBUG',modules='IsapiModule',scriptProcessor='%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll',requireAccess='Script',preCondition='classicMode,runtimeVersionv2.0,bitness32']"

    appcmd.exe set config "Default Web Site" -section:system.web/httpHandlers /+"[path='*.iis',type='System.Web.UI.PageHandlerFactory',verb='*']"

    appcmd.exe set config "Default Web Site" -section:system.web/compilation /+"buildProviders.[extension='.iis',type='System.Web.Compilation.PageBuildProvider']"

    appcmd.exe set config "Default Web Site" -section:system.webServer/validation /validateIntegratedModeConfiguration:"False"

    So what does this actually do?

    Lets actually describe the AppCmd.exe lines since it breaks nicely the different operations.

    1. Integrated Mode. When running in Integrated mode conceptually only the IIS Pipeline gets executed and not the ASP.NET pipeline, this means that the HttpHandlers section (in system.web) is actually not used at all.
      1. So just by adding a new handler (ASPNETLikeHandler above) to the System.WebServer/Handlers will cause IIS to see this extension and correctly execute the page. Note that we use the preCondition attribute to tell IIS that this handler should only be used when we are running in an Integrated Pipeline Application Pool.
      2. The second line only tells the ASP.NET compilation infrastructure how to deal with files with this extension so that it can compile it as needed.
    2. Classic Mode. In classic mode the ASP.NET pipeline keeps running as previous versions of IIS as a simple ISAPI and the new IIS pipeline gets executed as well, this means that we first need to tell IIS to route the request to ASP.NET ISAPI and then we need to tell ASP.NET how to handle this extension as well by using the system.web/httpHandlers to process the request.
      1. The first line adds a handler (ASPNETLikeHandler-Classic above) to IIS so that IIS correctly routes this request to the aspnet_isapi.dll. Note that in this case we also use the preCondition attribute to tell IIS that this only applies when running in an Application Pool in classic mode, furthermore we also use it to specify that it should only be used in an Application Pool running in 32 bit mode and that is using version 2.0 of the runtime. If you wanted to support also 64 bit application pools you would add another handler pointing to the 64bit version of aspnet_isapi.dll in Framework64 folder and use bitness64 instead.
      2. The second line tells ASP.NET that the .iis extension is handled by the PageHandlerFactory, so that the ASP.NET pipeline understands what to do when a file with the .iis extension is requested.
      3. Again, just as 2 in the Integrated Mode case we need to tell the ASP.NET compilation infrastructure how to deal with this extension.
      4. Finally since we are adding a system.web/httpHandler entry and to make sure that this will not break when someone changes the App pool to integrated mode we tell IIS to "not complain" if we get run in Integrated mode since we also included the handler in the IIS sections in the right way.

    Hopefully this helps understanding a bit how to re-map extensions to ASP.NET extensions, and in doing that learn a bit more about preConditions, Handlers and AppCmd.

  • CarlosAg Blog

    IIS Admin Pack Technical Preview 2 Released

    • 13 Comments

    Today we are releasing the Technical Preview 2 of the IIS Admin Pack, it is an update of the release we made on February.

    Install the Admin Pack and Database Manager today!

    Admin Pack (x86):  http://www.iis.net/downloads/default.aspx?tabid=34&i=1682&g=6

    Database Manager (x86):  http://www.iis.net/downloads/default.aspx?tabid=34&g=6&i=1684

    Admin Pack (x64): http://www.iis.net/downloads/default.aspx?tabid=34&i=1683&g=6

    Database Manager (x64):  http://www.iis.net/downloads/default.aspx?tabid=34&g=6&i=1685

    New Features:

    There are a lot of interesting features we've added to almost every component for this release:

    Database Manager

    1. Specify your own Connections. We heard during TP1 that it was a very important feature to specify your own database connection information without us automatically reading them from Connection Strings, with TP2 now you can do it. We've also added a switch that allows administrators the ability to disable this feature in case they have concerns and want to still enforce the "only read the connectionStrings from config" functionality and prevent users from trying to add their own.
    2. Extensibility. In these release we are making the API's public to write your own Database Provider that allows you to plugin your own database and reuse all of our UI and remoting, all you need to do is implement a few functions (CreateTable, DeleteTable, InsertRow, DeleteRow, etc) and your provider will be ready to use remotely over HTTPS to manage your own DB.
    3. Support for MySQL. In the upcoming weeks we will be releasing support for MySQL using the extensibility model mentioned above.
    4. Small things. New Toolbar in the Connections Panel to simplify discovery of commands
    5. Use of SMO. In this release we are using SQL Server Management Objects for all the schema manipulation, this means that things like "scripts exported from SQL inluding 'GO statements' and others will work in the Query window"

    Configuration Editor

    1. Choose where Configuration is read: now allows you to specify where you want to read and write your configuration from. This feature is great for advanced users that really understand the inheritance of our distributed configuration system and want to take advantage of it. Now ehen you go to a site, application or anywhere else, you will by default have the same experience where we read configuration from the deepest configuration path, however, now you can use the "From:" Combo box and tell us where you really want to read the configuration from, for example the following image shows how the options look like for a folder underneath Default Web Site. As you can see now you can choose if you want to use locationPath's or go directly to the web.config. This plays really nice with locking allowing you to lock items for delegated users, but still be able to change things yourself pretty easily. This change also works with script generation so now when you generate your scripts you can customize where to read/write configuration.
    2. Lots of small things:Now, all the changes you perform will be show bolded untill you commit them. Enhanced the locking functionality to better reflect when attributes/elements are locked. Several minor bug fixes for script generation, collections without keys, etc.

    IIS Reports

    1. No longer depends on LogParser. TP1 was using LogParser for parsing logs. This version no longer uses LogParser which menas no additional installs for this. We also measured performance and we see an increase of up to 40% which means faster reports. (In my machine for logs of up to 6.4GB or 17 million rows it takes about 2 minutes to generate a report, wee see about 5-7 seconds for 1 million rows)
    2. Better Reports. We took a look at the set of reports and we extended the list of reports as well as added new filters for them, for example, the status code report now lets you drill down and see which URL's generated a particular Status code, etc.
    3. Export to CSV and XML.
    4. Extensibility: For this release just as for DBManager we've made the API's of the UI public so that you can extend and add your own set of reports by writing a class derived from either TableReportDefinition or ChartReportDefinition. This means that just by overriding a method a returning a DataTable, we will take care of the formatting, adding a Chart and letting your users export in HTML, MHTML, CSV, XML, etc.

     UI Extensions

    1. Bug fixes for all the features like Request Filtering, FastCGI, ASP.NET Authorization Rules, and ASP.NET Error Pages.

    As you can see the extensibility is a big theme now, and in my following posts I will be showing how to extend both IIS Reports as well as DBManager.

Page 3 of 10 (91 items) 12345»