ASP.NET Custom Configuration Sections and Group Policy Aware Configuration Providers

Part 2 - Creating and Using Group Policy Aware Providers with ASP.NET

 

©2007 Alex Homer, Stonebroom Limited, http://www.stonebroom.com

This series of three articles shows how you can create custom configuration sections in your ASP.NET application configuration file, and expose the data they contain within your application using custom providers. The articles also describe how you can use Windows Group Policy at both the machine and domain level to control configuration settings from a central location, and impose these settings on some or all of the servers that run your Web applications. Finally, the third article shows an example of how these techniques are used to great effect in existing products; by demonstrating how you can use the Manageable Configuration Provider that is part of Microsoft's Enterprise Library.

Contents

Introduction. 2

Working with Group Policy. 2

Local Group Policy. 3

Domain Group Policy. 4

Making Applications Group Policy Aware. 7

Creating an Administrative Template for a GPO. 8

ADM Template Structure. 8

ADM Template Controls 10

Installing and Configuring Administrative Templates 11

Reading Group Policy Settings from Windows Registry. 12

A Group Policy Aware Custom Configuration Provider 13

Creating a Group Policy Helper Class 13

Converting CHECKBOX Part Values to Boolean Values 14

Integrating Group Policy into the Configuration Provider 15

Using the Group Policy Aware Configuration Provider 16

User Configuration Settings in ASP.NET Web Applications 18

Summary. 19

Introduction

ASP.NET applications usually rely on two of the standard sections in a Web.config file: the <appSettings> section for holding general configuration and application values, and the <connectionStrings> section that is specialized to store connection details for data providers such as database servers.

However, custom configuration sections are useful where an application requires complex sets of configuration data that the .NET configuration system can expose, and automatically validate. It is easy to create custom configuration sets and the related classes that expose this data.

When it comes to actually managing configuration values, the common approach is to simply update and deploy the Web.config file to all servers running the application. However, the ASP.NET configuration system provides no central mechanism for controlling the values in a Web.config file, which means that – if you have multiple servers such as a Web farm – the Web.config files can become unsynchronized. For example, local operators can change values in Web.config on specific machines.

Group Policy provides a mechanism for centrally managing features of the operating system and the applications that run upon it. You can take advantage of Group Policy to create a mandatory and centrally managed environment for configuration settings for your Web applications by creating a custom Group Policy Object (GPO) and building Group Policy awareness into your configuration code.

In these articles, you will see an example of a custom configuration section and the use of a Group Policy aware configuration system. Of course, you can create Group Policy aware code that reads configuration information from the <appSettings> and <connectionStrings> sections of Web.config using the technique shown in these articles. Alternatively, as demonstrated, you can combine the custom configuration provider and Group Policy aware techniques so that the configuration classes that expose the custom configuration information are themselves Group Policy aware.

Working with Group Policy

Windows Group Policy provides a mechanism for applying settings to all the machines within an Active Directory domain or forest, or locally on individual machines. Domain controllers apply each Group Policy Object (GPO) defined within their domain to all the machines in the domain as part of the security policy, storing the values in the registry in either the HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER hive, depending on the definitions in an administrative template (.adm file) installed within Group Policy.

Windows includes several common templates for setting parameters of the operating system and applications such as Internet Explorer. However, you can create your own templates to add custom GPOs to Group Policy, and have these applied throughout the domain. Alternatively, you can install templates locally (on each machine) to control the settings available on just that machine.

Local Group Policy

To work with local Group Policy settings, use a Command prompt or the Start | Run command to execute gpedit.msc. This opens the Group Policy Object Editor where you can view and edit the values for any of the Group Policy settings that are not configured at domain or forest level (see Figure 1). However, if an administrator has configured a setting at domain or forest level, the local setting will be disabled.

Figure 1 – The Group Policy Editor showing the contents of the Network\DNS Client administrative template and the DNS Servers setting within this template. Notice that Vista places .adm templates in the section named "Classic Administrative Templates".

To edit a setting, double-click on it (or right-click and select Properties) to display the settings dialog. For each setting, you can specify whether it is enabled, disabled, or not configured. When you enable a setting, you can enter the appropriate values in the controls displayed in the central section of this dialog. There is also an Explain tab that displays information about the setting (see Figure 2).  

Figure 2 – Editing a setting in the Group Policy Editor

Domain Group Policy

Group Policy is generally most effective in situations where administrators need to manage multiple machines, such as all the servers in Web farm or all the machines running a specific application. To create a GPO that applies at domain or forest level, and edit the settings, you must install and configure an administrative template using the domain-level Group Policy Management Console (GPMC).

 

Installing the GPMC adds the Group Policy Editor link to the Administrative Tools section of your Start menu. The editor provides a view of all the GPOs and settings available for the forest and domains. Figure 3 shows the editor. In the left-hand tree are the forests, domains, sites, and GPOs. In the right-hand pane are four tabbed pages that allow you to view the scope for a selected GPO, details of the GPO, the settings within that GPO, and the delegation for other users.

For example, Figure 3 shows the Scope page for the sample GPO used in this article. You can see in the Location list that it is Enforced for this domain. The Link Enabled setting places a link in the left-hand tree view directly under the domain item to make it easier to navigate to this GPO. You can see this link in Figure 3. Right click on an item in the Location list to change the Enforced and Link Enabled settings.

Figure 3 – The Group Policy Management Console showing the Scope page for the example GPO used in this article

Figure 4 shows the Settings page for the example GPO. Here, you can view the settings for all the items in this GPO. Notice that the list contains two sections named Computer Configuration and User Configuration. The example GPO contains Group Policy settings for the computers in the domain and for the current user on each computer so that you can see the different effects of these two types of settings.

Figure 4 – The Group Policy Management Console showing the settings for the example GPO, both for computers and for users

To edit the settings for a GPO, right-click the entry in the left-hand tree (either the entry under the Group Policy Objects item or the link directly under the domain item) and select Edit. This opens the same Group Policy Object Editor window as you saw used for Local Policy settings in the previous section of this article, but now it applies at domain or forest level scope instead of local machine scope.

Navigate to the GPO you want to edit (in the Administrative Templates section), where you can enable and disable each setting, edit the setting values, and view the explanation for each setting (see Figure 5).

Figure 5 – The settings for the custom domain-wide GPO shown in the Editor window.

The settings you specify are replicated throughout the domain to each computer, and stored in Windows Registry. Figure 6 shows the Computer settings for the example GPO viewed on one of the machines within the domain. Note that it takes a few minutes to the settings to replicate, and so you may not see them immediately after editing the GPO.

Figure 6 – Windows Registry showing the values applied through domain-wide Group Policy. Note that this only shows the machine values in the HKLM hive, and not the user-specific values in the HKCU hive.

Making Applications Group Policy Aware

To build group policy awareness into your applications, you must:

You will see how to achieve all of these tasks in the remainder of this article.

Creating an Administrative Template for a GPO

In Windows Vista, the new .admx file type for Administrative Templates is – as you would expect – an XML-based format. The classic .adm Administrative Template format is a more arcane text format that is likely to be unfamiliar. However, it is easy to get used to, as long as you have a reference guide to the syntax readily to hand.

ADM Template Structure

An .adm file will usually contain three sections, as shown in the following listing:

CLASS MACHINE

CATEGORY !!GPConfigExample

  KEYNAME "Software\Policies\Examples\GPConfigExample"

  ... category settings for computer configuration go here ...

END CATEGORY

... more categories here as required ...

 

CLASS USER

CATEGORY !!GPConfigExample

  KEYNAME "Software\Policies\Examples\GPConfigExample"

  ... category settings for user configuration go here ...

END CATEGORY

... more categories here as required ...

 

[strings]

... string values referenced from class definitions go here ...

... for example, the string used as the category name above:

GPConfigExample="GP Configuration Example"

 

As you would expect, categories in the CLASS MACHINE section define policies for the Computer Configuration (stored in the HKEY_LOCAL_MACHINE Registry hive), while categories in the CLASS USER section define policies for the User Configuration (stored in the HKEY_CURRENT_USER Registry hive). However, before you use the CLASS USER section in a Web application, be sure to read the section "User Configuration Settings in ASP.NET Web Applications" at the end of this article!

Each category section defines the Registry key that will persist the settings for that category, and contains one or more policies – each of which produces a single setting within that category. Notice how you can use two exclamation marks (!!) to specify a string value name, and then define the string in the [strings] section. This is especially useful for values that are repeated in the template, and also makes it easier to edit the text strings.

Each policy within a category defines the set of controls (named PARTS) required to edit the setting, and the "explain" text that helps administrators understand what the policy does and how to set the values. For example, this category contains a single policy that defines two settings:   

CATEGORY !!GPConfigExample

  KEYNAME "Software\Policies\Examples\GPConfigExample"

  POLICY !!DefaultUser

    EXPLAIN !!DefaultUserExplain

    PART !!DefaultUserNameText EDITTEXT REQUIRED VALUENAME "UserName"

    END PART

    PART !!DefaultLocationText EDITTEXT VALUENAME "UserLocation"

    END PART

  END POLICY

END CATEGORY

 

[strings]

GPConfigExample="GP Configuration Example"

DefaultUser="Default User Details"

DefaultUserExplain="The default values for the user name and location to use when attempting to connect."

DefaultUserNameText="User Name: "

DefaultLocationText="Location: "

 

This policy generates an entry in the GPO named "Default User Details", which – when opened for editing – contains two text boxes with the specified captions ("User Name:" and "Location:"), as shown in Figure 7. The text from the EXPLAIN entry appears in the Explain tab of the editor dialog.  

Figure 7 – The policy editor controls and content generated by the example Administrative Template file.

ADM Template Controls

Each policy definition specifies the controls that administrators will use to configure that policy. The previous example uses EDITTEXT parts that generate text boxes with a specified caption. You can also specify behavior for the part, for example the previous policy specifies that, for the "User Name" text box, the administrator must provide a value (REQUIRED). The VALUENAME "UserName" section of the declaration specifies that the setting should be persisted under a value named "UserName" within the registry key for this policy:

PART !!DefaultUserNameText EDITTEXT REQUIRED VALUENAME "UserName"

 

The parts you can use in a classic Administrative Template are:

You can see several of the controls in use in the example GPO. The following listing shows the complete Administrative Template for the example GPO:

CLASS MACHINE

 

CATEGORY !!GPConfigExample

  KEYNAME "Software\Policies\Examples\GPConfigExample"

    POLICY !!AutoConnect

      EXPLAIN !!AutoConnectExplain

      PART !!AutoConnectText CHECKBOX DEFCHECKED VALUENAME "AutoConnect"

      END PART

    END POLICY

    POLICY !!DeviceMode

      EXPLAIN !!DeviceModeExplain

      PART !!DeviceModeText DROPDOWNLIST REQUIRED VALUENAME "DeviceMode"

        ITEMLIST

          NAME !!DeviceModeItem1Text VALUE "Static"

          NAME !!DeviceModeItem2Text VALUE "Roaming"

          NAME !!DeviceModeItem3Text VALUE "Cradled"

          NAME !!DeviceModeItem4Text VALUE "Disconnected"

        END ITEMLIST

      END PART

    END POLICY

    POLICY !!ConnectionPrices

      EXPLAIN !!ConnectionPricesExplain

      PART !!InternalPriceText NUMERIC VALUENAME "InternalPrice"

        DEFAULT 3 MIN 1 MAX 10 REQUIRED

      END PART

      PART !!ExternalPriceText NUMERIC VALUENAME "ExternalPrice"

        DEFAULT 6 MIN 3 MAX 10 REQUIRED

      END PART

      PART !!WirelessPriceText NUMERIC VALUENAME "WirelessPrice"

        DEFAULT 9 MIN 5 MAX 20

      END PART

      PART !!GPRSPriceText NUMERIC VALUENAME "GPRSPrice"

        DEFAULT 12 MIN 10 MAX 25

      END PART

    END POLICY

END CATEGORY

 

CLASS USER

 

CATEGORY !!GPConfigExample

  KEYNAME "Software\Policies\Examples\GPConfigExample"

  POLICY !!DefaultUser

    EXPLAIN !!DefaultUserExplain

    PART !!DefaultUserNameText EDITTEXT REQUIRED VALUENAME "UserName"

    END PART

    PART !!DefaultLocationText EDITTEXT VALUENAME "UserLocation"

    END PART

  END POLICY

END CATEGORY

 

[strings]

GPConfigExample="GP Configuration Example"

AutoConnect="Automatic Connection Settings"

AutoConnectExplain="Specifies if the application will attempt to activate a connection automatically."

AutoConnectText="Attempt to connect automatically"

DeviceMode="Device Mode Settings"

DeviceModeExplain="Specifies the type of connection that the application should attempt to use."

DeviceModeText="Device Mode: "

DeviceModeItem1Text="Static or local connections only"

DeviceModeItem2Text="Roaming (any available connection)"

DeviceModeItem3Text="Cradled (local connections only)"

DeviceModeItem4Text="Disconnected"

ConnectionPrices="Connection Prices"

ConnectionPricesExplain="The relative cost or price of each connection type, which allows the application to minimize overall connection costs."

InternalPriceText="Internal Connection Price: "

ExternalPriceText="External Connection Price: "

WirelessPriceText="Wireless Connection Price: "

GPRSPriceText="GPRS (cellular) Connection Price: "

DefaultUser="Default User Details"

DefaultUserExplain="The default values for the user name and location to use when attempting to connect."

DefaultUserNameText="User Name: "

DefaultLocationText="Location: "

Installing and Configuring Administrative Templates

To install an Administrative Template as a custom GPO:

  1. Open the appropriate Group Policy Editor. For local policies, use gpedit.msc (see the earlier section "Local Group Policy" for details). For forest/domain policies, use the GPMC (see the earlier section "Domain Group Policy" for details).
  2. If you are installing a forest/domain policy in the GPMC, right-click the Group Policy Objects entry within the appropriate domain, select New, and enter the name for the GPO. Then right-click the new GPO entry and select Edit to open the Group Policy Object Editor window.
  3. In the Group Policy Object Editor window, right-click the entry Administrative Templates in the left-hand tree view and select Add/Remove Templates....
  4. In the Add/Remove Templates dialog, click the Add button and navigate to the .adm or .admx file you want to add. For the example application, add the template named GroupPolicyDemo.adm from the GPScript subfolder of the example Web site.
  5. Click the Close button in the Add/Remove Templates dialog, and the template policies appear in the Group Policy Object Editor window. Categories in the CLASS MACHINE section of the template appear in the Computer Configuration section of the tree, and categories in the CLASS USER section of the template appear in the User Configuration section.
  6. To configure or edit the values for the policies in the GPO, select the new GPO section within the Administrative Templates section in either the Computer Configuration or the User Configuration section of the left-hand tree view and double-click on the item in the list in the right-hand pane to open the edit dialog.
  7. Use the Next Setting and Previous Setting buttons in the edit dialog to scroll through the settings. Values are saved as soon as you close the edit dialog or move to another setting.   

Reading Group Policy Settings from Windows Registry

Once you have created and installed a GPO, you must write code within your application that makes it aware of the settings, and applies any values configured in that GPO. The .NET Framework contains classes in the Microsoft.Win32 namespace that make it easy to work with Windows Registry. You must add this namespace to your project and code files to take advantage of these classes:

// in C#:

using Microsoft.Win32;

 

' in Visual Basic.NET

Imports Microsoft.Win32

 

To open and read a registry key, you use the Registry and RegistryKey classes. For example, this code opens a key in the HKEY_LOCAL_MACHINE hive and reads the value. Notice that it is good practice to close the key after use:

// in C#:

String REG_PATH = "Software\\Policies\\Examples\\GPConfigExample";

Object keyValue = null;

// open named key in HKEY_LOCAL_MACHINE section

RegistryKey demoKey = Registry.LocalMachine.OpenSubKey(REG_PATH);

if (demoKey != null)

{

  // get the specified value from this key

  keyValue = demoKey.GetValue("MyKeyValueName");

  demoKey.Close();

}

 

' In Visual Basic.NET:

Private Const REG_PATH = "Software\Policies\Examples\GPConfigExample"

Dim keyValue = Nothing

Dim demoKey As RegistryKey = Registry.LocalMachine.OpenSubKey(REG_PATH)

If Not demoKey Is Nothing Then

  ' get the specified value from this key

  keyValue = demoKey.GetValue(keyName)

  demoKey.Close()

End If

 

Notice that the GetValue method of the RegistryKey class returns an Object type. You can use the GetValueKind method to determine the registry value type if required, and other methods of the class to read multiple values and subkeys. However, when reading Group Policy settings, you usually only need to read the key value as an Object, and then convert it as required within your code.

A Group Policy Aware Custom Configuration Provider

This final section of the article describes how you can integrate the techniques for creating and installing Group Policy templates, and reading Windows Registry, to make your applications Group Policy aware. The example application you can download for this article contains a Group Policy aware configuration provider for the Web.config file you saw in the previous article.

Creating a Group Policy Helper Class

The action of reading from Windows Registry is a regular and repeated task when making code Group Policy aware, and so it makes sense to create a helper class that can abstract this task.

The example application contains a class named GPAwareHelper that contains a static/shared method named GetGPOverride. This method takes as parameters a Boolean value that indicates if the target is the HKEY_LOCAL_MACHINE or the HKEY_CURRENT_USER hive, the name of the key, and the default value to return if there is no Group Policy configured value in the Registry. The path to the key is stored as a constant within the class in this example, but could equally well be passed to the method.  

The next listing shows the C# code for the GetGPOverride method (the downloadable samples also contain both C# and Visual Basic.NET versions). The code for the method is the same as that shown in the previous section of this article, with the exception that it selects the appropriate registry hive (HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER) depending on the value of the isHKLM parameter:

using System;

using Microsoft.Win32;

 

namespace GPUtilities

{

  public static class GPAwareHelper

  {

    private const String REG_PATH

            = "Software\\Policies\\Examples\\GPConfigExample";

 

    public static Object GetGPOverride(Boolean isHKLM, String keyName,

                                       Object configValue)

    {

      Object keyValue = null;

      RegistryKey demoKey = null;

      if (isHKLM)

        // open named key in HKEY_LOCAL_MACHINE section

        demoKey = Registry.LocalMachine.OpenSubKey(REG_PATH);

      else

        // open named key in HKEY_CURRENT_USER section

        demoKey = Registry.CurrentUser.OpenSubKey(REG_PATH);

      if (demoKey != null)

      {

        // get the specified value from this key

        keyValue = demoKey.GetValue(keyName);

        demoKey.Close();

        // check that a value was found and, if not, return

        // the value provided in method parameters

        if (keyValue != null)

          return keyValue;

        else

          return configValue;

      }

      else

      {

        // key not found, so return value provided in method parameters

        return configValue;

      }

    }

    ...

 

The GetGPOverride method returns the value of the specified key as an Object if found in the Registry, or the default value passed to the method if the key or a value does not exist.

Converting CHECKBOX Part Values to Boolean Values

One problem when working with Group Policy is that the data types within the Registry, and the settings that the PARTS you define in the Administrative template generate, are not directly compatible with common .NET value types such as Int32 and Boolean. In particular, the setting generated by a CHECKBOX part is either zero (unchecked) or 1 (checked). Therefore, your code must convert this to a Boolean value if your configuration provider expects, and exposes, this setting as a Boolean type.

The helper class in the example application includes a method with the delightfully descriptive name ConvertInt32ObjectToBooleanObject that performs this conversion. It tests the underlying value type of a value passed as the single parameter, and returns an Object of underlying Boolean type with the appropriate value: true if the input is an Int32 type of value 1, or false if it is an Int32 type of value 0. If the value is not of underlying type Int32, the method simply returns the input Object

    ...

    public static Object ConvertInt32ObjectToBooleanObject(

                          Object inputValue)

    {

      // see if input value is of underlying type Int32 (int)

      if (inputValue.GetType().Name == "Int32")

      {

        // return Boolean equivalent

        return ((Int32)inputValue == 1);

      }

      else

      {

        // just return the input Object

        return inputValue;     

      }

    }

  }

}

 

Depending on the control types you use in your Administrative Templates, and the way that your custom configuration provider exposes settings, you may need other conversion methods that return values as the appropriate types.

Integrating Group Policy into the Configuration Provider

With the helper class described above, it is easy to modify the existing configuration provider for the example application to make it Group Policy aware. For each property accessor within the provider classes that return configurable values (from attributes in the custom section of the Web.config file), the code passes each value through the GetGPOverride method before returning it as the value of the property. For example, this is the code for the DeviceMode property accessor in the ConnectionItemElementCollection class:

[ConfigurationProperty("deviceMode")]

// returns the value of the optional "deviceMode" attribute 

public String DeviceMode

{

  // Pass value through the Group Policy configuration helper method

  // that applies any Group Policy settings.

  get

  {

    return GPAwareHelper.GetGPOverride(true, "DeviceMode",

                                       this["deviceMode"]).ToString();

  }

}

 

You can see that this code passes the value that the .NET configuration system extracts from the Web.config file through the static GetGPOverride helper method, and exposes the value that this method returns as the property value. If a Group Policy Object provides a value for this setting, the property accessor will return this setting instead of the value in the configuration file. 

The property accessor for the AutoConnect property, for which Group Policy administration uses a CHECKBOX part, extracts the value from the local Web.config file, passes it through the GetGPOverride method, and then passes it to the ConvertInt32ObjectToBooleanObject method. This converts the underlying Int32 value returned from the RegistryKey class to a Boolean type of the appropriate value, or just returns the Web.config value if there is no setting configured for this property in the registry:

[ConfigurationProperty("autoConnect", DefaultValue = false,

                       IsRequired = false)]

// returns the value of the optional "autoConnect" attribute 

public Boolean AutoConnect

{

  get

  {

    // Pass value through the Group Policy configuration helper method

    // that applies any Group Policy settings.

    Object gpAwareValue = GPAwareHelper.GetGPOverride(true, "AutoConnect",

                                                     this["autoConnect"]);

    // This is a Boolean value in the configuration but GroupPolicy returns

    // 0 or 1 from a CHECKBOX control, so use helper method to convert it.

    return (Boolean)GPAwareHelper

                    .ConvertInt32ObjectToBooleanObject(gpAwareValue);

  }

}

Using the Group Policy Aware Configuration Provider

The example application contains a class named GPAwareConfigSection that implements the Group Policy aware configuration provider. It is the same as that described in the previous article in this series (see http://www.daveandal.net/articles/GPAwareConfig/), but uses the techniques described in this article to apply Group Policy settings to the values it exposes to the hosting application.  

To allow the example application to show both the "normal" custom configuration provider and the Group Policy aware provider in use, the Web.config file contains defines two custom configuration sections. The second of these is for the Group Policy aware provider:

<configSections>

  <section name="CustomConnections"

           type="CustomConfigSection.ConnectionSettingsSection"/>

  <section name="GPAwareConnections"

           type="GPAwareConfigSection.ConnectionSettingsSection"/>

</configSections>

 

Then, in the main body of Web.config, is the custom section containing the configuration values. Note that these are the same as used for the "normal" custom provider described in the previous article, with only the section element name being different:

<!-- configuration values for GP-aware configuration provider -->

<GPAwareConnections autoConnect="false">

  <ConnectionItems deviceMode="Static">

    <add connectionType="InternalPrice" price="2"/>

    <add connectionType="ExternalPrice" price="4"/>

    <add connectionType="WirelessPrice" price="8"/>

    <add connectionType="GPRSPrice" price="15"/>

  </ConnectionItems>

  <defaultUser userName="John Smith" location="Block 7"/>

</GPAwareConnections>

 

There are only two differences in the code-behind file for Default.aspx in the example application compared to the "normal" configuration provider used in the previous article. The handler for the button that gets configuration settings from the Group Policy aware provider instantiates this provider instead of the "normal" provider:

protected void btnGetGPAware_Click(object sender, EventArgs e)

{

  // Retrieve the <GPAwareConnections> section from the configuration file

  GPAwareConfigSection.ConnectionSettingsSection configSection

      = (GPAwareConfigSection.ConnectionSettingsSection)

         WebConfigurationManager.GetSection("GPAwareConnections");

  // Get value of "autoConnect" attribute on <Connections> element

  lblAutoConnect.Text = configSection.AutoConnect.ToString();

  // Get value of "deviceMode" attribute on <ConnectionItems> element

  lblDeviceMode.Text = configSection.ConnectionItems.DeviceMode;

  // Iterate through collection of <add> elements within <ConnectionItems>

  foreach (GPAwareConfigSection.ConnectionItemElement conn in

             configSection.ConnectionItems)

  {

    // Get value of "connectionType" and "price" on each <Add> element

    lblConnectionItems.Text += conn.ConnectionType + " = "

                            + conn.Price.ToString() + " &nbsp; ";

  }

  // Get the value of the attributes on the <defaultUser> element

  lblDefaultUser.Text = configSection.DefaultUser.DefaultUserName;

  lblUserLocation.Text = configSection.DefaultUser.DefaultUserLocation;

  // display the identity of the current user

  lblUserIdentity.Text = "Current user identity: <b>"

                       + User.Identity.Name + "</b><p />";

}

 

The code to display the values from the configuration provider in the Label controls on the page is the same for both providers. However, as you can see in the listing above, the btnGetGPAware_Click handler also displays the name of the current user, extracted from the current User.Identity instance. You'll see why this is significant shortly.

Figure 8 shows the example application in action when you execute the "normal" configuration provider described in the previous article. This displays the values in the local Web.config file.

Figure 8 – The values in Web.config as exposed by the "normal" custom configuration provider

Figure 9 shows the results when you click the second button to execute the Group Policy aware provider. You can see the values specified in Group Policy settings for the domain, and the current user identity.

Figure 9 – The configuration values exposed by the Group Policy aware configuration provider, showing how it overrides Web.config values with the settings defined in Group Policy. Notice that the page also displays the current user identity when you execute the Group Policy aware configuration provider 

If you look back at Figure 4, you can see the Group Policy settings specified for this domain, and confirm that the Group Policy aware configuration provider actually does expose the settings specified in Group Policy instead of the values in the Web.config file.

User Configuration Settings in ASP.NET Web Applications

Before you go off to implement the techniques shown in this article in your own ASP.NET applications, you must be aware of the way that User Configuration policies work. In general, you should avoid creating GPOs that contain User Configuration settings for Web applications (in other words, do not use the CLASS USER section of an Administrative Template).

This is because the values you set in Group Policy for User Configuration policies will only appear in the HKEY_CURRENT_USER hive of the Registry, loaded as Windows loads the settings for each user as they log onto the machine. However, a server may not have a logged on (current) user; and even if it does, the user will not be the account that ASP.NET and your Web application is running under (unless you change the default settings for the application so that it runs under the context of the currently logged on user).

The only reason that the user identity appears in Figure 9, and the application reads the Group Policy settings defined in the CLASS USER section of the Administrative Template, is because the application is running within Visual Studio 2005 under the context of the currently logged on user (alex). You can see that the URL contains the custom port number for the Visual Studio Web Server.

If you install the application into Internet Information Services, and run it from there, you will see that there is no "current user" and the Group Policy settings in the CLASS USER section are not available (the User name and User location values are those contained in Web.config), as you can see in Figure 10.

Figure 10 – The configuration values exposed by the Group Policy aware configuration provider when running the application under IIS instead of the Visual Studio Web Server. Notice that there is no "current user", and the values for "User name" and "User location" revert to those in Web.config

If you disable anonymous access so that users must provide valid Windows account credentials to run the application, the page does show the user name as the current identity. However, it still does not use the Group Policy settings from the CLASS USER section of the Administrative Template, because the user is not actually the "current user" on the machine.

Therefore, you should only use the CLASS USER section of the Administrative Template for Windows Forms or other applications that will always run under the context of the currently logged on user, and not in ASP.NET Web applications.

 

 

Next: Using the Enterprise Library Manageable Configuration

 

Summary

Developers are increasingly pressured into building applications that are more manageable. It is an accepted fact that deployment, runtime, and maintenance costs make up over 80% of the lifetime cost of an application, and any techniques that can help to minimize these costs are extremely welcome.

Managing application configuration is a major part of the deployment and runtime tasks that administrators face, and best practice in application design and development tends towards central management of configuration data, together with the ability to impose specific configurations on users and groups of machines. In the ASP.NET arena, being able to control centrally configuration for multiple servers, such as a Web farm, reduces the cost and complexity of updating and managing Web.config files.

This series of articles describes how you can create custom configuration sections and providers that automatically verify the presence of configuration data matching a required schema, and expose this data through objects and properties that make it easy to consume the data within applications.

Secondly, the articles describe how you can use Windows Group Policy to centrally manage configuration information, and impose specific values on all machines and users. By using these techniques within a custom configuration provider, you can easily build Group Policy aware configuration systems that automatically provide the advantages of centralized administration and control.

Finally, to demonstrate how these techniques are used in the "real world", the articles describe how version 3.0 of Microsoft's Enterprise Library provides the same kinds of capabilities through its own Manageable Configuration Provider mechanism.

 

©2007 Alex Homer, Stonebroom Limited, http://www.stonebroom.com