Use C# to manage IIS

2010年7月29日 | by luckzj | 标签:

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 a IIS management dll called IISManager and it’s 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 can not meet this requirement. However, you can also use IISManager to manage it under the condition that you don’t do operations unsurpported by IIS 5.1

Copy Right:

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

DownLoad IISManager: IISManager

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 manipulte 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 to 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 accept one parameter which represent an ADS node. I will use a 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 a IIS ADS server object, there may bye 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 name of the website but name of the node. To get 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 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 source code and give 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 exsit.");
            }

            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 name of node do not mean name of 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 existed 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 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 virtal 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 use 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);
 }

applicatoin 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 applicatoin pool or IISAppPool.CreateAppPool to create a 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

IIS ADSI Provider
http://msdn.microsoft.com/en-us/library/ms524997(v=VS.90).aspx

1 trackbacks

本文的评论功能被关闭了.