Many of us use scripts to setup our web servers, a lot of the work is boilerplate - create sites, vroots.... To ease  maintanence work over the last year i've moved to a data driven approach to setting up web sites. I use an XML configuration file to describe the web site and a driver program that uses WMI to configure IIS based on the XML. Some of the benefits of this are : you only need to make fixes to the single driver program ; anyone can edit/create the configuration XML; operations has to deal with a single way to setup all your sites; updates to the schema are available to all sites (e.g: you add support to set content expiration). I spent some time today looking at support for WMI in C# and thought it may be useful to folks who're just starting out. In the rest of this post i'll show you a simplified example to give you a better feel for what i'm talking about.

You can find the WMI IIS 6.0 provider described here.

The Scripting Guys have a great introduction to WMI.

You can find some WMI related tools on MSDN.

Sample Configuration:

 

<?xml version="1.0" encoding="utf-8" ?>
<webSettings>
     <webSites>
             <webSite ServerId="1234" ServerComment="My Test WebSite" PathOfDefaultVroot="c:\inetpub\wwwroot\Test" >
                    <siteSettings>
                          <propertyList>
                                 <property name="AllowKeepAlive" value="True"/>
                                 <property name="ConnectionTimeout" value="90"/>
                          </propertyList>
                         <serverBinding Hostname="" IP="" Port="80"/>
                   </siteSettings>
            </webSite>
     </webSites>
</webSettings>

The property names used in the configuration match the names used by WMI, to make it easier to relate to the documentation. I've left out a lot in the sample such as ... templates to store common settings (e.g. custom error pages, supported mime types, log settings); server settings (e.g. AppPool creation); vroot creation ...

The driver program  (a console app) uses a helper class to load and apply the configuration to the target server. The WMI classes are in the System.Management namespace (that you need to add a reference to). This is just a first stab at using the WMI classes, do let me know if i'm missing something.

 class App
 {
  [STAThread]
  static int Main (string[] args)
  {

   if (args.Length != 2)
   {
    StdOut ("Usage: Xml2IIs <targetServer> <configPath>");
    StdOut ("  e.g: Xml2IIs localhost mywebsites.xml");
    return 1;
   }

   string target = args[0];
   string configPath = args[1];

   XmlConfig config = new XmlConfig (configPath);
   if (config.ApplySettings (target))
    return 0;
   else
    return 1;
  }

  public static void LogException (Exception ex)
  {
   Console.Error.WriteLine ("EXCEPTION!> " + ex.Source);
   Console.Error.WriteLine ("EXCEPTION!> " + ex.Message);
   Console.Error.WriteLine ("EXCEPTION!> " + ex.StackTrace);
  }

  public static void StdOut (string msg)
  {
   Console.WriteLine (">{0}", msg);
  }

 }

XmlConfig is the helper class that processes the XML configuration, it moves thru' the different sections and uses a WMI wrapper class to apply the settings to the IIS Metabase

 public class XmlConfig
 {
  public XmlConfig (string configPath)
  {
   Load (configPath);
  }

  public bool ApplySettings (string target)
  {
   if (_doc == null) return false;

   // connect to WMI on target server
   _wmi = new WmiIIS ();
   _wmi.Connect (target);

   if (!_wmi.IsConnected ()) return false;

   // process website nodes
   XmlNodeList siteNodes = _doc.SelectNodes (@"webSettings/webSites/webSite");
   foreach (XmlNode siteNode in siteNodes)
   {
    HandleWebSite (siteNode);
   }

   return true;
  }

  private bool HandleWebSite (XmlNode siteNode)
  {
   try
   {
    XmlNode settingsNode = siteNode.SelectSingleNode ("siteSettings");

    XmlNodeList bindingNodes = settingsNode.SelectNodes ("serverBinding");
    ManagementBaseObject[] serverBindings = new ManagementBaseObject[bindingNodes.Count];

    int i = 0;
    foreach (XmlNode bindingNode in bindingNodes)
    {

     serverBindings[i++] =
     _wmi.CreateServerBinding (bindingNode.Attributes["Hostname"].Value,
      bindingNode.Attributes["IP"].Value,
      bindingNode.Attributes["Port"].Value);
    }

    string wmiPath = _wmi.CreateWebSite (siteNode.Attributes["ServerId"].Value,
     siteNode.Attributes["ServerComment"].Value,
     siteNode.Attributes["PathOfDefaultVroot"].Value,
     serverBindings);

    ManagementObject oSite = _wmi.GetInstance (wmiPath.Replace("IIsWebServer","IIsWebServerSetting"));

    App.StdOut("Connected To..." + oSite.Path);

    XmlNode propertyNodes = settingsNode.SelectSingleNode ("propertyList");

    if (propertyNodes != null && propertyNodes.ChildNodes.Count > 0)
    {

     Hashtable tableProperty = new Hashtable ();
     foreach (XmlNode propertyNode in propertyNodes.ChildNodes)
     {
      string name, value;
      name = propertyNode.Attributes["name"].Value;
      value = propertyNode.Attributes["value"].Value;

      tableProperty.Add (name, value);
     }

     _wmi.ApplyPropertyList (oSite, tableProperty);
    }

    return true;

   }
   catch (Exception ex)
   {
    App.LogException (ex);
    return false;
   }

  }


  private bool Load (string configPath)
  {
   try
   {
    _doc = new XmlDocument ();
    _doc.Load (configPath);

    return true;

   }
   catch (Exception ex)
   {
    App.LogException (ex);
    _doc = null;
    return false;
   }

  }


  private XmlDocument _doc = null;
  private WmiIIS _wmi = null;
 }

Finally we have a WMI wrapper class that interfaces with WMI to actually update the IIS metabase

 public class WmiIIS
 {
  public WmiIIS ()
  {
  }

  public bool IsConnected()
  {
   if (_target == null || _connection == null || _scope == null) return false;
   return _scope.IsConnected;
  }

  public bool Connect (string target)
  {
   if (target == null) return false;


   try
   {
    _target = target;
    _connection = new ConnectionOptions ();
    _scope = new ManagementScope (@"\\" + target + @"\root\MicrosoftIISV2", _connection);
    _scope.Connect ();

    App.StdOut ("Connected To... " + target);
   }
   catch (Exception ex)
   {
    App.LogException (ex);
    return false;
   }

   return IsConnected ();
  }

  public ManagementObject CreateServerBinding (string HostName, string IP, string Port)
  {
   try
   {
    ManagementClass classBinding = new ManagementClass (_scope, new ManagementPath ("ServerBinding"), null);
    ManagementObject serverBinding = classBinding.CreateInstance ();
    serverBinding.Properties["Hostname"].Value = HostName;
    serverBinding.Properties["IP"].Value = IP;
    serverBinding.Properties["Port"].Value = Port;
    serverBinding.Put ();
    return serverBinding;
   }
   catch (Exception ex)
   {
    App.LogException (ex);
    return null;
   }

  }

  public string CreateWebSite (string serverID, string serverComment, string defaultVrootPath, ManagementBaseObject[] serverBinding)
  {
   if (serverID == null || serverID.Length == 0) return null;
   if (defaultVrootPath == null || defaultVrootPath.Length == 0) return null;
   if (serverComment == null || serverComment.Length == 0) return null;
   if (serverBinding == null) return null;

   try
   {

    ManagementObject oW3SVC = new ManagementObject (_scope, new ManagementPath (@"IIsWebService='W3SVC'"), null);
    App.StdOut("Connected To..." + oW3SVC.Path);

    if (IsWebSiteExists (serverID))
    {
     App.StdOut ("Site Already Exists..." + serverID);
     DeleteSite (serverID);
    }

    ManagementBaseObject inputParameters = oW3SVC.GetMethodParameters ("CreateNewSite");

    inputParameters["ServerComment"] = serverComment;
    inputParameters["ServerBindings"] = serverBinding;
    inputParameters["PathOfRootVirtualDir"] = defaultVrootPath;
    inputParameters["ServerId"] = serverID;

    ManagementBaseObject outParameter = null;
    outParameter = oW3SVC.InvokeMethod ("CreateNewSite", inputParameters, null);
    return (string)outParameter.Properties["ReturnValue"].Value;

   }
   catch (Exception ex)
   {
    App.LogException (ex);
    return null;
   }

  }

  public bool ApplyPropertyList(ManagementObject managementObject, Hashtable tableProperty)
  {
   try
   {
    App.StdOut ("Setting Properties On..." + managementObject.Path);

    foreach (string key in tableProperty.Keys)
    {
     Console.WriteLine ("Setting...{0}={1}", key, tableProperty[key]);
     managementObject.Properties[key].Value = tableProperty[key];
    }

    managementObject.Put ();
    return true;
   }
   catch (Exception ex)
   {
    App.LogException (ex);
    return false;
   }


  }

  public ManagementObject GetInstance (string wmiPath)
  {
   try
   {
    return new ManagementObject(_scope, new ManagementPath(wmiPath),null);
   }
   catch (Exception ex)
   {
    App.LogException (ex);
    return null;

   }

  }

  public bool IsWebSiteExists (string serverID)
  {
   try
   {
    string siteName = "W3SVC/" + serverID;
    ManagementObjectSearcher searcher = new ManagementObjectSearcher (_scope, new ObjectQuery ("SELECT * FROM IIsWebServer"), null);

    ManagementObjectCollection webSites = searcher.Get ();
    foreach (ManagementObject webSite in webSites)
    {
     if ((string)webSite.Properties["Name"].Value == siteName)
      return true;
    }

    return false;
   }
   catch (Exception ex)
   {
    App.LogException (ex);
    return false;
   }


  }

  public bool DeleteSite (string serverID)
  {
   try
   {
    string serverName = "W3SVC/" + serverID;
    ManagementObject webSite = new ManagementObject (_scope, new ManagementPath (@"IIsWebServer='" + serverName + "'"), null);

    App.StdOut ("Stopping..." + webSite.Path);
    webSite.InvokeMethod ("Stop", null);

    App.StdOut ("Deleting..." + webSite.Path);
    webSite.Delete();

    webSite = null;

    return true;
   }
   catch (Exception ex)
   {
    App.LogException (ex);
    return false;
   }

  }


  string _target = null;
  ManagementScope _scope = null;
  ConnectionOptions _connection = null;
 }

Bye for now

- ramesh