Get started with Microsoft.Web.administration - Create a simple website monitor


The other day a co-worker ran into an interesting problem: their IIS website would get stopped for no apparent reason. To assist with troubleshooting (and to help with uptime) I created a utility that checks every couple of seconds to make sure that the website is up. If it is NOT up it logs the event to a text file and attempts to restart the website.

While there are quite a few articles out there that describe how to work with Microsoft.Web.Administration (MWA), there are comparatively few that help you with the setup pre-requisites. When I first started with MWA I had no success getting the methods in the namespaces to work for me until a few simple things were explained to me.

This article will discuss how to get started with MWA at the ground floor.

References:

 

Foundation: Project pre-requisites

Before you can take advantage of the C# Microsoft.Web.Administration (MWA) API you need to get your project into a state where you can access those calls. Here are the steps that work for me:

  1. Ensure that you have IIS 7 or 7.5 installed. The rest of the steps will not work if this pre-requisite is not met.
  2. Create a new project in Visual Studio (Console / WinForms)
  3. Open the project Properties and change the Target Framework to .NET Framework 4
    1. It is important to note that the Client Profile will NOT work
      GetStartedWithMwa.png
  4. Add a reference to System.Web.
    1. This is a pre-requisite for Microsoft.Web.Administration
  5. Add a DLL reference to Microsoft.Web.Administration
    1. I found the DLL here: c:\windows\system32\inetsrv\microsoft.web.administration.dll
    2. You will need to reference the DLL even if you have the MWA assemblies in your GAC

      Note: I am developing on Windows 2008 R2 with IIS 7.5 Installed. The path may be different if you develop on Windows 7.
  6. Add the following using statement to your code:

    using Microsoft.Web.Administration;

 

Using the Microsoft.Web.Administration API

At this point you should be able to build your 'empty' project without any errors. It won't do anything, but it proves that you have access to the MWA methods. The WinForms sample code (at the bottom of the page) performs the following functions:

  1. When the Go button is clicked, a new BackgroundWorker is started (StartMonitor method)
  2. The Background worker enters a while loop to periodically check that the specified website name is up and running (siteMonitor.DoWork method)
  3. If the site is Stopped, it will be started and a log message written out to a file in the same directory as the executable
  4. If a Stop command is issued in the UI, the thread will stop and reset the state variables / UI elements (siteMonitor_RunWorkerCompleted method)

 

For brevity, here is an extract of how I am using the MWA API within my project (see further down for the 'Sample Code'). I have Bolded the relevant code lines and comments are in green:

// Grab a server manager object for the local IIS server
var iisServer = new ServerManager();
while (!ShutDownNow) {
   // Get the website
   var site = iisServer.Sites.First(website => website.Name.ToUpperInvariant() == ((string)e.Argument).ToUpperInvariant());
   // If the site isn't started, start it!
   if (!(site.State == ObjectState.Started || site.State == ObjectState.Starting)) {
     site.Start();
   }
   Thread.Sleep(2000);
}

 

The code snippet works like this:

  • Obtain a ServerManager object
  • Use the ServerManager to find a Website
  • If the website's state is not Started or Starting, then initiate the site.Start() method.

Once you have a ServerManager object the power of the API is available at your fingertips.

 

 

Sample Code for WinForms based website monitor

using System;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Web.Administration;
using System.IO;
using System.Threading;

namespace WebsiteMonitor {
 public partial class FormWebSiteMon : Form {

  // Class variables
  bool started = false;
  BackgroundWorker worker;
  bool ShutDownNow = false;

  public FormWebSiteMon() {
        InitializeComponent();
  }

  private void WsMonSiteNameBtn_Click(object sender, EventArgs e) {
   // If not started, start the site monitor
   if (!started) {
    StartMonitor(WsMonSiteNameBx.Text);
    started = true;
   } else {
    // Otherwise, pass the shutdown message
    ShutDownNow = true;
   }
  }

  ///
  /// Kick off the background worker monitor process
  ///
  ///
  private void StartMonitor(string siteName) {
   BackgroundWorker siteMonitor = new BackgroundWorker();
   siteMonitor.DoWork += new DoWorkEventHandler(siteMonitor_DoWork);    siteMonitor.RunWorkerCompleted += new RunWorkerCompletedEventHandler(siteMonitor_RunWorkerCompleted);
   siteMonitor.RunWorkerAsync(siteName);

   WsMonSiteNameBtn.Text = "Stop!";
   worker = siteMonitor;
  }

  ///
  /// Work that the monitor performs
  ///
  ///
  ///
  void siteMonitor_DoWork(object sender, DoWorkEventArgs e) {

   // Create the log file if it doesn't already exist
   if (!File.Exists(System.Windows.Forms.Application.StartupPath + @"\" + "WebsiteMonitorLog" + ".txt")) {
    File.Create(System.Windows.Forms.Application.StartupPath + @"\" + "WebsiteMonitorLog" + ".txt");
   }

   // Grab a server manager object for the local IIS server
   var iisServer = new ServerManager();

   while (!ShutDownNow) {

    // Get the website
    var site = iisServer.Sites.First(website => website.Name.ToUpperInvariant() == ((string)e.Argument).ToUpperInvariant());

    // If the site isn't started, start it!
    if (!(site.State == ObjectState.Started || site.State == ObjectState.Starting)) {
     site.Start();

     // Write out the results to a file
     using (StreamWriter fStream = File.AppendText(System.Windows.Forms.Application.StartupPath + @"\" + "WebsiteMonitorLog" + ".txt")){
      fStream.WriteLine(DateTime.Now.ToShortDateString() + "  " + DateTime.Now.ToLongTimeString() + "- Error: Website \"" + (string)e.Argument + "\" is down");
      fStream.WriteLine(DateTime.Now.ToShortDateString() + "  " + DateTime.Now.ToLongTimeString() + "- Info: Issued Restart command for Website \"" + (string)e.Argument + "\"");
     }

    }

    Thread.Sleep(2000);
   }
  }

  ///
  /// Reset the button when the thread exits
  ///
  ///
  ///
  void siteMonitor_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
   WsMonSiteNameBtn.Text = "Start!";
   ShutDownNow = false;
   started = false;
   worker = null;
  }
 }
}