How to automate IIS 7 Configuration with. NET
is tired of manually configuring IIS sites (adding Web sites, application pools, virtual directories and so on)? Use C # and the Microsoft.Web.Administration Namespace to simplify your life (or at least IIS configuration).
I maintain a number of Web sites outside my day job. Nothing major--a church web site, a small PHP web site for the salon my wife works at and a hot-lunch-ordering Web site For my kid ' s school. These sites is all hosted by the Web hosting companies, but I had a copy of all of the them set up on my development Maintenance and testing.
From time to time, I has to do a clean install of the My machine. It's either a new version of Windows or, most recently, a solid-state drive (SSD) that died on me. Either, when it comes to setting up IIS on my new environment, I grew tired of the myriad of settings and options that Had to is configured for all these sites. I decided to put my programming skills to work and automate this task.
Admins only!
An important point I ' d like to begin with:access to IIS Manager was restricted to Administrators. Therefore, any utilities your write using the techniques covered in this article must is executed by an administrator. Likewise, if you ' re going to do debugging of your utilities from inside visual Studio, you'll need to be running visual St Udio with Administrator access.
The sample code included in this article contains a complete example of programmatically creating an IIS site, complete WI Th virtual directories, application pools and security settings.
IIS 7 Information
To show your what's possible, I created a new Windows Forms application and used NuGet to install the Microsoft.Web.Adminis Tration package. Then I dropped a few lines of code into the Form_Load event. IT displays basic information for the local IIS server in a tree view, as can see in Listing 1.
Listing 1:form_load Event Showing information for Local IIS Server in a Tree View
using (var Servermanager = new Servermanager ()) { foreach (var site in servermanager.sites) { var sitenode = TV WIIS.NODES.ADD (String. Format ("site: {0}", site. Name)); foreach (var appl in site. applications) { var apppoolnode = siteNode.Nodes.Add (string. Format ("AppPool: {0}, Type: {1}", Appl. Applicationpoolname, getappltype (Servermanager, Appl)); foreach (Var virtdir in Appl. Virtualdirectories) { appPoolNode.Nodes.Add ("String.Format" ("Dir: {0} [{1}]", Virtdir.path, Virtdir.physicalpath));}}}
The utility function Getappltype is used to obtain the. NET runtime version based on the application Pool the application ' S using:
private string Getappltype (Servermanager SM, application appl) { var apppool = Sm. Applicationpools[appl. Applicationpoolname]; return apppool.managedruntimeversion;}
The result of running this on my machine was shown in Figure 1. It isn ' t going to win any UI design awards, but I hope you'll see how easy it's to query basic information about IIS and Its inner configuration. Now I ' m going to do some real work!
[Click on the image for larger view.]
Figure 1.The Basic Display of IIS Configuration
Creating a New Site
Now I want to automate the setup and configuration of my Web sites in IIS. The first thing to does is create the actual site. When I run the locally, each site was mapped to its own port in my local machine. I don ' t need to remember the port numbers because I had bookmarks for each one. However, when it comes-setting these up (manually), I had to remember the port numbers. The process of automating this have the added benefit of not have to change my bookmarks if I accidentally set up a site On the wrong port.
Site creation is easy, as shown in Listing 2.
Listing 2:site Creation in IIS
private static void Addsite (String siteName, string physicalpath, int portnumber) { using (var sm = new Servermanager () ) { var invalidchars = Sitecollection.invalidsitenamecharacters (); if (Sitename.indexofany (InvalidChars) >-1) { throw new Exception (String.Format ("Invalid Site Name: {0}", SiteName)); } var site = Sm. Sites.add (SiteName, PhysicalPath, portnumber); Site. ServerAutoStart = true; Sm.commitchanges (); }}
Note the SiteCollection object exposes a static char[] that contains a list of invalid site name characters. All of my sites has simple names that I know contain valid characters. But as you can see, I ' ve created a generic Addsite method that can is used in many situations. This extra check makes sure I don ' t accidentally the use of any invalid characters.
You should also see, the last line inside the using block was a call to the CommitChanges method on the Servermanager. All of the work-done before the just-done in-memory. If I forget to call CommitChanges, nothing was changed in IIS. The commitchanges call pushes the changes from memory into IIS and makes them live.
At here, my site is created and browsing to http://localhost:9898 works fine. However, I ' m not do yet. I like all of my sites to has their own application pool. This was mostly for isolation, but if you noticed in Figure 1, I still has one of the these old sites running under T The He Microsoft. NET Framework 2.0. If I don ' t provide the name of the application pool when I'm setting up my site, it uses the DefaultAppPool--which, on M Y is running the. NET Framework 4. So I ' m going to expand my Addsite method to support application Pool creation.
Creating an AppPool
When creating the application pool, I need to set three things:
- The application pool name. For this, I ' m going to default to the site name with "_apppool" appended to it.
- The. NET runtime version. All of my sites, except for one, is running the. NET Framework 4, so I guess I could make this the default if it isn ' t PR Ovided by the caller.
- The Managed Pipeline Mode. Again, this would normally default to "Integrated," but the old. NET 2.0 site needs "Classic." Like the runtime version, I'll create some overloads that default to Integrated.
When setting-the. NET runtime version, the ApplicationPool class takes in a string of either v2.0 or v4.0 as the runtime V Ersion. Microsoft probably used a string instead of an enum-to-plug in future versions of the runtime without have To update an enum. The Managed pipline mode, however, is exposed as an enum.
Here ' s my updated addsite signature:
void Addsite (String siteName, string physicalpath, int portnumber, string runtimeversion, Managedpipelinemode Pipelinemode)
After making sure the SiteName passed to Addsite are valid, I added some code to create the application pool:
... var poolname = siteName + "_apppool"; Addapppool (SM, poolname, RuntimeVersion, Pipelinemode);
The assignment of the application pool to my site is do through an application. A site can has many applications, but would always had at least one--and that one was automatically associated with the root directory of the site. Application pool assignment is do by using the app pool name, not a reference to an ApplicationPool object:
Site. Applications.first (). Applicationpoolname = poolname;
The Addapppool code is trivial:
private static void Addapppool (Servermanager sm, string poolname, String runtimeversion, Managedpipelinemode Piplinemode) { var apppool = Sm. Applicationpools.add (poolname); Apppool.managedruntimeversion = runtimeversion; Apppool.managedpipelinemode = Piplinemode;}
Basic setup for this site was now complete. With minimal effort, I ' ve got code to quickly set up my sites with specific configuration settings. This gets me percent of the there. One site has a virtual directory with be created, and a couple having some customizations to being made to the application pool. The current Addsite method already have five arguments, and I ' d hate to add more. Instead, I ' m going to refactor things a bit.
Customized configurations
Instead of sending a bunch of parameters as settings for the various properties of my IIS objects, I ' ll expose a lambda th At receives the IIS objects and allows the caller to set whatever it wants. Here's what my new addsite signature looks like:
void Addsite (String siteName, Action<servermanager, site> siteconfigurator, Action<servermanager, Applicationpool> apppoolconfigurator)
This gives callers of the "Full Control" Configure each item (site and app pool) the same as they like, but doesn ' t b Urden them with the repetitive code of creating the sites, app pools and committing changes.
My new code to set up the ' VSM ' site is now:
Addsite ("VSM", (Mgr, site) =>{ site. Setphysicalpath (@ "C:\TEMP\VSM"); Site. Bindtoport (9898); Site. ServerAutoStart = true;}, (Mgr, AppPool) =>{ apppool.managedruntimeversion = "V2.0"; Apppool.managedpipelinemode = Managedpipelinemode.classic;});
And Addsite have been updated to execute the callbacks during the site creation process, shown in Listing 3.
Listing 3:updating Addsite to Execute callbacks During Site Creation Process
private static void Addsite (String siteName, Action<servermanager, site> siteconfigurator, action< Servermanager, applicationpool> apppoolconfigurator) { using (var sm = new Servermanager ()) { var InvalidChars = Sitecollection.invalidsitenamecharacters (); if (Sitename.indexofany (InvalidChars) >-1) { throw new Exception (String.Format ("Invalid Site Name: {0}", SiteName)); } var poolname = siteName + "_apppool"; var AppPool = Sm. Applicationpools.add (poolname); Apppoolconfigurator (SM, apppool); var site = Sm. Sites.add (SiteName, "", 0); Site. Applications.first (). Applicationpoolname = poolname; Siteconfigurator (SM, site); Sm.commitchanges (); }}
Page 2
Notice that I no longer pass in the physical path and port number to the Addsite method. I wanted to keep the parameters passed to Addsite to a minimum. But this did create a problem-the Sitecollection.add method requires the physical path and port number. I changed the Sitecollection.add call to pass in a empty string for physical path and zero for port number. Instead, the physical path and port is set during my Site configuration callback:
(MGR, site) = = { site. Setphysicalpath (@ "C:\TEMP\VSM"); Site. Bindtoport (9898); Site. ServerAutoStart = true; }
The Site object doesn ' t has a method called Setphysicalpath, nor a method called Bindtoport. Those is extension methods I created to make the assignment of the those II items easier to perform.
As pointed out earlier, when a site ' s created, it automatically gets a application created for it. That ' s where I assigned the app pool name. In addition to an application being created, that application have a virtual directory automatically created (to map the RO OT directory), and an HTTP binding are created in the site ' s Bindings collection.
Knowing that, my extension methods simply grab those default objects and set their properties individually:
Static class extensions{public static void Setphysicalpath (this site site, string path) { Site. Applications.first (). Virtualdirectories.first (). PhysicalPath = path; } public static void Bindtoport (this site site, int port) { Site. Bindings.first (). Bindinginformation = String.Format ("*:{0}:", port);} }
Virtual Directories
I already touched on virtual directories a bit--there's a default one created for each site ' s root directory. One of my sites needs an additional virtual directory created, points to a folder outside the site ' s physical path.
To add a new one, I just had to grab the list of virtual directories from the site's application and add a new entry with A specific path and physical location:
Addsite ("VSM", (Mgr, site) =>{ ... Site. Applications.first (). Virtualdirectories.add ("/static", @ "C:\Temp\static"); ...}
The last part of my IIS site Creation automation is some more changes for the application pools.
Further AppPool Configuration
The site I maintain that still uses the. NET Framework 2.0 also have a DLL that must is run in 32-bit mode. Normally, from the IIS Manager GUI, I ' d go into the advanced Settings for my app pool and set the option to Enable 32-bit Applications (see Figure 2).
[Click on the image for larger view.]
Figure 2.Configuring APP pools via the GUI
I want this automated as well. Luckily, this was easy, as this functionality is exposed via a Boolean property. Here's my updated app pool configuration handler with the new code on the Second-to-last line:
(Mgr, apppool) = { apppool.managedruntimeversion = "V2.0"; Apppool.managedpipelinemode = Managedpipelinemode.classic; Apppool.enable32bitapponwin64 = true; }
Another thing that needs changing is the identity under which some of the app pools and virtual directories run. Noticed in Figure 1 , and all of these sites is under my Documents on my machine. Normally, the default identity of the ASP. Process doesn ' t has High-enough permissions to access my directory, So I need the identity under which the app pool and virtual directories run.
For the virtual directories, there ' s a username and password field I can set during the site creation. For app Pools, there's a ProcessModel property that exposes the identity information. While my setup currently have the same identity for all of the these, I can ' t assume it'll be and that's the IT forever. Therefore, I made a utility method to handle getting authentication information from the console, as shown in Listing 4.
Listing 4:utility Method
private static void Getauthinfo (string item, action<string, String> authhandler) { Console.WriteLine ("Please Enter authentication information for: {0} ", item); if (Allowblank) Console.WriteLine ("(Leave blank to use same credentials as last time)"); while (true) { Console.Write ("Username:"); var inputuser = Console.ReadLine (); if (Allowblank && string.isnullorempty (inputuser)) { authhandler (userName, password); return; } Console.Write ("Password:"); var Inputpassword = GetPassword (); if (! String.IsNullOrEmpty (Inputpassword)) { userName = Inputuser; Password = Inputpassword; Authhandler (userName, password); Allowblank = true; return;}}}
I had to craft a quick console-based password input method because the. NET Framework doesn ' t has one built-in. As you can see in Listing 5, it simply displays a asterisk as each character is typed.
Listing 5:console-based Password Input Method
private static string GetPassword () { var passwd = String.Empty; while (true) { var ki = Console.readkey (true); if (ki. Key = = Consolekey.enter) {break ; } if (ki. Key = = consolekey.backspace) { if (passwd. Length > 0) { passwd = passwd. Substring (0, passwd. LENGTH-1); Console.Write ("\b \b"); } } else { passwd + = (ki. KeyChar); Console.Write ("*"); } } return passwd;}
The Getauthinfo method gets a username and password from the console and hangs in to them for future use. Once It has a non-blank username and password, it calls the Authhandler delegate the it does it work.
For virtual directories, I just needed to set the UserName and Password properties:
var RootDir = site. Applications.first (). Virtualdirectories.first (); Getauthinfo ("Site" + site.) Name, (U, p) =>{ rootdir.username = u; Rootdir.password = P;});
For the app pool, I need to set the Identitytype property as well as UserName and Password on the ProcessModel associated With the app pool:
AppPool.ProcessModel.IdentityType = Processmodelidentitytype.specificuser; Getauthinfo ("AppPool" + Apppool.name, (U, p) =>{ appPool.ProcessModel.UserName = u; AppPool.ProcessModel.Password = P;});
Now my IIS setup is totally automated. I can reuse this code again and again to quickly get a site up and running with all the specific settings needed. I could also repurpose most of the This code for a simple setup application if I even needed to install the on a different MA Chine.
More Than Just Web Sites
The Microsoft.Web.Administration namespace can be used for a number of things. In this article, I reviewed how it can is used to automate the setup and configuration of the Web sites. It can also is used to query IIS – How many app pools is running the. NET Framework 2.0? How many aren ' t using the default AppPool Identity? I can also write code to start and stop a site or even a application pool. It's a handy little library that's might not get used much and when it's used, it can save a lot of time.
About the Author
Patrick Steele is a Senior. NET Developer with Billhighway in Troy, Michigan. A recognized expert on the. NET Framework, he's a Microsoft MVP award winner and a presenter at conferences and user Grou P meetings.
How to automate IIS 7 Configuration with. NET