Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Use C# to manage IIS

0.00/5 (No votes)
6 Aug 2010 3  
Use C# to manage IIS

Copyright

IISManager is a product of luckzj. Anyone could use this product for free but without commercial purpose. If there’s any problem using IISManager, feel free to contact me at luckzj12@163.com. Or visit my website http://soft-bin.com.

The original address of this article is:

Introduction

We can use IIS management tools provided by Microsoft to manage our IIS server. However, sometimes we need to manipulate our IIS server inside our application. For example, to deploy our website related applications. I will give a brief on how to manage IIS server using C#. Along with that, I will also provide an IIS management DLL called IISManager and its source code writing in C# language.

The target version of IIS discussed in this article is IIS6.0 or higher. Version of IIS on Windows Xp is IIS 5.1 which cannot meet this requirement. However, you can also use IISManager to manage it under the condition that you don't do operations unsupported by IIS 5.1.

IISManager can only provide website server management classes for the moment. However, I would continue to work with it and make it a perfect tool to manipulate IIS servers.

I will start this article with Active Directory Service.

Active Directory Service

I will use Active Directory Service (ADS) to manipulate IIS server. No further discussion would be made about ADS for there’s already too many introductions on the web. I would only discuss how to use it here.

To get access to Active Directory Service, we would use classes under the namespace System.DirectoryServices which is defined in .NET assembly System.DirectoryServices. So we need to add this assembly to our project.

The most important class for IISManager in System.DirectoryServices is DirectoryEntry. We can take DirectoryEntry as a node which contains a set of properties, methods and child nodes. We can use DirectoryEntry.Properties[key] to access properties and use DirectoryEntry.Invoke to access methods of the node. We can get the prototype name of the node with DirectoryEntry.SchemaClassName.

The constructor of DirectoryEntry accepts one parameter which represents an ADS node. I will use an IIS ADS node to construct a DirectoryEntry instance and use it to access IIS servers.

Open IIS website with DirectoryEntry

We can use the following way to get a DirectoryEntry instance to access IIS Active DirectoryServices (ADS):

DirectoryEntry Services = new DirectoryEntry("IIS://localhost/W3SVC");  

IIS://localhost/W3SVC” is the name of IIS ADS.

We just created an IIS ADS server object. There may be many websites on this node, we must find the website we are looking for:

private DirectroyEntry websiteEntry = null;
internal const string IIsWebServer = "IIsWebServer";
 
protected IISWebsite(DirectoryEntry Server)
{
    websiteEntry = Server;
}
 
public static IISWebsite OpenWebsite(string name)
{
    // get directory service
    DirectoryEntry Services = new DirectoryEntry("IIS://localhost/W3SVC");
    IEnumerator ie = Services.Children.GetEnumerator();
    DirectoryEntry Server = null;
 
    // find iis website
    while (ie.MoveNext())
    {
        Server = (DirectoryEntry)ie.Current;
        if (Server.SchemaClassName == IIsWebServer)
        {
            // "ServerComment" means name
            if (Server.Properties["ServerComment"][0].ToString() == name)
            {
                return new IISWebsite(Server);
                break;
             }
        }
    }
 
    return null;
} 

I use IISWebsite to represent a IIS website and use static method ISWebsite.OpenWebsite() to get an instance of this class.

DirectoryEntry.SchemaClassName means the prototype of the node. The prototype name of IISWebServer is IISWebServer. We would find the website with the specified name. However, DirectoryEntry.Name is not the name of the website, but the name of the node. To get the name of the website, we should use DirectoryEntry.Properties["ServerComment"]. Each property in DirectoryEntry.Properties is represented as a collection, we should use foreach to access members of a property. However, we know that a website must have a name, so we use Server.Properties["ServerComment"][0] to get the real name of this website.

Create a Website

To create a website, we need to add a new node to “IIS://localhost/W3SVC”. I will list the source code and give the explanation later.

/// <summary>
/// create a new website
/// </summary>
/// <param name="name">website name</param>
/// <param name="port">website port</param>
/// <param name="rootPath">root path</param>
/// <returns></returns>
public static IISWebsite CreateWebsite
	(string name, int port, string rootPath, string appPool)
{
    // validate root path
    if (System.IO.Directory.Exists(rootPath) == false)
    {
        throw new DirNotFoundException(rootPath);
    }
 
    // get directory service
    DirectoryEntry Services = new DirectoryEntry("IIS://localhost/W3SVC");
 
    // get server name (index)
    int index = 0;
    foreach (DirectoryEntry server in Services.Children)
    {
        if (server.SchemaClassName == "IIsWebServer")
        {
            if (server.Properties["ServerComment"][0].ToString() == name)
            {
                throw new Exception("website:" + name + " already exists.");
            }
 
            if (Convert.ToInt32(server.Name) > index)
            {
                index = Convert.ToInt32(server.Name);
            }
        }
    }
    index++; // new index created
 
    // create website
    DirectoryEntry Server = Services.Children.Add(index.ToString(), IIsWebServer);
    Server.Properties["ServerComment"].Clear();
    Server.Properties["ServerComment"].Add(name);
    Server.Properties["Serverbindings"].Clear();
    Server.Properties["Serverbindings"].Add(":" + port + ":");
 
    // create ROOT for website
    DirectoryEntry root = Server.Children.Add("ROOT", IISWebVirturalDir.IIsVirtualDir);
    root.Properties["path"].Clear();
    root.Properties["path"].Add(rootPath);
 
    // create application
    if (string.IsNullOrEmpty(appPool))
    {
        root.Invoke("appCreate", 0);
    }
    else
    {
        // use application pool
        root.Invoke("appCreate3", 0, appPool, true);
    }
 
    root.Properties["AppFriendlyName"].Clear();
    root.Properties["AppIsolated"].Clear();
    root.Properties["AccessFlags"].Clear();
    root.Properties["FrontPageWeb"].Clear();
    root.Properties["AppFriendlyName"].Add(root.Name);
    root.Properties["AppIsolated"].Add(2);
    root.Properties["AccessFlags"].Add(513);
    root.Properties["FrontPageWeb"].Add(1);
 
    // commit changes
    root.CommitChanges();
    Server.CommitChanges();
 
    // return the newly created website
    IISWebsite website = new IISWebsite(Server);
    return website;
} 

It has already been clarified that the name of the node does not mean the name of the website. Actually, for a website node, name of the node is an integer. So when I'm trying to create a new website node, I should first find out the largest integer that exists and add 1 to it which makes our new website node name.

To add a node seems easy:

DirectoryEntry Server = Services.Children.Add(index.ToString(), IIsWebServer); 

Then, we should set properties for this website including website name and port.

Website name:

node.Properties["ServerComment"].Add(name)

Server port:

The property name for server port is “ServerBindings”. However, we should add “:” to the real port at the front and rear of the port string. “:8080:” for example. I don't know why Microsoft did this, I just do what they told me to do.

So far, we created a website. However, a website must have a root directory:

// create ROOT for website
DirectoryEntry root = Server.Children.Add("ROOT", IISWebVirturalDir.IIsVirtualDir);
root.Properties["path"].Clear();
root.Properties["path"].Add(rootPath);
 
// create application
if (string.IsNullOrEmpty(appPool))
{
    root.Invoke("appCreate", 0);
}
else
{
    // use application pool
    root.Invoke("appCreate3", 0, appPool, true);
 }
 
root.Properties["AppFriendlyName"].Clear();
root.Properties["AppIsolated"].Clear();
root.Properties["AccessFlags"].Clear();
root.Properties["FrontPageWeb"].Clear();
root.Properties["AppFriendlyName"].Add(root.Name);
root.Properties["AppIsolated"].Add(2);
root.Properties["AccessFlags"].Add(513);
root.Properties["FrontPageWeb"].Add(1); 

A website root is a child node of the website named “ROOT” and with a SchemaClassName of IIsWebVirtualDir. I also created a class IISWebVirtualDir to manipulate virtual directories. I will discuss it later. All we should know here is that we created a root virtual directory for our website.

Virtual Directory of a Website

Every website has virtual directories, at least a root virtual directory as discussed above. The other virtual directories should be sub nodes of the root virtual directory. I created the class IISWebVirtualDir to represent virtual directories.

To access the root directory of a website, we can use IISWebsite.Root:

/// <summary>
/// Root path
/// </summary>
public IISWebVirturalDir Root
{
    get
    {
        foreach (DirectoryEntry entry in websiteEntry.Children)
        {
            if (entry.SchemaClassName == IISWebVirturalDir.IIsVirtualDir)
            {
                return new IISWebVirturalDir(entry);
            }
        }
 
        throw new WebsiteWithoutRootException(this.Name);
    }
} 

With a root, we can use IISWebVirtualDir.OpenSubVirtualDir to get sub virtual directories.

/// <summary>
/// Open a sub virtual directory
/// </summary>
/// <param name="name">Name of directory to be opened. Case insensitive.</param>
/// <returns>A IISWebVirtualDir instance if open successfully done.Otherwise null.
/// </returns>
public IISWebVirturalDir OpenSubVirtualDir(string name)
{
    DirectoryEntry entry = this.FindSubEntry(name);
 
    if (entry == null)
    {
        return null;
    }
 
    return new IISWebVirturalDir(entry);
} 

We can create a sub virtual directory using IISWebVirtualDir.CreateSubVirtualDir:

/// <summary>
/// Create a sub virtual directory
/// </summary>
/// <param name="name">Name of the sub virtual directory to be created.</param>
/// <param name="path">Path of the sub virtual directory.</param>
/// <param name="appPool">
/// Application pool. Application pool with this name would be created if not exist.
/// Use string.Empty or null to this parameter 
/// if you don't want to use a application pool.
/// </param>
/// <returns>A IISWebVirtualDir if created. Otherwise  null.</returns>
public IISWebVirturalDir CreateSubVirtualDir(string name, string path, string appPool)
{
    // already exist
    if (this.ExistVirtualDir(name))
    {
        throw new VirtualDirAlreadyExistException(this._entry, path);
    }
 
    // validate path
    if (System.IO.Directory.Exists(path) == false)
    {
        throw new DirNotFoundException(path);
    }
 
    DirectoryEntry entry = this._entry.Children.Add(name, IIsVirtualDir);
    entry.Properties["path"].Clear();
    entry.Properties["path"].Add(path);
 
    // create application
    if (string.IsNullOrEmpty(appPool))
    {
        entry.Invoke("appCreate", 0);
    }
    else
    {
        // use application pool
        entry.Invoke("appCreate3", 0, appPool, true);
    }
 
    entry.Properties["AppFriendlyName"].Clear();
    entry.Properties["AppIsolated"].Clear();
    entry.Properties["AccessFlags"].Clear();
    entry.Properties["FrontPageWeb"].Clear();
    entry.Properties["AppFriendlyName"].Add(this._entry.Name);
    entry.Properties["AppIsolated"].Add(2);
    entry.Properties["AccessFlags"].Add(513);
    entry.Properties["FrontPageWeb"].Add(1);
 
    entry.CommitChanges();
    return new IISWebVirturalDir(entry);
}

Application Pool

We also use DirectoryEntry to access application pools. The root node of application pool is “IIS://localhost/W3SVC/AppPools/”.

I wrote a class IISAppPool to manage application pools. We can use static method IISAppPool.OpenAppPool to open an application pool or IISAppPool.CreateAppPool to create an application pool.

/// <summary>
/// Open a application pool and return an IISAppPool instance
/// </summary>
/// <param name="name">application pool name</param>
/// <returns>IISAppPool object</returns>
public static IISAppPool OpenAppPool(string name)
{
    string connectStr = "IIS://localhost/W3SVC/AppPools/";
    connectStr += name;
 
    if (IISAppPool.Exsit(name) == false)
    {
        return null;
    }
 
    DirectoryEntry entry = new DirectoryEntry(connectStr);
    return new IISAppPool(entry);
}
 
/// <summary>
/// create app pool
/// </summary>
/// <param name="name">the app pool to be created</param>
/// <returns>IISAppPool created if success, else null</returns>
public static IISAppPool CreateAppPool(string name)
{
    DirectoryEntry Service = new DirectoryEntry("IIS://localhost/W3SVC/AppPools");
    foreach (DirectoryEntry entry in Service.Children)
    {
        if (entry.Name.Trim().ToLower() == name.Trim().ToLower())
        {
            return IISAppPool.OpenAppPool(name.Trim());
        }
    }
 
    // create new app pool
    DirectoryEntry appPool = Service.Children.Add(name, "IIsApplicationPool");
    appPool.CommitChanges();
    Service.CommitChanges();
 
    return new IISAppPool(appPool);
} 

Operations on application pools are quite simple, we can Start or Stop an application pool:

/// <summary>
/// Start application pool.
/// </summary>
public void Start()
{
    this._entry.Invoke("Start");
}
 
/// <summary>
/// Stop application pool.
/// </summary>
public void Stop()
{
    this._entry.Invoke("Stop");
} 

Properties of DirectoryEntry

We can find references on microsoft.com about properties for each schema class. However, if we want to enumerate properties with ADS nodes, here’s one solution:

 private void ListProperty(DirectoryEntry server)
{
     foreach (DirectoryEntry e in server.Children)
     {
         ListProperty(e);
     }
 
     StringBuilder sb = new StringBuilder();
     sb.AppendLine("Property for " + server.SchemaClassName);
     sb.AppendLine("Name = " + server.Name);
     sb.AppendLine("Path = " + server.Path);
     sb.AppendLine("UserName = " + server.Username);
     sb.AppendLine("====================================================================");
      IEnumerator ie = server.Properties.PropertyNames.GetEnumerator();
      while (ie.MoveNext())
      {
          try
          {
               string name = (string)ie.Current;
               string val = "";
                foreach (object obj in server.Properties[name])
                {
                     val += obj.ToString() + ",";
                }
 
                sb.AppendLine(name + " = " + val.ToString());
           }
           catch (Exception)
           {
           }
       }
        System.IO.StreamWriter sw = new System.IO.StreamWriter
	("PropertyList_" + server.SchemaClassName + "_" + server.Name + ".txt");
        sw.Write(sb.ToString());
        sw.Close();
     }
} 

Reference

History

  • 6th August, 2010: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here