I've moved my blog to!. Please update your links. This blog is no longer in use--you can find all posts and comments at my new blog; I will no longer be posting to this site and comments have been disabled.

Tuesday, April 1, 2008

Include IIS with Disaster Recovery Backup

I mentioned in my previous post that I was doing a presentation at the local SharePoint user group meeting and wanted to create a simple command to demonstrate what is involved with creating custom stsadm extensions.  The first command I created (gl-backupsites) turned out to be too complex for demonstration purposes so I took another approach and decided to show how to simply wrap an existing command in order to extend it.  What I chose to do was to wrap the existing out-of-the-box backup command and simply add an "-includeiis" parameter.  I called my new command gl-backup.

The code is pretty simple.  I basically just reproducing the parameters from the built-in command which I just grabbed with the help of Reflector.  I then just loop through the parameters in order to build the call to the built-in command.  Once the built-in command is finished running I grab the generated XML index file and locate the folder in which the backup was stored.  I then use this folder to store the IIS settings:

   1:  public class Backup2 : SPOperation
   2:  {
   3:      private const int FLAG_EXPORT_INHERITED_SETTINGS = 1;
   5:      /// <summary>
   6:      /// Initializes a new instance of the <see cref="BackupSites"/> class.
   7:      /// </summary>
   8:      public Backup2()
   9:      {
  10:          SPParamCollection parameters = new SPParamCollection();
  12:          parameters.Add(new SPParam("directory", "dir", false, null, null));
  13:          parameters.Add(new SPParam("backupmethod", "method", false, "None", null));
  14:          parameters.Add(new SPParam("item", "item", false, null, null));
  15:          parameters.Add(new SPParam("quiet", "quiet", false, null, null));
  16:          parameters.Add(new SPParam("percentage", "update", false, null, new SPIntRangeValidator(1, 100)));
  17:          parameters.Add(new SPParam("backupthreads", "threads", false, null, new SPIntRangeValidator(1, 10)));
  18:          parameters.Add(new SPParam("showtree", "tree", false, "False", null));
  19:          parameters.Add(new SPParam("url", "url", false, null, null));
  20:          parameters.Add(new SPParam("filename", "f", false, null, new SPValidator()));
  21:          parameters.Add(new SPParam("overwrite", "overwrite"));
  22:          parameters.Add(new SPParam("includeiis", "iis"));
  24:          StringBuilder sb = new StringBuilder();
  25:          sb.Append("For site collection backup:");
  26:          sb.Append("\r\n    stsadm.exe -o backup ");
  27:          sb.Append("\r\n        -url <url>");
  28:          sb.Append("\r\n        -filename <filename>");
  29:          sb.Append("\r\n        [-overwrite]");
  30:          sb.Append("\r\n\r\nFor catastrophic backup:");
  31:          sb.Append("\r\n    stsadm.exe -o backup");
  32:          sb.Append("\r\n        -directory <UNC path>");
  33:          sb.Append("\r\n        -backupmethod <full | differential>");
  34:          sb.Append("\r\n        [-item <created path from tree>]");
  35:          sb.Append("\r\n        [-percentage <integer between 1 and 100>]");
  36:          sb.Append("\r\n        [-backupthreads <integer between 1 and 10>]");
  37:          sb.Append("\r\n        [-showtree]");
  38:          sb.Append("\r\n        [-quiet]");
  39:          sb.Append("\r\n        [-includeiis]");
  41:          Init(parameters, sb.ToString());
  42:      }
  44:      #region ISPStsadmCommand Members
  46:      /// <summary>
  47:      /// Gets the help message.
  48:      /// </summary>
  49:      /// <param name="command">The command.</param>
  50:      /// <returns></returns>
  51:      public override string GetHelpMessage(string command)
  52:      {
  53:          return HelpMessage;
  54:      }
  56:      /// <summary>
  57:      /// Runs the specified command.
  58:      /// </summary>
  59:      /// <param name="command">The command.</param>
  60:      /// <param name="keyValues">The key values.</param>
  61:      /// <param name="output">The output.</param>
  62:      /// <returns></returns>
  63:      public override int Run(string command, StringDictionary keyValues, out string output)
  64:      {
  65:          output = string.Empty;
  67:          InitParameters(keyValues);
  69:          string args = "-o backup";
  70:          foreach (string key in keyValues.Keys)
  71:          {
  72:              if (key.ToLowerInvariant() == "includeiis" || key.ToLowerInvariant() == "o")
  73:                  continue;
  75:              args += string.Format(" -{0} \"{1}\"", key, keyValues[key]);
  76:          }
  78:          if (Utilities.RunStsAdmOperation(args, false) != 0)
  79:              return 0;
  82:          if (Params["includeiis"].UserTypedIn && Params["directory"].UserTypedIn)
  83:          {
  84:              bool verbose = !Params["quiet"].UserTypedIn;
  85:              XmlDocument xmlToc = new XmlDocument();
  86:              xmlToc.Load(Path.Combine(Params["directory"].Value, "spbrtoc.xml"));
  87:              string iisBakPath = Path.Combine(xmlToc.DocumentElement.FirstChild.SelectSingleNode("SPBackupDirectory").InnerText, "iis.bak");
  88:              using (DirectoryEntry de = new DirectoryEntry("IIS://localhost"))
  89:              {
  90:                  if (verbose)
  91:                      Console.WriteLine("Flushing IIS metadata to disk....");
  92:                  de.Invoke("SaveData", new object[0]);
  93:                  if (verbose)
  94:                      Console.WriteLine("IIS metadata successfully flushed to disk.");
  95:              }
  96:              using (DirectoryEntry de = new DirectoryEntry("IIS://localhost"))
  97:              {
  98:                  if (verbose)
  99:                      Console.WriteLine("Exporting full IIS settings to {0}....", iisBakPath);
 100:                  string decryptionPwd = string.Empty;
 101:                  de.Invoke("Export", new object[] { decryptionPwd, iisBakPath, "/lm", FLAG_EXPORT_INHERITED_SETTINGS });
 102:              }
 103:          }
 105:          return 1;
 106:      }
 108:      #endregion
 110:  }

The syntax of the command can be seen below:

C:\>stsadm -help gl-backup

stsadm -o gl-backup
For site collection backup:
    stsadm.exe -o gl-backup
        -url <url>
        -filename <filename>

For catastrophic backup:
    stsadm.exe -o gl-backup
        -directory <UNC path>
        -backupmethod <full | differential>
        [-item <created path from tree>]
        [-percentage <integer between 1 and 100>]
        [-backupthreads <integer between 1 and 10>]

Here's an example of how to backup a farm and include the IIS settings:

stsadm -o gl-backup -directory c:\backups -backupmethod full -includeiis


Anonymous said...

I have just tried the command and it goes all the way to the end successfully until the last line:

Exporting full IIS settings to \\myserver\mypath\spbr0002\iis.bak....
Exception has been thrown by the target of an invocation.

Then the process ends.

Any ideas as to what has gone wrong?

Gary Lapointe said...

I just pushed a new build out. I'm not correcting the issue as I don't know what it is yet - I changed the code so that it will surface the real error and not the TargetInvodationException error - if you could please download the latest and report back the error that you receive.

Anonymous said...

Ok, I grabbed the latest and did an upgrade.

The error returned is:

Provider type not defined. (Exception from HRESULT: 0x80090017)

Gary Lapointe said...

Unfortunately these COM+ errors are a bit difficult to debug. My gutt tells me it's a permissioning issue. The only helpful thing I could google was this post: It suggests adjusting the permissions of some registry keys. I also found one that suggested putting the "nt authority\authenticated users" account into the USERS group.

Anonymous said...

Yes, I think it's permissions too, though, as the backup is to a remote share, I'm not suprised. What user does the iis backup run as? That user will need write access to the server share the holds the backups.

Also, any chance of an enumwebapps tool? :)

Gary Lapointe said...

The IIS backup will just run as the user your are logged in as (or specifically the user that you are executing the command as).

Anonymous said...

And how do we use the backup file? I'm going to assume we just use stsadm -o restore?

Gary Lapointe said...

Correct. For the IIS file you'll use IIS though.

The SharePoint NOob said...

Does this backup the settings for IIS7? There is no metabase in 7, just the applicationHosts file, i think?