ASP.NET Configuration and Group Policy, Part 2: Creating and Using Group Policy-Aware Providers

n Article 1 of this series, you saw how to create a custom configuration section in a Web.config file and access it using a custom provider. That solves the problem of where to store configuration information and how to use it in your applications, but it doesn’t address another problem mentioned in that article, which is that the ASP.NET configuration system provides no central mechanism for controlling the values in Web.config files. The result is that Web.config files on multiple servers (such as in a web farm) can easily get out of sync. Fortunately, you can solve that by extending your configuration providers to make them Group Policy aware.

Group Policy provides a mechanism for centrally managing Windows features and applications. You can take advantage of Group Policy to create a mandatory and centrally-managed environment for your web applications’ configuration settings by creating a custom Group Policy Object (GPO) and building Group Policy awareness into your configuration code.

Of course, you can also create Group Policy-aware code that reads configuration information from the and 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 to make the configuration classes that expose the custom configuration information 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 operating system parameters and applying policy to 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, or you can install templates locally that control the settings available to just that machine.

Author’s Note: This article describes using Group Policy in a mixed Windows 2000/2003/Vista environment. However, Windows Vista and the forthcoming Windows Vista Server (code-name “Longhorn”) introduce several new features for Group Policy, including a wider range of operating system and application default settings, multi-lingual support, and improved GPO management. In Vista,.adm templates are called “Classic Administration Templates;” however, the basic approach is the same as described here. For more details, see the document “Deploying Group Policy Using Windows Vista.”

Working with Local Group Policy Settings
You can work with local Group Policy settings using either a command prompt or by executing gpedit.msc. The latter opens the Group Policy Object Editor where you can view and edit the values for any Group Policy settings except those configured at domain or forest level (see Figure 1). Settings configured by administrators at higher levels cause the local setting to be disabled.

Notice in Figure 1 that Vista places .adm templates in the section named “Classic Administrative Templates.”

?
Figure 1. The Group Policy Editor: The figure shows the contents of the NetworkDNS Client administrative template and the DNS Servers setting within this template.
?
Figure 2. Editing Settings: The figure shows the dialog that lets you edit a setting in the Group Policy Editor.

To edit a setting, double-click on it (or right-click and select Properties) to display the settings dialog. You can specify whether each setting 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. An Explain tab displays information about the setting (see Figure 2).

Working with Domain Group Policy Settings
Group Policy is generally most effective when 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).

Author’s Note: You can run the GPMC on any Windows XP, Windows 2003 Server, or Windows Vista system within the domain, providing that your account has Domain Administrator privileges. You can download the GPMC from Microsoft.

Installing the GPMC adds the Group Policy Editor link to the Administrative Tools section of your Start menu. The editor (see Figure 3) provides a view of all the GPOs and settings available for the forest and domains. The left-hand tree contains the forests, domains, sites, and GPOs, while the right-hand pane contains four tabbed pages that let you 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 set to 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. Group Policy Management Console?Scope Tab: Here, the console is showing the Scope page for the GPO example used in this article.
?
Figure 4. Group Policy Management Console?Settings Tab: Here’s the Settings page showing the settings for the GPO example, for both computers and users.

Figure 4 shows the Settings page for the GPO example. Here, you can view the settings for all the items in this GPO. Notice that the list contains two sections (the items in blue text) named Computer Configuration and User Configuration. The GPO example 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.

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 here it applies at the forest and domain levels instead of the local machine level.

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

?
Figure 5. Editing Settings: Here are the settings for the custom domain-wide GPO shown in the Editor window.
?
Figure 6. Replicated Values: This view of the Windows Registry shows the machine values in the HKLM hive applied through domain-wide Group Policy. Note that this does not show the user-specific values in the HKCU hive.

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

Making Applications Group Policy Aware
To build group policy awareness into your applications, you must:

  • Create an appropriate administrative template that describes the Group Policy Object and the settings it contains.
  • Install the administrative template into Active Directory at forest/domain level, or at computer level if you only want it to apply to a specific computer.
  • Add code to your application that reads the settings from Windows registry, usually as part of the process of loading configuration information.

You will see how to achieve all 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 has a more arcane text format that is likely to be unfamiliar. However, it is easy to get used to, as long as you keep a syntax reference guide at hand.

Author’s Note: An excellent reference to the syntax and operation of templates is “Using Administrative Template Files with Registry-Based Group Policy.”

ADM Template Structure
An .adm file will usually contain three sections, as shown in the following code:

   CLASS MACHINE   CATEGORY !!GPConfigExample     KEYNAME "SoftwarePoliciesExamplesGPConfigExample"     ... category settings for computer configuration go here ...   END CATEGORY   ... more categories here as required ...      CLASS USER   CATEGORY !!GPConfigExample     KEYNAME "SoftwarePoliciesExamplesGPConfigExample"     ... 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 that gets stored in the HKEY_LOCAL_MACHINE registry hive, while categories in the CLASS USER section define policies for the User Configuration that gets 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 a registry key that will hold 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 points (!!) 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 "SoftwarePoliciesExamplesGPConfigExample"     POLICY !!DefaultUser       EXPLAIN !!DefaultUserExplain       PART !!DefaultUserNameText EDITTEXT REQUIRED VALUENAME           "UserName"       END PART       PART !!DefaultLocationText EDITTEXT VALUENAME           "UserLocation"
?
Figure 7. Generated Dialog Content: The figure shows the policy editor controls and content generated by the sample administrative template file.
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.

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:

  • EDITTEXT?creates a text box where the administrator can type in a value
  • NUMERIC?creates a text box for entering numeric values, and optionally “spin” controls to change the value
  • COMBOBOX?creates a combo box where the administrator can type in a value or select from a list of suggested values
  • DROPDOWNLIST?creates a drop-down list from which the administrator can select one value
  • LISTBOX?creates a single or two-column list of settings from which the administrator can select or edit values
  • CHECKBOX?creates a checkbox for a Yes/No or True/False setting
  • TEXT?creates non-editable text to display with other controls

You can see several of the controls in use in the GPO example. Listing 1 shows the complete administrative template for the GPO example.

Installing and Configuring Administrative Templates
Here’s the procedure 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 “Working with Local Group Policy Settings” for details). For forest/domain policies, use the GPMC (see the earlier section “Working with Domain Group Policy Settings” 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. The dialog saves values as soon as you close the edit dialog or move to another setting.

Reading Group Policy Settings from the Windows Registry
After you have created and installed a GPO, you must write application code to make your application aware of the settings and apply any values configured in that GPO. The .NET Framework Microsoft.Win32 namespace contains classes that make it easy to work with Windows Registry. 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. It’s good practice to close a key when you’re done using it. For example, the following code opens a key in the HKEY_LOCAL_MACHINE hive, reads the value, and then closes it:

   // 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 =       "SoftwarePoliciesExamplesGPConfigExample"   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

The RegistryKey.GetValue method 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 sub-keys. However, when reading Group Policy settings, you usually need to read the key value only as an Object; then you can convert it as required in your code.

A Group Policy-Aware Custom Configuration Provider
This final portion of the article describes how to make your applications Group Policy aware by integrating the techniques for creating and installing Group Policy templates and reading the Windows Registry. The downloadable sample application for this article contains a Group Policy-aware configuration provider for the Web.config file you saw in the first article in this series.

Creating a Group Policy Helper Class
You’ll find that reading from the Windows Registry is a regular and repeated task when making code Group Policy aware, so it makes sense to create a helper class that abstracts the process.

The example application contains a class named GPAwareHelper that contains a static/shared method named GetGPOverride. This method takes three parameters: a Boolean value that indicates whether the target hive is HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER, the key name, 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 code contains 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, except 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); otherwise it returns the default value passed to the method.

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 generated by the PARTS you define in an administrative template 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 0 (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 with an underlying Boolean type that has 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 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
Using the helper class described in the preceding section, it’s easy to modify the existing sample configuration provider 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 property value. For example, here’s 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 return value as the property value. In other words, if a Group Policy Object provides a value for this setting, the property accessor will return that setting rather than 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 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 sample 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, 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 sample 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:

        

Then, in the main body of Web.config, you’ll find 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; only the section element name is different:

                                                    

There are only two differences in the code-behind file for Default.aspx in the sample 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 that provider instead of the “normal” provider:

   protected void btnGetGPAware_Click(object sender, EventArgs e)   {      // Retrieve the  section from the       // configuration file      GPAwareConfigSection.ConnectionSettingsSection configSection         = (GPAwareConfigSection.ConnectionSettingsSection)         WebConfigurationManager.GetSection("GPAwareConnections");      // Get value of "autoConnect" attribute on       //  element      lblAutoConnect.Text = configSection.AutoConnect.ToString();      // Get value of "deviceMode" attribute on       //  element      lblDeviceMode.Text =          configSection.ConnectionItems.DeviceMode;      // Iterate through collection of  elements within       //        foreach (GPAwareConfigSection.ConnectionItemElement conn          in configSection.ConnectionItems)      {         // Get value of "connectionType" and "price" on          // each  element         lblConnectionItems.Text += conn.ConnectionType + " = "             + conn.Price.ToString() + "   ";      }      // Get the value of the attributes on the       //  element      lblDefaultUser.Text =          configSection.DefaultUser.DefaultUserName;      lblUserLocation.Text =          configSection.DefaultUser.DefaultUserLocation;      // display the identity of the current user      lblUserIdentity.Text = "Current user identity: "          + User.Identity.Name + "

"; }

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 sample 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. “Normal” Value: The figure shows the values in Web.config as exposed by the “normal” custom configuration provider.
?
Figure 9. “Group Policy-Aware Provider Value: The configuration values exposed by the Group Policy-aware configuration provider override Web.config values with the settings defined in Group Policy. This page also displays the current user identity.

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.

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 appear only in the HKEY_CURRENT_USER hive of the Registry, which gets loaded when Windows loads a user’s settings at logon. However, a server may not have a logged-on (current) user?and even if it does, the user account will not be the same as the account under which ASP.NET and your Web application are 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 in this case). 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. Running under IIS: When running under IIS rather than 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.

Manageability Is Key
In this article, you’ve seen 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 support centralized administration and control.

In the final article in this series, I’ll demonstrate how a “real world” application uses these techniques by showing how version 3.0 of Microsoft’s Enterprise Library provides the same kinds of centralized administration capabilities through its Manageable Configuration Provider mechanism.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: