How to read/write administration.config

IIS7 configuration system understands machine.config, web.config and applicationHost.config but does not handle administration.config natively. This means reading and writing administration.config is little difficult. If you use AhAdmin directly and call GetAdminSection for a section defined in administration.config, it will throw a configuration error for all configuration paths saying that it couldn’t find the section in the configuration file. Easiest way you can work with administration.config is by using Microsoft.Web.Administration (MWA). ServerManager::GetAdministrationConfiguration gives you a Configuration object which represents administration.config. Below is a sample program which uses MWA to print UI module providers registered in administration.config.

class
Program
{
    static void Main(string[] args)
    {
        ServerManager sm = new ServerManager();

        Configuration administrationConfig = sm.GetAdministrationConfiguration();
        ConfigurationSection moduleProvidersSection = administrationConfig.GetSection("moduleProviders");
        ConfigurationElementCollection moduleProvidersCollection = moduleProvidersSection.GetCollection();

        foreach
(ConfigurationElement moduleProviderElement in moduleProvidersCollection)
        {
            Console.WriteLine(moduleProviderElement.GetAttribute("name").Value);
        }
    }
}

MWA achieves this by setting a pathMapper to map MACHINE/WEBROOT configuration path to administration.config instead of root web.config (it uses a different AdminManager for root web.config). You can write a pathMapper yourself and use AppHostAdminLibrary directly to read or write administration.config. Program below uses a simple pathMapper to map MACHINE/WEBROOT to administration.config and then prints UI module count.

using
AppHostAdminLibrary;

class Program
{
    static void Main(string[] args)
    {
        AppHostAdminManager configManager = new AppHostAdminManager();
        configManager.SetMetadata("pathMapper", new MyPathMapper());

        IAppHostElement modulesElement = configManager.GetAdminSection(
            "modules",
            "MACHINE/WEBROOT");
        IAppHostElement wmodulesElement = configManager.GetAdminSection(
            "system.webServer/modules",
            "MACHINE/WEBROOT/APPHOST");

        Console.WriteLine(modulesElement.Collection.Count);
        Console.WriteLine(wmodulesElement.Collection.Count);
    }
}

public
class MyPathMapper : IAppHostPathMapper
{
    public string MapPath(
        string bstrConfigPath,
        string bstrMappedPhysicalPath)
    {
        string physicalPath = bstrMappedPhysicalPath;

        if (bstrConfigPath.Equals("MACHINE/WEBROOT", StringComparison.OrdinalIgnoreCase))
        {
            string windir = Environment.ExpandEnvironmentVariables("%windir%");
            physicalPath = windir + @"\system32\inetsrv\config\administration.config";
        }

        return physicalPath;
    }
}

In windows server 2008 you can also implement IAppHostPathMapper2 and then set “pathMapper2” metadata on IAppHostAdminManager. IAppHostPathMapper2 allows you to return the impersonation token with the physical path mapping which is then used by the configuration system to read the configuration file. Also, native configuration system has an in-built pathMapper which maps MACHINE/WEBROOT to administration.config. Sample program below sets this in-built pathMapper and then creates an IIS manager user.

using
System;
using System.Text;
using System.Security.Cryptography;
using AppHostAdminLibrary;
 namespace AdminConfig
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                AppHostWritableAdminManager ahadmin = new AppHostWritableAdminManager();
                ahadmin.SetMetadata("pathMapper", "AdministrationConfig");
                ahadmin.CommitPath = "MACHINE/WEBROOT";

                IAppHostElement authenticationSection = ahadmin.GetAdminSection(
                    "system.webServer/management/authentication",
                    "MACHINE/WEBROOT");

                IAppHostElement credentialsElement = authenticationSection.GetElementByName("credentials");
                IAppHostElementCollection credentialsCollection = credentialsElement.Collection;
                IAppHostElement newElement = credentialsCollection.CreateNewElement("add");

                newElement.Properties["name"].Value = "newuser";

                //
                // Get SHA256 hash of password
                // This is required by UI
                // Plain text passwords won't work for IIS Manager users
                //
                SHA256 sha = SHA256.Create();
                byte[] hashBytes = sha.ComputeHash(Encoding.UTF8.GetBytes("iisrocks"));

                byte f1 = 0xf0;
                byte f2 = 0x0f;
                string hexString = "";

                foreach (byte b in hashBytes)
                {
                    int first4 = (b & f1) >> 4;
                    int second4 = (b & f2);

                    hexString = hexString + ((first4 > 9) ? (char)('A' + (first4 – 10)) : (char)('0' + first4));
                    hexString = hexString + ((second4 > 9) ? ((char)('A' + (second4 – 10))) : (char)('0' + second4));
                }

                newElement.Properties["password"].Value = hexString;

                credentialsCollection.AddElement(newElement, -1);
                ahadmin.CommitChanges();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }
    }
}

You can also use Microsoft.Web.Managerment.Server.ManagementAuthentication.CreateUser("user", "password") to create an IIS Manager user. This will also make sure that user is saved to the right provider which might or might not be administration.config.

 

-Kanwal

Feature delegation of custom module in UI

I was at TechEd developers last week and one question that came up was what it takes for custom modules to show up in "Features Delegation" UI. I had image copyright walkthrough setup on the machine and in spite of having the custom schema xml and <section> entry in applicationHost.config, imageCopyright was not shown in "Features Delegation" UI. I did some more investigations on it and following is what it takes for UI to offer delegation to a custom module.

As feature delegation can be done at various levels, just having a new section doesn’t allow UI to offer delegation for it. It requires one to extend admin UI tool. Go through image copyright walkthrough to see how to extend UI. Following methods need to be implemented in the UI module provider for feature delegation page to include your custom module as well.

  1. public bool SupportsDelegation – Feature delegation UI checks if the custom module provider supports delegation using this property. Abstract class ModuleProvider which is base class of all module providers returns false by default. If you dont override this property in your custom module provider, your feature wont show up in the "features delegation" list.
  2. public DelegationState GetChildDelegationState(string path) – Features delegation UI call this method to check the current delegation state of a module at a given path.
  3. public DelegationState[] GetSupportedChildDelegationStates(string path) – Features delegation UI calls this to get the supported delegation states. For configuration sections this would typically return Read Only, Read/Write, Remove delegation and Reset to inherited.
  4. public void SetChildDelegationState(string path, DelegationState delegationState) – This gets called when someone changes the delegation of the module in UI. delegationState is the new state which should be set at path ‘path’.

Implementing all this for image copyright module is easy. Instead of deriving imageCopyrightUIProvider class from ModuleProvider, we can derive it from ConfigurationModuleProvider which has the above methods implemented in it. ConfigurationModuleProvider requires you to add a get property to your module provider to tell the name of the section which gets locked. For image copyright walkthrough following changes are required.

//
// Inherit from ConfigurationModuleProvider instead of ModuleProvider
//
class imageCopyrightUIProvider : ConfigurationModuleProvider

//
// Add ConfigurationSectionName getter to tell section name which gets locked
//
protected
override string ConfigurationSectionName
{
    get 
    {
        return "system.webServer/imageCopyright";
    }
}

Compile your module again, add to GAC and launch UI again. You should see imageCopyRight in your features delegation UI. I tried locking the section at server level by selecting "Read Only" from the available options and sure enough UI added the following to applicationHost.config which locked the section at machine level.

<location path="" overrideMode="Deny">
    <system.webServer>
        <imageCopyright />
    </system.webServer>
</location>

-Kanwal