There are a variety of tools for programmatic administration of IIS7.  Specifically there is a PowerShell provider, AppCmd.exe, and Microsoft.Web.Administration .NET library that are all discussed on the IIS website under the article Powerful Admin Tools.

In looking for a way to remotely create AppPools, Sites, and perform various site configuration I wanted to programmatically control these functions and do so remotely.  In this article I'm going to talk about the C# library, Microsoft.Web.Administration and how I was able to use it remotely.

Microsoft.Web.Administration

Usage of this library is very straight forward.  You can use NuGet to add the package into your project. From there, you can perform all of the site configuration you would normally be able to from the IIS Manager.

There is decent API documentation on MSDN:

Admin Utility

I wrapped most of this functionality into a utility class for ease of use.  This includes basic site administration for adding and configuring sites.  Below is an example for creating a new site:

public class IISAdmin
{
   public void AddSite(string hostname, string sitepath)
   {
      var server = new ServerManager();

      // create the app pool
      var newAppPool = server.ApplicationPools.Add(hostname);
      newAppPool.ManagedRuntimeVersion = "v4.0";

      // create the site
      var newsite = server.Sites.Add(hostname, sitepath, 80);
      newsite.Applications[0].ApplicationPoolName = hostname;

      // remove the default binding
      newsite.Bindings.Remove(newsite.Bindings[0]);

      // add a host specific binding
      newsite.Bindings.Add("*:80:" + hostname, "http");

      server.CommitChanges();
   }
}

Remote Connection

One of the goals of my exploration of Microsoft.Web.Administration was being able to programmatically perform remote administration with systems that are not part of a domain, we're using EC2.

There is a method in the ServerManager class called OpenRemote. This method allows you to connect to a remote server via the hostname.  Notice that this method does not include login credentials.  This means it relies upon NTLM authentication from the credentials of the calling process.  You will need to have a user on both the calling system and the remote system that uses the same username and password.

I found a good article on configuring remote connections: Connecting to IIS 7.0 configuration remotely with Microsoft.Web.Administration. This requires configuring DCOM on the remote server and setting firewall rules. After following the the steps outlined I was able to get OpenRemote to connect and work correctly... with one exception: it won't work when you are using impersonation. More info on that... http://mvolo.com/connecting-to-iis-70-configuration-remotely-with-microsoftwebadministration

Using Impersonation

In my set up, I was making these calls with a process that was using impersonation. I quickly learned that impersonation throws the whole thing into a tizzy. I found a bit of information on this thread, using Microsoft.Web.Administration on a remote machine not in the domain.

With further research I was able to determine that DCOM calls use two different credentials

  1. Authorization for object creation
  2. Authorization for methods calls

Credentials can be specified using CoInitializeSecurity of CoSetProxyBlacket.  IMO this starts trending down territory I don't wish to play in. So Enter WCF.

Using WCF

My final solution was to create a WCF endpoint on the IIS machine.  This endpoint acts as a proxy for the Microsoft.Web.Administration requests and executes the requests locally.  You could run this endpoint through IIS or as a Windows Service.  Because we already have a Windows Service running on our web machines, we simply added this endpoint.  Mission accomplished.


public class IISAdminEndpoint : IIISAdminEndpoint
{
    [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
    public int AddSite(string hostname)
    {
        try
        {
            var sitepath = ConfigurationManager.AppSettings["SitePath"];
            new IISAdmin().AddSite(hostname, sitepath);
        }
        catch (Exception)
        {
            return 1;
        }
        return 0;
    }
}