Overview

This article describes how to create a new web site on IIS using ASP.NET and C#.

Description

Before going into the article, I just want to brief about the client requirement. It is a Content Management System which allows users to create new websites and host it on the server. Users can browse through the available domain names via a third party API and select a domain name and choose to host it on the server and then a new ASP.NET website should be created on IIS automatically and run within no time.

For each new website request, administrator cannot open IIS to create a new ASP.NET website. We require an instant solution where user enters website name and a new website should be created automatically on IIS with required settings like host name, local path, default document, etc., similar to the settings which a user does manually through wizard on the server.

Environment: Windows Server 2003, IIS 6.0, ASP.NET 2.0

For demo purposes, user interface is .aspx page with just a text box to enter website name and a submit button.

On the server side, web service does the job. It takes the website name as parameter and creates new website on IIS. And a point to be noted is all the new websites created point to a same physical location and depending upon the name of the website, content gets populated. If you need to configure different physical paths for each website, send an additional parameter to the web service.

Let’s start the job now

  1. Create a new “ASP.NET Web Service” with Language as “C#”.
  2. Open “web.config” file, add below configuration values under “appSettings” section.
    <appSettings>
      <clear/>
      <!---Default format is IIS://<your server name>/W3SVC-->
      <add key="metabasePath" value="IIS://C37336-113134/W3SVC"/>
      <!--Framework version of newly created website-->
      <add key="frameworkVersion" value="2.0.50727"/>
      <!---Local path of newly created website -->
      <add key="defaultFileLocation" value="C:\Test"/>
      <!---Application Pool of newly created website-->
      <add key="defaultAppPool" value="CustomAppPool"/>
    </appSettings>

     

  3. Add Reference to “System.DirectoryServices” library and navigate to .cs file and import the namespaces “using System.DirectoryServices”.
  4. Create a new web method “CreateWebsite”
  5. /// <summary>
    /// Creates new ASP.NET website on IIS
    /// </summary>
    /// <param name="siteName"></param>
    /// <returns></returns>
    [WebMethod]
    public string CreateWebsite(string siteName)
    {
      try
      {
         //Initialize configuration variables
         string metabasePath =  Convert.ToString(ConfigurationManager.AppSettings["metabasePath"]);
         string frameworkVersion = Convert.ToString(ConfigurationManager.AppSettings["frameworkVersion"].ToString());
         string physicalPath = Convert.ToString(ConfigurationManager.AppSettings["defaultFileLocation"].ToString());
         string defaultAppPool = ConfigurationManager.AppSettings["defaultAppPool"].ToString();//Host Header Info
         object[] hosts = new object[2];
         string hostHeader = siteName.Replace("www.", string.Empty).Replace(".com",string.Empty);
         hosts[0] = ":80:" + hostHeader+".com";
         hosts[1] = ":80:" + "www." + hostHeader + ".com";
    
         //Gets unique site id for the new website
         int siteId = GetUniqueSiteId(metabasePath);
    
         //Extracts the directory entry
         DirectoryEntry objDirEntry = new DirectoryEntry(metabasePath);
         string className = objDirEntry.SchemaClassName;
         if (!className.EndsWith("Service")) return "Invalid configuration variables";
    
         //creates new website by specifying site name and host header
         DirectoryEntry newSite = objDirEntry.Children.Add(Convert.ToString(siteId), (className.Replace("Service", "Server")));
         newSite.Properties["ServerComment"][0] = siteName;
         newSite.Properties["ServerBindings"].Value = hosts;
         newSite.Invoke("Put", "ServerAutoStart", 1);
         newSite.Invoke("Put", "ServerSize", 1);
         newSite.CommitChanges();
    
         //Creates root directory by specifying the local path, default  document and permissions
         DirectoryEntry newSiteVDir = newSite.Children.Add("Root", "IIsWebVirtualDir");
         newSiteVDir.Properties["Path"][0] = physicalPath;
         newSiteVDir.Properties["EnableDefaultDoc"][0] = true;
         newSiteVDir.Properties["DefaultDoc"].Value = "default.aspx";
         newSiteVDir.Properties["AppIsolated"][0] = 2;
         newSiteVDir.Properties["AccessRead"][0] = true;
         newSiteVDir.Properties["AccessWrite"][0] = false;
         newSiteVDir.Properties["AccessScript"][0] = true;
         newSiteVDir.Properties["AccessFlags"].Value = 513;
         newSiteVDir.Properties["AppRoot"][0] = @"/LM/W3SVC/" + Convert.ToString(siteId) + "/Root";
         newSiteVDir.Properties["AppPoolId"].Value = defaultAppPool;
         newSiteVDir.Properties["AuthNTLM"][0] = true;
         newSiteVDir.Properties["AuthAnonymous"][0] = true;
         newSiteVDir.CommitChanges();
    
         //Sets the framework version to 2.0 for the new website
         //Assuming the version will be something like n.n.nnnnn
         Regex versionRegex = new Regex(@"(?<=\\v)\d{1}\.\d{1}\.\d{1,5}(?=\\)");
         PropertyValueCollection lstScriptMaps =  newSiteVDir.Properties["ScriptMaps"];
         System.Collections.ArrayList arrScriptMaps = new   System.Collections.ArrayList();
          foreach (string scriptMap in lstScriptMaps)
          {
              if (scriptMap.Contains("Framework"))
              {
                 arrScriptMaps.Add(versionRegex.Replace(scriptMap, frameworkVersion));
              }
              else
             {
                 arrScriptMaps.Add(scriptMap);
              }
           }
          newSiteVDir.Properties["ScriptMaps"].Value =  arrScriptMaps.ToArray();
          newSiteVDir.CommitChanges();
          return "Website created successfully.";
      }
      catch (Exception ex)
      {
          return "Website creation failed. <br/>" + ex.Message;
      }
    }
    
    /// <summary>
    /// Retunrs Unique SiteId for the new website
    /// </summary>
    /// <param name="metabasePath"></param>
    /// <returns></returns>
    private int GetUniqueSiteId(string metabasePath)
    {
         int siteId = 1;
         DirectoryEntry objDirEntry = new DirectoryEntry(metabasePath);
         foreach (DirectoryEntry e in objDirEntry.Children)
         {
            if (e.SchemaClassName == "IIsWebServer")
            {
                int id = Convert.ToInt32(e.Name);
                if (id >= siteId)
                siteId = id + 1;
             }
          }
         return siteId;
    }
    
  6. Now take a new “ASP.NET website” and add the web reference to the created web service.
  7. In default.aspx, place a textbox control and button control and in button click event handler, call the web method by passing in textbox value.
    Service createWebsiteService = new Service();
    lblStatus.Text = createWebsiteService.CreateWebsite(txtSiteName.Text);
    
  8. Now get ready to run the application and enter a website name “www.stweet.com” in the textbox and click the button. OOPsssss!!!
  9. Access is denied. (Exception from HRESULT: 0×80070005 (E_ACCESSDENIED))

    Don’t worry. We are not yet done with required configuration part yet.

Configuration

Basically for creating a new website on IIS, user requires administrator privileges. But by default ASP.NET application runs under low privileged account i.e., Network Service, so additional settings needs to be done to run the above mentioned code.

Method 1

Remember, we have used two ASP.NET applications for this, one for website and another for web service. Now web service needs to be run under administrative privileges. Add below code in the “web.config” file under “system.web” section.

<identity impersonate="true" userName="adminname" password="adminpwd"/>

It impersonates the user request with administrator privileges and thus executes the code successfully.

Method 2

Typing administrator user name and password in web.config may not be feasible and less secured. No problem! We can do it in other way by creating a new application pool for the web service as explained below.

  1. Open IIS
  2. Right click on Application Pool > select “New” > select “Application Pool”
  3. Enter “Application Pool ID” and click OK. New application pool is created
  4. Right click on newly created application pool > select “Properties”
  5. Navigate to “Identity” tab, choose “configurable” and enter administrator user name and password and click “OK”
  6. Now configure web service to run under newly created application pool. Right click on already created web service>>Select “Properties” and navigate to “Home Directory” tab >>Select newly create application pool under “Application Pool” field

Note: Be careful with step no. 5 and 6, configuration issues occur while running the web service. This happens when invalid password is entered under the administrator account in application pool. In this case, modifying the administrator password doesn’t help. We need to again start configuration from beginning by removing the web service association with application domain and deleting the application domain and creating a new one with correct credentials.

For both methods, it is preferred to have new admin account which serves only this purpose and make sure he is member of  IIS_WPG user group. Because due to security reasons, password of existing administrator may change from time to time and for every reset we need to again re-configure the application pool.

That’s it. Now run the application and see the result.

Additional Inputs

  1. If time is not crucial part for new website creation on IIS, consider storing the website names in a database and implement the code written in the web service in a windows application (or) windows service and schedule it, so that the configuration part can be skipped.
  2. On Load Balancing servers, this won’t work because we don’t know where the request is routed. In this scenario, windows application/service is preferred.

Conclusion

This article demonstrated one of the concepts to create a new website on IIS programmatically using ASP.NET and C#.

Note: It is just the concept which is explained in this article but doesn’t include additional things like exception handling & web service security. Please feel to modify it according to your requirements.

It is my first article, please bear with me and it would be great help if you guys provide feedback and help me improve the content.

About these ads