Use C# to manage IIS
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
- IIS ADSI Provider
History
- 6th August, 2010: Initial post