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.

Wednesday, September 26, 2007


This is one of those things that I just shouldn't have had to create. So I'm going through my upgrade and I come to the point where I'm ready to start scripting the upgrade of the individual team sites (I'd been focusing on the upgraded Areas up until now). I figured no problem - this will be easy, all the documentation says I just need to use the built-in "upgrade" command - simply pass in the url you want to upgrade or an xml file containing multiple urls and you're good to go. So I started off with a command like this:

C:\>stsadm -o upgrade -sidebyside -reghost -sitelistpath "c:\sites.xml"

The sites.xml file contained a list of about 700 team sites that needed to be upgraded. The problem was that my test environment is too damn slow so the upgrade was continuously failing due to a SQL timeout.

As the timeout is set via code in some buried down deep unmanaged class I didn't see that there was much I could do to bump it up (if anyone knows differently please pass along the info). So I figured, no problem - I'll just upgrade one site at a time rather than passing all 700 sites into the upgrade tool. So I went to the command line and tried the following:

C:\>stsadm -o upgrade -sidebyside -reghost -url "http://intranet/sites/site1"

To my dismay I got this error in return: "Missing required argument: sitelistpath.". This made no sense to me - all the documentation I've read has made it clear that sitelistpath is optional and that you can pass in an url parameter to upgrade a single site.

So I decided to decompile the SPUpgrade class contained within stsadm itself. After spending a significant amount of time studying the code I realized that the url parameter is not utilized at all - I've dug through every line of the code and it's simply not there (with the exception of the Validation method which sets the Enabled property on the SPParam object which basically has no effect on anything as the parameter is enabled by default anyways).

So why is it listed as a parameter and why does all the documentation make it so obvious that it can be used? If anyone can find evidence to the contrary please let me know as this makes no sense to me at all.

For my purposes I didn't want to have to create 700 xml files just so that I could upgrade one site at a time (I can autogenerate my batch file using a simple SQL statement but generating xml files would be problematic without writing a separate tool to do it and I really didn't want to do that). So I decided to create this command, gl-upgrade2, which is really nothing more than a convenience hack - it functions basically the same as the original upgrade command but I've made it so that the url parameter actually works. I did this by simply creating a temporary xml file and then passing in all the parameters to the built-in upgrade command - so in a sense I'm just acting as a shell for the built-in command but now you don't have to create that xml file to just upgrade a single site - the command will create it for you behind the scenes (I also added a targetdb parameter so that you can specify the target database as well).

My gut tells me that I'm missing something - either that or the built-in upgrade command is just really misleading and a lot of people a lot smarter than me have been getting it wrong (something I doubt) - so if anyone out there knows differently as to the function of the url parameter in regards to the built-in upgrade command please, please, pass whatever you can along.

The core code is shown below - as you can see almost all the code is just building the command to make the call to the built-in upgrade command with the addition of the file generation:

   1: public override int Execute(string command, StringDictionary keyValues, out string output)
   2: {
   3:     output = string.Empty;
   5:     /* stsadm.exe -o upgrade
   6:            {-inplace |
   7:             -sidebyside}
   8:            [-url <url>]
   9:            [-forceupgrade]
  10:            [-quiet]
  11:            [-farmuser <farm user>]
  12:            [-farmpassword <farm user password>]
  13:            [-reghost]
  14:            [-sitelistpath <sites xml file>]
  15:     */
  16:     StringBuilder cmd = new StringBuilder();
  17:     cmd.Append(" -o upgrade");
  19:     if (Params["forceupgrade"].UserTypedIn)
  20:         cmd.Append(" -forceupgrade");
  22:     if (Params["quiet"].UserTypedIn)
  23:         cmd.Append(" -quiet");
  25:     if (Params["inplace"].UserTypedIn)
  26:         cmd.Append(" -inplace");
  27:     else if (Params["sidebyside"].UserTypedIn)
  28:         cmd.Append(" -sidebyside");
  30:     if (Params["reghost"].UserTypedIn)
  31:         cmd.Append(" -reghost");
  33:     if (Params["farmuser"].UserTypedIn)
  34:         cmd.AppendFormat(" -farmuser \"{0}\"", Params["farmuser"].Value);
  36:     if (Params["farmpassword"].UserTypedIn)
  37:         cmd.AppendFormat(" -farmpassword \"{0}\"", Params["farmpassword"].Value);
  39:     if (Params["sidebyside"].UserTypedIn && Params["sitelistpath"].UserTypedIn)
  40:         cmd.AppendFormat(" -sitelistpath \"{0}\"", Params["sitelistpath"].Value);
  42:     if (Params["inplace"].UserTypedIn && Params["url"].UserTypedIn)
  43:         cmd.AppendFormat(" -url \"{0}\"", Params["url"].Value);
  45:     string filename = null;
  46:     if (Params["sidebyside"].UserTypedIn && Params["url"].UserTypedIn)
  47:     {
  48:         filename = Path.GetTempFileName();
  49:         string xml = string.Format("<RedirectedSites Count=\"1\"><Site Url=\"{0}\"", Params["url"].Value);
  50:         if (Params["targetdb"].UserTypedIn)
  51:         {
  52:             xml += string.Format(" TargetDatabase=\"{0}\"", Params["targetdb"].Value);
  53:         }
  54:         xml += " /></RedirectedSites>";
  55:         File.WriteAllText(filename, xml);
  57:         cmd.AppendFormat(" -sitelistpath \"{0}\"", filename);
  58:     }
  60:     Console.WriteLine("Upgrading site...");
  61:     if (Utilities.RunStsAdmOperation(cmd.ToString(), true) != 0)
  62:         throw new SPException("Error occured upgrading.\r\nCOMMAND: stsadm.exe" + cmd);
  63:     Console.WriteLine("Site upgraded.\r\n");
  65:     if (filename != null)
  66:         File.Delete(filename);
  68:     string url = Params["url"].Value;
  69:     if (Params["moveto"].UserTypedIn)
  70:     {
  71:         Console.WriteLine("Moving site collection to new location...");
  72:         MoveSite.MoveSiteCollection(url, Params["moveto"].Value);
  73:         url = Params["moveto"].Value;
  74:         Console.WriteLine("Site collection moved.");
  75:     }
  77:     if (Params["theme"].UserTypedIn)
  78:     {
  79:         Console.WriteLine("Applying specified theme to site collection...");
  80:         Themes.ApplyTheme.ApplyThemeToWeb(Params["theme"].Value, url, true);
  81:         Console.WriteLine("Theme applied.\r\n");
  82:     }
  84:     return OUTPUT_SUCCESS;
  85: }

The syntax of the command I created can be seen below.

c:\>stsadm -help gl-upgrade2

stsadm -o gl-upgrade2

Slightly more flexible version of the built-in upgrade command (addresses the fact that the URL parameter does not do an

        {-inplace |
        [-url <url>]
        [-farmuser <farm user>]
        [-farmpassword <farm user password>]
        [-sitelistpath <sites xml file>]
        [-targetdb <target database name>]
        [-moveto <url of a new target path to move the upgraded site collection to>]
        [-theme <id of the theme to apply (see C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\Template\Layouts\[LCID]\SPThemes.xml for template IDs>]

Here’s an example of how to upgrade a single site (note that it's the same as what I showed above but instead of upgrade it's upgrade2 and I've added the target database):

stsadm -o gl-upgrade2 -sidebyside -reghost -url "http://intranet/sites/site1" -targetdb "intranet1_site_pair"

Update 6/25/2008: I actually made these changes months ago but forgot to update the post.  I added the moveto and theme parameters so that I could move the site collection and set the them with one statement.  You can of course do all this using the other individual commands I created but I'm lazy and didn't want to have to do with three commands what I could do with one.

No comments: