White Papers                  Home |  White Papers  |  Message Board |  Search |  Products |  Purchase | News |  

 

Configuring IIS via code using the IISAdmin objects

 

by Rick Strahl

http://www.west-wind.com/

rstrahl@west-wind.com

 

Updated: 4/6/2001


Amazon Honor System Click Here to Pay Learn More

 

 

 

 

In this day and age Web applications have become the norm. We've even come to the point where many development projects build Web applications that need to be installed on multiple servers. But even if you don't build vertical Web applications, it's useful to build a configuration utility that can re-create a configuration via code. This maybe for backup purposes, or if you’re running in a high volume environment for situations such as load balancing where multiple servers need to be configured.

 

In order to accomplish this gracefully you need to be able to actually configure the Web server programmatically because the current crop of development tools do not provide that functionality as part of the development tools. In this article I'll demonstrate how you can configure Internet Information Server programmatically by using the Active Directory Service Interface (ADSI) and the IIS Admin objects to create an installation application. I'll show a Visual FoxPro class that provides several of the key elements needed for configuring a Web application so that you can handle these tasks via code that can be used to build set up Wizards or preset setup scripts.

What do we need to do to configure a Web server?

When you build a Web application there are many components that make up that application. You have your data engine along with its data files whether it be a SQL server of some sort, or whether it's a set of files if you're using a tool like Visual FoxPro or file based data from Access. There are the actual application binary files – the EXE or DLL of the application, plus any support files. If you're building a purely script based application you won't have this aspect. In a Web based application there are of course  HTML or scripted HTML (plain HTML or ASP, JSP, Cold Fusion or Web Connection script) files that need to be deployed in a Web directory.

 

Notice that in most Web application the HTML/script content will be separate from the binary and data content of the application. This means that the data and binary content are separate from the content in the Web directories. This means if you distribute a Web application you typically install into two locations: the application path and the Web path.

 

The Web path requires more work than a traditional application path, because it's sitting on the Web server and needs to be recognized as a Web path. It also needs certain permissions set so that the Web server can access the files there correctly.

 

A typical Web application needs to perform the following configuring tasks:

 

 

The IIS Admin Objects

Using the IIS Admin objects it's possible to configure the tasks mentioned above plus a lot more relatively painlessly. The IIS Admin objects are a COM object that let you connect to any resource that's available in the IIS metabase. The metabase is used to store all the IIS configuration settings. The IIS Admin objects are an Active Directory Service Interface that's specific to IIS and uses the common syntax used for any ADSI interface which includes a handful of method calls and properties, which are extended by the properties that the actual service – in this case the IIS Admin provider – exposes.

 

How this works is best illustrated by an example. The following code accesses the default Web site and retrieves a few of the properties available via the IIS Admin objects:

 

*** Connect to the Root directory of the first site

oVirtual = GETOBJECT("IIS://LOCALHOST/W3SVC/1/ROOT")

 

? oVirtual.Class           && IIsWebVirtualDir

 

? oVirtual.Path               && d:\inetpub\wwwroot

? oVirtual.AnonymousUserName  && IUSR_<Machine>

? oVirtual.AuthBasic          && Basic Authentication flag

? oVirtual.AccessExecute      && Execute rights

 

*** Configure settings

oVirtual.Path = "d:\wwindweb"

oVirtual.AuthBasic = .T.

oVirtual.AccessExecute = .T.

 

oVirtual.SetInfo()            && Save Settings

 

This short snippet connects to the Web root directory of the default Web site and reads a few settings. Most configuration settings of value are found at the virtual directory level. The hierarchy of the IIS Admin objects starts with:

 

IISWebService

GETOBJECT("IIS://LOCALHOST/W3SVC/")

Contains many settings similar to the virtual directory settings, but actually doesn't let you control them. This object contains many performance options however – typically you'll only use this for very specific things. The Web service above is W3SVC in the GETOBJECT moniker string above.

IISWebServer

GETOBJECT("IIS://LOCALHOST/W3SVC/1")

The individual Web sites which can be enumerated. The 1 in the GETOBJECT moniker above identifies the site in question. Sites are numbered sequentially and must be enumerated in order to retrieve a friendly name (I'll show an example of that shortly). This object also serves mainly as a performance and high level settings place holder. Although it has many of the same settings as IISWebVirtualDir, many of these settings don't do anything. The performance options do however.

 

IISWebVirtualDir

GETOBJECT("IIS://LOCALHOST/W3SVC/1/ROOT")      && Root directory       

GETOBJECT("IIS://LOCALHOST/W3SVC/1/WCONNECT")  && Specific Virtual

The virtual directories including the ROOT directory contain most of the important settings that you need to make to configure a Web site.

 

As a general rule you want to set any inherited settings at the lowest possible level. So setting execute rights should be done at the virtual level not at the Web server level. Performance settings that are available both in the WebService and WebServer objects should be made on the WebServer object. For a detailed list of properties available for each of these objects see the MSDN documentation for the IIS Admin objects – a Web link to this topic is provided at the end of this article.

 

Looking at the example above again you can see that you can read settings and also write settings simply by assigning values to the appropriate property. Make sure that you call SetInfo() to actually write the settings into the IIS metabase. Until you do the settings are cached and don't actually change the operation of IIS. Once SetInfo() has been called the settings are written and take effect immediately.

We've got some configuring to do

Ok now that we have the basic idea for how to get our Web service let's set up a class for this and show some specific tasks that you'll be wanting to accomplish. What I will show here is a class called wwIISAdmin. Here are a few examples of how you might use it:

 

SET CLASSLIB TO WEBSERVER ADDIT

 

lcPath = "d:\westwind\CodeDemo\"

 

*** Some code to create a dummy directory and

*** copy an ISAPI DLL there

IF !ISDIR(lcPath)

   MD (lcPath)

   COPY FILE scripts\wc.dll TO (lcPath) + "wc.dll"

ENDIF

 

oIIS = CREATEOBJECT("wwIISAdmin")

 

*** Retrieve a list of all Web sites into laVirtuals

DIMENSION laVirtuals[1,3] 

 

*** 1 - Site ID Number

*** 2 - Site Name

*** 3 - Site ADSI Path

lnCount = oIIS.aGetWebSites(@laVirtuals, "IIS://LOCALHOST/W3SVC")

 

*** Get the Web path to our site

lcWebPath = ""

FOR x=1 to lnCount

   IF UPPER(laVirtuals[x,2]) = "WEST WIND"

      lcWebPath = laVirtuals[x,3]

   ENDIF

ENDFOR

 

IF EMPTY(lcWebPath)

   RETURN

ENDIF

 

*** Assign the ROOT path of the Web site

oIIS.cPath = lcWebPath + "/ROOT"

 

*** Create the new virtual directory under the root

oIIS.CreateVirtual("CodeDemoVirtual",lcPath)

 

*** Set additional options on the

oIIS.oRef.AuthBasic = .F.

oIIS.oRef.AccessExecute = .T.

 

*** Save the setting changes on the oRef object

oIIS.Save()

 

*** Create a mapping for wwwc to wc.dll ISAPI DLL

oIIS.CreateScriptMap("wwwc",lcPath + "wc.dll")

 

This simple code demonstrates the basics of what happens in common Web installs: You create a new directory, copy some files to it, then you create a Web virtual directory and configure that directory as needed. Optionally in this example I'm also creating a script map that maps an ISAPI DLL to the WWWC extension so that any requests against this extension are routed to the specified DLL.

 

Figure 1 – A Wizard like interface can be perfect to let the user choose the Web server and Web site.

 

The next step once you've figured out which site to pick is to create a virtual directory or update settings on the ROOT directory of the server. If you're working on an existing directory you can simply use GETOBJECT() directly to manipulate that object as shown in the first example earlier.

 

If you need to create it, you can use the wwIISAdmin object. Since I have a pretty standard procedure and set of settings I use for virtuals created in my installs I also provide a set of common settings into the CreateVirtual method of the wwIISAdmin class. Take a look:

 

FUNCTION CreateVirtual

LPARAMETERS lcVirtual, lcPhysical, llNoExecute, llNoAuthBasic

LOCAL lcPath, loVirtual

 

THIS.lError = .F.

 

THIS.oRef = GETOBJECT(THIS.cpath)

IF ISNULL(THIS.oRef)

   THIS.cerrormsg = "Unable to connect to server root."

   RETURN .F.

ENDIF

 

IF EMPTY(lcPhysical)

   *** Delete the Virtual

   THIS.oRef.DELETE("IIsWebVirtualDir",lcVirtual)

   THIS.SAVE()

   IF THIS.lError

      RETURN .F.

   ENDIF

   RETURN .T.

ELSE

   *** Try to create it

   loVirtual = THIS.oRef.CREATE("IIsWebVirtualDir",lcVirtual)

 

   *** If an error occurred it might exist already

   IF THIS.lError OR TYPE("loVirtual") # "O"

      THIS.lError=.F.

      lcPath = THIS.cpath  && Our current relative path

 

      *** ADd the virtual path to it and try to connect

      loVirtual = GETOBJECT(lcPath + "/" + lcVirtual)

      IF THIS.lError

         *** Still an error - reconnect to the original path

         *** and exit

         THIS.oRef = GETOBJECT(lcPath)

         RETURN .F.

      ENDIF

   ENDIF

 

   loVirtual.PATH = lcPhysical

   loVirtual.AppCreate(.T.)  && Make sure our app is In Process

   loVirtual.AppFriendlyName = lcVirtual

 

   loVirtual.AccessRead = .T.

   loVirtual.AccessExecute = !llNoExecute

   loVirtual.AuthBasic = !llNoAuthBasic

   loVirtual.AuthNTLM = .T.

 

   THIS.OnCreateVirtual(loVirtual)

 

   loVirtual.SetInfo()

 

   *** Pass out the reference for the directory

   THIS.oRef=loVirtual

ENDIF

 

RETURN .T.

 

This code goes out and goes to the directory on the Web server that you want to create the virtual under first, and then tries to create it. If the virtual exists already, an error will occur internally, which is captured and sets the lError flag. This flag is checked and if .T. the assumption is that the directory already exists. In that case the code simply tries to access that directory rather than creating it. Once create or accessed again, a reference to this object exists and several default settings are applied, the most important of which is the directory that the virtual points to.

 

Notice that the OnCreateVirtual method is called after this process is complete to allow you to customize the virtual creation process with your on post processing behavior by overriding the class. Alternately, as shown in the example code above you can simply exit the method and use the oRef member to change any settings after the fact. The bottom line is that you don't give up any flexibility here as you still get a reference to the base ADSI object to do as you will. While the basic concept of creating a virtual is easy you can see that a fair amount of code is required to do it right. Hence this wrapper class in the first place to provide the error handling and additional functionality of presetting defaults.

 

In an application it tends to be a good idea to let the user pick the location for the files and name of the virtual and a typical interface is shown in Figure 2.

 

Figure 2 – it's a good idea to let the end-user/administrator choose the directory and name of the virtual of where your files will go.

Dealing with ADSI Collections in VFP

Looking at the ADSI code here you can see that manipulation of the IIS Admin objects is pretty straight forward. However, one aspect in VFP has always been tricky and that is dealing with collections such as those used for scriptmap extensions for the Web root (or individual web directory). The following code demonstrates manipulating the scriptmaps collection for creation of a new script map:

 

FUNCTION CreateScriptMap

LPARAMETERS lcScriptMap, lcPath

LOCAL lnResult, x, cScriptMap, loScriptmaps

 

*** Fix up script map entry

IF lcScriptMap <> "."

   lcScriptMap = "."+lcScriptMap

ENDIF

 

lcScriptMap = LOWER(lcScriptMap)

lcPath = LOWER(lcPath)

 

THIS.lerror = .F.

 

*** Get a reference to the Web path to work on

THIS.oRef=GETOBJECT(THIS.cPath)

IF ISNULL(THIS.oRef)

   THIS.ErrorMsg = "Unable to access the default web root"

   RETURN .F.

ENDIF

 

*** Make sure we're using 0 based arrays by Value

COMARRAY(THIS.oRef,0)

loScriptmaps = THIS.oRef.scriptmaps

 

x=0

FOR EACH cScriptMap IN loScriptmaps

   x=x+1

 

   *** Check if we need to UPDATE an existing script map

   IF LOWER(LEFT(cScriptMap,LEN(lcScriptMap))) = lcScriptMap

      loScriptmaps[x] = lcScriptMap + "," + lcPath + ",1"

      THIS.oRef.PutEx(2,"ScriptMaps",@loScriptmaps)

      THIS.oRef.SetInfo()

 

      loScriptmaps = .NULL.

      THIS.oRef = .NULL.

      RETURN .T.

   ENDIF

 

   *** Just in case there's a problem

   IF x > 200

      EXIT

   ENDIF

 

ENDFOR

 

*** Add another item

x=x+1

DIMENSION loScriptmaps[x]

loScriptmaps[x] = lcScriptMap + "," + lcPath + ",1"

 

THIS.oRef.PutEx(2,"ScriptMaps",@loScriptmaps)

THIS.oRef.SetInfo()

 

loScriptmaps = .NULL.

THIS.oRef = .NULL.

RETURN .T..

 

Note the use of the COMARRAY function to set the array to 0 based, which is necessary for VFP to access and write the array data. This makes the array 0 based but lets VFP continue to treat it as an array starting with item [1].

 

This code runs through all the existing scriptmaps to check and see whether it already exists. If it does it's overwritten rather than added again. ADSI would (invalidly) allow duplicate scriptmapts so this code is necessary. Note that in order to save any changes to the script map array, we need to save with the PutEx() ADSI method, which is passed a value of 2 (array), the key and the actual value – in this case our scriptmap array passed by reference (actually because of the COMARRAY setting the array is passed by value over the COM boundary).

 

You can use this same type a code with any of the collections that the IIS Metabase provides such as the list of ISAPI Filters for example.

Summary

I hope this article has given you the basics you need to start building your own configuration programs for your Web applications. It's highly useful to be able to build an install script to quickly re-create a setup on a Web server or build a full-featured install for a Web application that gets installed in multiple locations.


The classes I provided let you easily do most of the work with IIS. Provided in the zip file of this class is also a more high level class called wwWebServer which provides functionality for IIS4 and 5, IIS3 (registry based), Personal Web Server  on Win9x, Apache, and O'Reilly's Web site, so you can build installs that can install on any of these Web servers providing most of the functionality I've described here. IIS is by far the most flexible platform to install on but if you have a product that can run on other servers this can come in handy.

 

Reference:

 

West Wind Web Server Configuration Class

  http://www.west-wind.com/presentations/WebServerConfig/WebServerConfig.zip

 

 

IIS Admin Object Reference

  http://msdn.microsoft.com/library/default.asp?URL=/library/psdk/iisref/aore7zn6.htm

 

 

Rick Strahl is president of West Wind Technologies on Maui, Hawaii. The  company specializes in Internet application development and tools focused on Internet Information Server, ISAPI, C++ and Visual FoxPro. Rick is author of West Wind Web Connection, a powerful and widely used Web application framework for Visual FoxPro, West Wind HTML Help Builder, co-author of Visual WebBuilder, a Microsoft Most Valuable Professional, and a frequent contributor to FoxPro magazines and books. His book "Internet Applications with Visual FoxPro 6.0", was published April 1999 by Hentzenwerke Publishing. For more information please visit: http://www.west-wind.com/.

  White Papers                  Home |  White Papers  |  Message Board |  Search |  Products |  Purchase | News |