Using ahadmin to read/write IIS configuration - Part 2

 

Continuing my ahadmin drill down, lets see how to use available interfaces to work with section groups, section definitions, locations and metadata.

 

IAppHostConfigFile interface

 

Working with section groups, section definitions and locations require you to get an instance IAppHostConfigFile which is obtained using IAppHostConfigManager.GetConfigFile(). Following code gets IAppHostConfigFile instances for machine.config, root web.config, applicationHost.config and prints file path of each.

 

var ahwrite = new ActiveXObject("Microsoft.ApplicationHost.WritableAdminManager");

 

// Get ConfigManager using IAppHostAdminManager.ConfigManager get property

var configManager = ahwrite.ConfigManager;

 

// Get IAppHostConfigFile objects for machine.config, root web.config

// and applicationHost.config

var machineConfig = configManager.GetConfigFile("MACHINE");

var rootWebConfig = configManager.GetConfigFile("MACHINE/WEBROOT");

var appHostConfig = configManager.GetConfigFile("MACHINE/WEBROOT/APPHOST");

 

// Use FilePath get property to print paths

WScript.Echo(machineConfig.FilePath);

WScript.Echo(machineConfig.FilePath);

WScript.Echo(machineConfig.FilePath);

 

I get the following output on my machine.

 

\\?\C:\Windows\Microsoft.NET\Framework\v2.0.50727\config\machine.config

\\?\C:\Windows\Microsoft.NET\Framework\v2.0.50727\config\web.config

\\?\C:\Windows\system32\inetsrv\config\applicationHost.config

 

Working with section groups and definitions

 

You can use AhAdmin to get an instance of IAppHostSectionGroup corresponding to a section group defined in a config file. A section group can further contain multiple sections or section groups. IAppHostSectionGroup.Sections gives collection of sections defined in a section group and IAppHostSectionGroup.Count and IAppHostSectionGroup.Item() can be used to get count and sub section groups in a section group. Following code prints all the sections and section groups declared in machine.config.

 

// Get IAppHostSectionGroup for root section group corresponding

// to <configSections>...</configSections>

var rootSectionGroup = machineConfig.RootSectionGroup;

PrintSectionGroup(rootSectionGroup, 0);

 

function PrintSectionGroup(rootSectionGroup, level)

{

    var sectionGroupName = rootSectionGroup.Name;

    var sectionGroupType = rootSectionGroup.Type;

   

    PrintWithIndent("START - sectionGroup name=" + sectionGroupName +

        " type=" + sectionGroupType, level);

 

    for(var i = 0; i < rootSectionGroup.Count; i++)

    {

        // Call PrintSectionGroup for all the section

        // groups defined under this section group

        PrintSectionGroup(rootSectionGroup.Item(i), level + 1);

    }

   

    // Now print all the sections in this sectionGroup

    var sections = rootSectionGroup.Sections;

    if(sections != null)

    {

        for(var i = 0; i < sections.Count; i++)

        {

            var sectionName = sections.Item(i).Name;

            var sectionType = sections.Item(i).Type;

            var omDefault = sections.Item(i).OverrideModeDefault;

            var allowDefinition = sections.Item(i).AllowDefinition;

            var allowLocation = sections.Item(i).AllowLocation;

 

            PrintWithIndent(

                "section name=" + sectionName + " type=" + sectionType +

                    " overrideModeDefault=" + omDefault + " allowDefinition=" +

                    allowDefinition + " allowLocation=" + allowLocation,

                level + 1);

        }

    }

   

    PrintWithIndent("END", level);

}

 

function PrintWithIndent(str, indent)

{

    var prefix = "";   

    for(var i = 0; i < indent; i++)

    {

        prefix += "-";

    }

   

    WScript.Echo(prefix + str);

}

 

Code above will print all the section groups and sections defined in a config file. Due to a bug in Vista RTM, type information for sections is always returned blank. This has been fixed for LH server. Item method both in IAppHostSectionGroup and IAppHostSectionDefinitionCollection takes either index or name of section group or section. Also both these interface provides methods to add/delete section group or sections under a section group. Lets write a simple script to define a new section group named system.myServer in applicationHost.config and add a section for MyAuthenticationModule.

 

var rootSectionGroup = appHostConfig.RootSectionGroup;

var newSectionGroup = rootSectionGroup.AddSectionGroup("system.myServer");

// Set the type if required using newSectionGroup.Type = "";

 

// Create a new section definition under the new section group

var newSection = newSectionGroup.Sections.AddSection("MyAuthenticationModule");

 

// Set newSection properties

newSection.OverrideModeDefault = "Allow";

newSection.AllowDefinition          = "MachineToApplication";

newSection.AllowLocation           = "true";

// Set type if required using newSection.Type;

 

ahwrite.CommitChanges();

 

This writes the following under <configSections> in applicationHost.config.

 

<sectionGroup name="system.myServer">

    <section name="MyAuthenticationModule" overrideModeDefault="Allow"

                   allowDefinition="MachineToApplication" allowLocation="true" />

</sectionGroup>

 

Code to delete this added section and section group looks like following.

 

// You can use index or name of sectionGroup and section

rootSectionGroup.Item("system.myServer").Sections.DeleteSection("MyAuthenticationModule");

rootSectionGroup.DeleteSectionGroup("system.myServer");

 

Working with location tags

 

I have following contents in my "Default Web Site" root web.config. Code samples below play with this web.config. Make sure you have httpErrors section unlocked in applicationHost.config before trying these samples.

 

<configuration>

    <system.webServer>

        <httpErrors errorMode="Custom" />

    </system.webServer>

 

    <location path="iisstart.htm">

        <system.webServer>

            <httpErrors errorMode="Detailed" />

        </system.webServer>

    </location>

 

</configuration>

 

Program to set system.webServer/httpErrors errorMode to "Detailed" for test subfolder will look like following.

 

// Very first thing you should do is set the CommitPath.

// Setting CommitPath after getting IAppHostConfigFile won't work

var filePath            = "MACHINE/WEBROOT/APPHOST/Default Web Site"; 

ahwrite.CommitPath = filePath;

 

// Get IAppHostConfigFile object corresponding to this config
// file and print web.config file path.

var siteConfig =

    configManager.GetConfigFile(filePath);
WScript.Echo(siteConfig.FilePath);

 

var locations = siteConfig.Locations;

WScript.Echo("Location count = " + locations.Count);

for(var i = 0; i < locations.Count; i++)

{

    WScript.Echo(i + ". Path=" + locations.Item(i).Path);

}

 

Above code will print location count = 2. One location path will be "" and other "iisstart.htm". Location with path blank contains all sections outside any <location> tag in web.config. So how can we write code to add the following to web.config?

 

<location path="test">

    <system.webServer>

        <httpErrors errorMode="Detailed" />

    </system.webServer>

</location>

 

There are many ways to do this. One way is to use IAppHostAdminManager.GetAdminSection or IAppHostConfigFile.GetAdminSection for MACHINE/WEBROOT/APPHOST/Default Web Site/test config path, set the property value and commit (CommitPath is already set to "MACHINE/WEBROOT/APPHOST/Default Web Site"). Other option is to add the location tag with path "test", add section system.webServer/httpErrors using IAppHostConfigLocation.AddConfigSection, set the property value and commit to Default Web Site commit path. Following code shows both these ways.

 

// using IAppHostConfigFile.GetAdminSection.

// Using IAppHostAdminManager.GetAdminSection gives same results

var testHttpErrorsSection =

    siteConfig.GetAdminSection(
        "system.webServer/httpErrors",

        "MACHINE/WEBROOT/APPHOST/Default Web Site/test");

testHttpErrorsSection.Properties.Item("errorMode").Value = "Detailed";

 

// using IAppHostConfigLocation.AddConfigSection

var httpErrorsSection =
    siteConfig.Locations.AddLocation(

        "test").AddConfigSection("system.webServer/httpErrors");

httpErrorsSection.Properties.Item("errorMode").Value = "Detailed";

 

// Commit changes using IAppHostWritableAdminManager.CommitChanges

ahwrite.CommitChanges;

 

Metadata

 

IAppHostAdminManager metadata available

 

Metadata-Name

Type

R/W     

pathMapper

IAppHostPathMapper

R/W 

changeHandler

IAppHostChangeHandler

R/W

ignoreInvalidAttributes

Bool

R/W

ignoreInvalidRanges

Bool

R/W   

ignoreInvalidDecryption

Bool

R/W

expandEnvironmentStrings

Bool

R/W

availableSections

String

R

mappingExtension

IAppHostMappingExtension

R

 

IAppHostElement metadata available

 

Metadata-Name

Type

R/W     

overrideMode

String

R/W  

effectiveOverrideMode

String

R    

deepestPathSet

String

R    

deepestFileNameSet

String

R    

deepestFileLineNumberSet

uint32

R    

configSource

String

R/W  

isPresent

Bool

R          

lockItem

Bool

R/W

lockAllElementsExcept

String

R/W  

lockElements

String

R/W  

lockAllAttributesExcept

String

R/W  

lockAttributes

String

R/W 

isLocked

Bool

R

 

IAppHostProperty metadata available

 

Metadata-Name

Type

R/W  

encryptionProvider

String

R/W    

isPropertyEncrypted

Bool

R      

isDefaultValue

Bool

R      

isInheritedFromDefault

Bool

R     

isLocked

Bool

R

 

Not all metadata properties are available everywhere and not all are persisted to disk as well. These work if they make sense. Following samples illustrates how to get/set metadata attached to various IAppHost objects.

 

// Get comma separated list available sections

WScript.Echo(ahwrite.GetMetadata("availableSections"));

 

// Get expandEnvironmentStrings and set to true.

// Change is not persisted to disk.

WScript.Echo(ahwrite.GetMetadata("expandEnvironmentStrings");

ahwrite.SetMetadata("expandEnvironmentStrings", true);

 

// Lock a collection item under system.webServer/modules

var modulesSection =
    ahwrite.GetAdminSection("system.webServer/modules", "MACHINE/WEBROOT/APPHOST");

modulesSection.Collection.Item(0).SetMetadata("lockItem", false);

ahwrite.CommitChanges();

 

// Get IAppHostMappingExtension interface and use it to get site element

var mappingExtension = ahwrite.GetMetadata("mappingExtension");

var defaultSiteElement = mappingExtension.GetSiteElementFromSiteId(1);

WScript.Echo(defaultSiteElement.GetElementByName(
                       "bindings").Collection.Item(0).GetPropertyByName("bindingInformation").Value);

 

// Check if the effective value is defaultValue for

// doDynamicCompression property

var urlCompSection =

    ahwrite.GetAdminSection("system.webServer/urlCompression", "MACHINE/WEBROOT/APPHOST");

WScript.Echo(urlCompSection.GetPropertyByName("doDynamicCompression").GetMetadata("isDefaultValue"));

 

// Get encryption provider for apppool password

var apppoolsSection =

    ahwrite.GetAdminSection("system.applicationHost/applicationPools", "MACHINE/WEBROOT/APPHOST");

var apProcessModel = apppoolsSection.Collection.Item(2).GetElementByName("processModel");

WScript.Echo(apProcessModel.GetPropertyByName("password").GetMetadata("encryptionProvider"));

 

Together with my earlier post, you should be able to do about anything using ahadmin. Good luck.

 

Kanwal

3 Comments

Comments have been disabled for this content.