MOSS MVP

I've moved my blog to http://blog.falchionconsulting.com!. 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, October 31, 2007

Delete Web Application

I had just finished creating the gl-createwebapp command and was about to go and delete the test web applications that I had created while testing the command and I discovered that there is no built in stsadm command for deleting a web application. I took a look at the code that is called when deleting via the browser and decided that it was simple enough that I could recreate it as a command - can't have a create without a delete :).  The command I created is called gl-deletewebapp.

The only tricky spot I had was that there was a couple of pieces of code that required the use of internal classes - fortunately I'm getting pretty good at making calls to these internal classes so it didn't really slow me down much. The main hang up was that the Unprovision() method of the SPWebApplication object doesn't allow you to specify whether you want to delete the IIS web site or not - it just defaults to deleting it.

I wanted this command to function like the browser equivalent so I had to use the internal UnprovisionIisWebSites() method along with the RetractSolutions method of the SPSolutions class. And to make sure that I was mirroring the code executed when using the browser I had to also set a timer job using an internal only class (there's gotta be a way around this one). The code to do all this is below:

   1: public override int Run(string command, System.Collections.Specialized.StringDictionary keyValues, out string output)
   2: {
   3:  output = string.Empty;
   4:  
   5:  InitParameters(keyValues);
   6:  
   7:  string url = Params["url"].Value;
   8:  bool deleteContent = Params["deletecontentdb"].UserTypedIn;
   9:  bool deleteIis = Params["deleteiiswebsite"].UserTypedIn;
  10:  
  11:  SPWebApplication webApp = SPWebApplication.Lookup(new Uri(url));
  12:  if (webApp.IisSettings.Count <= 0 && deleteContent)
  13:  {
  14:   DeleteContentDBs(webApp);
  15:   webApp.Delete();
  16:   return 1;
  17:  }
  18:  
  19:  int index = 0;
  20:  string[] serverComments = new string[webApp.IisSettings.Count];
  21:  string[] vdirs = new string[webApp.IisSettings.Count];
  22:  foreach (SPIisSettings iisSetting in webApp.IisSettings.Values)
  23:  {
  24:   vdirs[index] = iisSetting.Path.ToString();
  25:   serverComments[index] = iisSetting.ServerComment;
  26:   index++;
  27:  }
  28:  
  29:  // webApp.Unprovision() does not allow us to specify whether we want the site deleted so we have to use the internal version.
  30:  // SPWebApplication.UnprovisionIisWebSites(deleteIis, serverComments, webApp.ApplicationPool.Name);
  31:  MethodInfo unprovisionIisWebSites = webApp.GetType().GetMethod("UnprovisionIisWebSites",
  32:          BindingFlags.NonPublic | BindingFlags.Public |
  33:          BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Static,
  34:          null, new Type[] {typeof(bool), typeof(string[]), typeof(string)}, null);
  35:  
  36:  unprovisionIisWebSites.Invoke(webApp, new object[] { deleteIis, serverComments, webApp.ApplicationPool.Name });
  37:  
  38:  
  39:  // SPSolution.RetractSolutions(webApp.Farm, webApp.Id, vdirs, serverComments, true);
  40:  MethodInfo retractSolutions = typeof(SPSolution).GetMethod("RetractSolutions",
  41:       BindingFlags.NonPublic | BindingFlags.Public |
  42:       BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Static,
  43:       null, new Type[] {typeof(SPFarm), typeof(Guid), typeof(string[]), typeof(string[]), typeof(bool)}, null);
  44:  
  45:  retractSolutions.Invoke(null, new object[] { webApp.Farm, webApp.Id, vdirs, serverComments, true });
  46:  
  47:  
  48:  if (SPFarm.Local.TimerService.Instances.Count > 1)
  49:  {
  50:   // SPIisWebsiteUnprovisioningJobDefinition is an internal class so we need to use reflection to set it.
  51:  
  52:   // SPIisWebsiteUnprovisioningJobDefinition jobDef = new SPIisWebsiteUnprovisioningJobDefinition(deleteIis, serverComments, webApp.ApplicationPool.Name, vdirs, webApp.Id, true);
  53:   Type sPIisWebsiteUnprovisioningJobDefinitionType = Type.GetType("Microsoft.SharePoint.Administration.SPIisWebsiteUnprovisioningJobDefinition, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
  54:  
  55:   ConstructorInfo sPIisWebsiteUnprovisioningJobDefinitionConstructor =
  56:    sPIisWebsiteUnprovisioningJobDefinitionType.GetConstructor(
  57:     BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public,
  58:     null,
  59:     new Type[] {typeof(bool), typeof(string[]), typeof(string), typeof(string[]), typeof(Guid), typeof(bool)}, null);
  60:   object jobDef = sPIisWebsiteUnprovisioningJobDefinitionConstructor.Invoke(new object[] { deleteIis, serverComments, webApp.ApplicationPool.Name, vdirs, webApp.Id, true });
  61:  
  62:  
  63:   // jobDef.Schedule = new SPOneTimeSchedule(DateTime.Now);
  64:   PropertyInfo scheduleProp = sPIisWebsiteUnprovisioningJobDefinitionType.GetProperty("Schedule",
  65:               BindingFlags.FlattenHierarchy |
  66:               BindingFlags.NonPublic |
  67:               BindingFlags.Instance |
  68:               BindingFlags.InvokeMethod |
  69:               BindingFlags.GetProperty |
  70:               BindingFlags.Public);
  71:  
  72:   scheduleProp.SetValue(jobDef, new SPOneTimeSchedule(DateTime.Now), null);
  73:  
  74:   // jobDef.Update();
  75:   MethodInfo update = sPIisWebsiteUnprovisioningJobDefinitionType.GetMethod("Update",
  76:              BindingFlags.NonPublic |
  77:              BindingFlags.Public |
  78:              BindingFlags.Instance |
  79:              BindingFlags.InvokeMethod |
  80:              BindingFlags.FlattenHierarchy,
  81:              null,
  82:              new Type[] { }, null);
  83:  
  84:  
  85:   update.Invoke(jobDef, new object[] { });
  86:  }
  87:  
  88:  if (deleteContent)
  89:   DeleteContentDBs(webApp);
  90:  
  91:  webApp.Delete();
  92:  
  93:  return 1;
  94: }
  95:  
  96:  
  97: private static void DeleteContentDBs(SPWebApplication webApp)
  98: {
  99:  foreach (SPContentDatabase db in webApp.ContentDatabases)
 100:  {
 101:   db.Unprovision();
 102:  }
 103: }
The syntax of the command can be seen below:
C:\>stsadm -help gl-deletewebapp

stsadm -o gl-deletewebapp

Deletes a web application.

Parameters:
        -url 
        [-deleteiiswebsite]
        [-deletecontentdb]
Here's an example of how to delete a web application without deleting the IIS web site or the content database:
stsadm –o gl-deletewebapp -url "http://webappname"
Here's another example which will delete the IIS web site and the content database:
stsadm -o gl-deletewebapp -url "http://webappname" -deleteiiswebsite -deletecontentdb

Create Web Application

It's been a couple of weeks since my last post - I've been traveling and taking some needed vacation time. But I'm back to work now and I've got a couple new commands that I just created today.

One nice to have for our company was to be able to specify the home directory (virtual directory path) for our web applications - we didn't want to use the default value that gets set when you use extendvs via stsadm. I was originally just going to leave the default directory (c:\inetpub\wwwroot\wss\virtualdirectories\{hostheader}{port}) but then decided that I prefer it to be elsewhere. Problem is that if you use extendvs you cannot set the path (nor can you set the port). This seemed a bit annoying to me so I decided to do some digging. With a quick search I discovered the SPWebApplicationBuilder class (this post got me started: http://forums.microsoft.com/TechNet/ShowPost.aspx?PostID=1238244&SiteID=17).

Using this class is very simple - you just create a new instance passing in the an SPFarm object, set the properties of interest, and then call Create(). Once you've created the web application you need to provision it and then add it to the list of web applications in Central Admin. You can then choose to create the root site if desired. The code for all of this is very straight forward:

   1: using System;
   2: using System.IO;
   3: using System.Reflection;
   4: using System.Security;
   5: using System.Text;
   6: using Lapointe.SharePoint.STSADM.Commands.SPValidators;
   7: using Microsoft.SharePoint;
   8: using Microsoft.SharePoint.Administration;
   9: using Microsoft.SharePoint.Search.Administration;
  10: using Microsoft.SharePoint.Utilities;
  11: using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
  12:  
  13: namespace Lapointe.SharePoint.STSADM.Commands.WebApplications
  14: {
  15:     public class CreateWebApp : SPOperation
  16:     {
  17:         /// <summary>
  18:         /// Initializes a new instance of the <see cref="CreateWebApp"/> class.
  19:         /// </summary>
  20:         public CreateWebApp()
  21:         {
  22:             SPParamCollection parameters = new SPParamCollection();
  23:             parameters.Add(new SPParam("url", "url", true, null, new SPUrlValidator()));
  24:             parameters.Add(new SPParam("directory", "dir", false, null, new SPNonEmptyValidator(),
  25:                                        "Please specify the virtual directory for the web application."));
  26:             parameters.Add(new SPParam("port", "p", false, "80", new SPIntRangeValidator(0, int.MaxValue),
  27:                                        "Please specify the port for the web application."));
  28:             parameters.Add(new SPParam("timezone", "tz", false, null, new SPIntRangeValidator(0, ushort.MaxValue)));
  29:             parameters.Add(new SPParam("description", "desc", false, null, new SPNullOrNonEmptyValidator()));
  30:             parameters.Add(new SPParam("sethostheader", "sethh", false, null, new SPNullOrNonEmptyValidator()));
  31:             parameters.Add(new SPParam("exclusivelyusentlm", "ntlm"));
  32:             parameters.Add(new SPParam("allowanonymous", "anon"));
  33:             parameters.Add(new SPParam("ssl", "ssl"));
  34:  
  35:             string normalizedDataSource;
  36:             string defaultDatabaseUsername;
  37:             string defaultDatabasePassword;
  38:             SPFarm local = SPFarm.Local;
  39:             if (local != null)
  40:             {
  41:                 SPWebService service = local.Services.GetValue<SPWebService>();
  42:                 normalizedDataSource = service.DefaultDatabaseInstance.NormalizedDataSource;
  43:                 defaultDatabaseUsername = service.DefaultDatabaseUsername;
  44:                 defaultDatabasePassword = service.DefaultDatabasePassword;
  45:                 if ((normalizedDataSource == null) || (normalizedDataSource.Length == 0))
  46:                 {
  47:                     normalizedDataSource = Environment.MachineName;
  48:                 }
  49:             }
  50:             else
  51:             {
  52:                 Console.WriteLine(SPResource.GetString("NoFarmObject", new object[0]));
  53:                 return;
  54:             }
  55:             if (defaultDatabaseUsername == null)
  56:             {
  57:                 defaultDatabaseUsername = "";
  58:             }
  59:             if (defaultDatabasePassword == null)
  60:             {
  61:                 defaultDatabasePassword = "";
  62:             }
  63:             parameters.Add(new SPParam("databaseserver", "ds", false, normalizedDataSource, new SPNonEmptyValidator()));
  64:             parameters.Add(new SPParam("databasename", "dn", false, null, new SPNullOrNonEmptyValidator()));
  65:             parameters.Add(new SPParam("databaseuser", "du", false, defaultDatabaseUsername, null));
  66:             parameters.Add(new SPParam("databasepassword", "dp", false, defaultDatabasePassword, null));
  67:  
  68:             parameters.Add(new SPParam("apidname", "apid", false, "DefaultAppPool", new SPNonEmptyValidator()));
  69:             parameters.Add(new SPParam("apidtype", "apidtype", false, "NetworkService",
  70:                                        new SPRegexValidator("^configurableid$|^networkservice$")));
  71:             parameters.Add(new SPParam("apidlogin", "apu", true, null, new SPNonEmptyValidator()));
  72:             parameters.Add(new SPParam("apidpwd", "app", true, null, new SPNonEmptyValidator()));
  73:  
  74:             parameters.Add(new SPParam("donotcreatesite", "nosite"));
  75:             parameters.Add(new SPParam("ownerlogin", "ol", true, null, new SPNonEmptyValidator()));
  76:             parameters.Add(new SPParam("ownername", "on", false, null, null));
  77:             parameters.Add(new SPParam("owneremail", "oe", true, null,
  78:                                        new SPRegexValidator(@"^[^ \r\t\n\f@]+@[^ \r\t\n\f@]+$")));
  79:             parameters.Add(new SPParam("sitetemplate", "st", false, null, new SPNullOrNonEmptyValidator()));
  80:             parameters.Add(new SPParam("lcid", "lcid", false, "0", new SPRegexValidator("^[0-9]+$")));
  81:  
  82:             StringBuilder sb = new StringBuilder();
  83:             sb.Append("\r\n\r\nCreates a web application.\r\n\r\nParameters:\r\n");
  84:             sb.Append("\t-url <url>\r\n");
  85:             sb.Append("\t[-directory <virtual directory path>]\r\n");
  86:             sb.Append("\t[-port <web application port>]\r\n");
  87:             sb.Append("\t[-ownerlogin <domain\\name>]\r\n");
  88:             sb.Append("\t[-owneremail <someone@example.com>]\r\n");
  89:             sb.Append("\t[-exclusivelyusentlm]\r\n");
  90:             sb.Append("\t[-ownername <display name>]\r\n");
  91:             sb.Append("\t[-databaseuser <database user>]\r\n");
  92:             sb.Append("\t[-databaseserver <database server>]\r\n");
  93:             sb.Append("\t[-databasename <database name>]\r\n");
  94:             sb.Append("\t[-databasepassword <database user password>]\r\n");
  95:             sb.Append("\t[-lcid <language>]\r\n");
  96:             sb.Append("\t[-sitetemplate <site template>]\r\n");
  97:             sb.Append("\t[-donotcreatesite]\r\n");
  98:             sb.Append("\t[-description <iis web site name>]\r\n");
  99:             sb.Append("\t[-sethostheader <host header name>]\r\n");
 100:             sb.Append("\t[-apidname <app pool name>]\r\n");
 101:             sb.Append("\t[-apidtype <configurableid/NetworkService>]\r\n");
 102:             sb.Append("\t[-apidlogin <DOMAIN\\name>]\r\n");
 103:             sb.Append("\t[-apidpwd <app pool password>]\r\n");
 104:             sb.Append("\t[-allowanonymous]\r\n");
 105:             sb.Append("\t[-ssl]\r\n");
 106:             sb.Append("\t[-timezone <time zone ID>]\r\n");
 107:  
 108:             Init(parameters, sb.ToString());
 109:         }
 110:  
 111:         #region ISPStsadmCommand Members
 112:  
 113:         /// <summary>
 114:         /// Gets the help message.
 115:         /// </summary>
 116:         /// <param name="command">The command.</param>
 117:         /// <returns></returns>
 118:         public override string GetHelpMessage(string command)
 119:         {
 120:             return HelpMessage;
 121:         }
 122:  
 123:         /// <summary>
 124:         /// Runs the specified command.
 125:         /// </summary>
 126:         /// <param name="command">The command.</param>
 127:         /// <param name="keyValues">The key values.</param>
 128:         /// <param name="output">The output.</param>
 129:         /// <returns></returns>
 130:         public override int Execute(string command, System.Collections.Specialized.StringDictionary keyValues,
 131:                                     out string output)
 132:         {
 133:             output = string.Empty;
 134:  
 135:  
 136:             Uri uri = new Uri(Params["url"].Value);
 137:  
 138:             SPWebApplicationBuilder builder = GetWebAppBuilder(uri);
 139:  
 140:             SPWebApplication app = builder.Create();
 141:  
 142:             ProvisionTimerJob(app, false);
 143:  
 144:             // Set the TimeZone of the Application
 145:             if (Params["timezone"].UserTypedIn)
 146:                 app.DefaultTimeZone = ushort.Parse(Params["timezone"].Value);
 147:  
 148:             app.Update();
 149:             app.Provision();
 150:  
 151:  
 152:             // Upload the newly created WebApplication to the List 'Web Application List' in Central Administration:
 153:             SPWebService.AdministrationService.WebApplications.Add(app);
 154:  
 155:             if (!Params["donotcreatesite"].UserTypedIn)
 156:             {
 157:                 uint nLCID = uint.Parse(Params["lcid"].Value);
 158:                 string webTemplate = Params["sitetemplate"].Value;
 159:                 string ownerLogin = Params["ownerlogin"].Value;
 160:                 ownerLogin = Utilities.TryGetNT4StyleAccountName(ownerLogin, app);
 161:                 string ownerName = Params["ownername"].Value;
 162:                 string ownerEmail = Params["owneremail"].Value;
 163:  
 164:                 app.Sites.Add(uri.AbsolutePath, null, null, nLCID, webTemplate, ownerLogin, ownerName, ownerEmail, null,
 165:                               null,
 166:                               null);
 167:             }
 168:  
 169:  
 170:             Console.WriteLine(SPResource.GetString("PendingRestartInExtendWebFarm", new object[0]));
 171:             Console.WriteLine();
 172:  
 173:             if (!Params["donotcreatesite"].UserTypedIn)
 174:                 Console.WriteLine(SPResource.GetString("AccessSiteAt", new object[] {uri.ToString()}));
 175:             Console.WriteLine();
 176:  
 177:             return OUTPUT_SUCCESS;
 178:         }
 179:  
 180:         private SPWebApplicationBuilder GetWebAppBuilder(Uri uri)
 181:         {
 182:             SPWebApplicationBuilder builder = new SPWebApplicationBuilder(SPFarm.Local);
 183:  
 184:             //Set the Port and the RootDirectory where you want to install the Application, e.g:
 185:             builder.Port = int.Parse(Params["port"].Value);
 186:             if (Params["directory"].UserTypedIn)
 187:                 builder.RootDirectory = new DirectoryInfo(Params["directory"].Value);
 188:  
 189:             // Set the ServerComment for the Application which will be the Name of the Application in the SharePoint-List And IIS. If you do not set this Property, the Name of the Application will be 'SharePoint - <Default given Portnumber from System>' 
 190:             if (Params["description"].UserTypedIn)
 191:                 builder.ServerComment = Params["description"].Value;
 192:  
 193:             // Create the content database for this Application
 194:             builder.CreateNewDatabase = true;
 195:             if (Params["databasename"].UserTypedIn)
 196:                 builder.DatabaseName = Params["databasename"].Value;
 197:             if (Params["databaseserver"].UserTypedIn)
 198:                 builder.DatabaseServer = Params["databaseserver"].Value;
 199:             if (Params["databaseuser"].UserTypedIn)
 200:                 builder.DatabaseUsername = Params["databaseuser"].Value;
 201:             if (Params["databasepassword"].UserTypedIn)
 202:                 builder.DatabasePassword = Params["databasepassword"].Value;
 203:  
 204:             // Host Header settings
 205:             if (Params["sethostheader"].UserTypedIn)
 206:             {
 207:                 if (string.IsNullOrEmpty(Params["sethostheader"].Value))
 208:                     builder.HostHeader = uri.Host;
 209:                 else
 210:                     builder.HostHeader = Params["sethostheader"].Value;
 211:             }
 212:             builder.DefaultZoneUri = uri;
 213:  
 214:  
 215:             // App pool settings
 216:             builder.ApplicationPoolId = Params["apidname"].Value;
 217:             if (Params["apidtype"].Value.ToLowerInvariant() == "networkservice")
 218:                 builder.IdentityType = IdentityType.NetworkService;
 219:             else
 220:             {
 221:                 builder.IdentityType = IdentityType.SpecificUser;
 222:                 builder.ApplicationPoolUsername = Params["apidlogin"].Value;
 223:                 builder.ApplicationPoolPassword = CreateSecureString(Params["apidpwd"].Value);
 224:             }
 225:  
 226:             // Some additional Settings
 227:             builder.UseNTLMExclusively = Params["exclusivelyusentlm"].UserTypedIn;
 228:             builder.AllowAnonymousAccess = Params["allowanonymous"].UserTypedIn;
 229:             builder.UseSecureSocketsLayer = Params["ssl"].UserTypedIn;
 230:             return builder;
 231:         }
 232:  
 233:         /// <summary>
 234:         /// Validates the specified key values.
 235:         /// </summary>
 236:         /// <param name="keyValues">The key values.</param>
 237:         public override void Validate(System.Collections.Specialized.StringDictionary keyValues)
 238:         {
 239:             bool isUserAccount;
 240:             if (Params["donotcreatesite"].UserTypedIn)
 241:             {
 242:                 Params["ownerlogin"].Enabled = false;
 243:                 Params["ownername"].Enabled = false;
 244:                 Params["owneremail"].Enabled = false;
 245:                 Params["sitetemplate"].Enabled = false;
 246:                 Params["lcid"].Enabled = false;
 247:             }
 248:             else
 249:             {
 250:                 if (Params["ownerlogin"].UserTypedIn)
 251:                 {
 252:                     string ownerLogin = Utilities.TryGetNT4StyleAccountName(Params["ownerlogin"].Value, null);
 253:                     if (!SPUtility.IsLoginValid(null, ownerLogin, out isUserAccount))
 254:                         throw new ArgumentException(
 255:                             SPResource.GetString("InvalidLoginAccount", new object[] {ownerLogin}));
 256:                     if (!isUserAccount)
 257:                         throw new ArgumentException(SPResource.GetString("OwnerNotUserAccount", new object[0]));
 258:                 }
 259:             }
 260:             if (Params["apidtype"].Value.ToLowerInvariant() == "networkservice")
 261:             {
 262:                 Params["apidlogin"].Enabled = false;
 263:                 Params["apidpwd"].Enabled = false;
 264:             }
 265:             else
 266:             {
 267:                 if (Params["apidlogin"].UserTypedIn)
 268:                 {
 269:                     string apidlogin = Utilities.TryGetNT4StyleAccountName(Params["apidlogin"].Value, null);
 270:                     if (!SPUtility.IsLoginValid(null, apidlogin, out isUserAccount))
 271:                         throw new ArgumentException(
 272:                             SPResource.GetString("InvalidLoginAccount", new object[] {apidlogin}));
 273:                 }
 274:             }
 275:             base.Validate(keyValues);
 276:         }
 277:  
 278:         #endregion
 279:  
 280:         /// <summary>
 281:         /// Creates the secure string.
 282:         /// </summary>
 283:         /// <param name="strIn">The string to convert.</param>
 284:         /// <returns></returns>
 285:         internal static SecureString CreateSecureString(string strIn)
 286:         {
 287:             if (strIn != null)
 288:             {
 289:                 SecureString str = new SecureString();
 290:                 foreach (char ch in strIn)
 291:                 {
 292:                     str.AppendChar(ch);
 293:                 }
 294:                 str.MakeReadOnly();
 295:                 return str;
 296:             }
 297:             return null;
 298:         }
 299:  
 300:         private static void ProvisionTimerJob(SPWebApplication application, bool resetIis)
 301:         {
 302:             if (SPFarm.Local.TimerService.Instances.Count > 1)
 303:             {
 304:                 // SPWebApplicationProvisioningJobDefinition is an internal class so we need to use reflection to set it.
 305:  
 306:                 // SPWebApplicationProvisioningJobDefinition definition = new SPWebApplicationProvisioningJobDefinition(application, resetIis);
 307:                 Type SPWebApplicationProvisioningJobDefinition =
 308:                     Type.GetType(
 309:                         "Microsoft.SharePoint.Administration.SPWebApplicationProvisioningJobDefinition, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
 310:  
 311:                 ConstructorInfo SPWebApplicationProvisioningJobDefinitionConstructor =
 312:                     SPWebApplicationProvisioningJobDefinition.GetConstructor(
 313:                         BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public,
 314:                         null,
 315:                         new Type[] {typeof (SPWebApplication), typeof (bool)}, null);
 316:                 object jobDef =
 317:                     SPWebApplicationProvisioningJobDefinitionConstructor.Invoke(new object[] {application, resetIis});
 318:  
 319:  
 320:                 // jobDef.Schedule = new SPOneTimeSchedule(DateTime.Now);
 321:                 PropertyInfo scheduleProp = SPWebApplicationProvisioningJobDefinition.GetProperty("Schedule",
 322:                                                                                                   BindingFlags.
 323:                                                                                                       FlattenHierarchy |
 324:                                                                                                   BindingFlags.NonPublic |
 325:                                                                                                   BindingFlags.Instance |
 326:                                                                                                   BindingFlags.
 327:                                                                                                       InvokeMethod |
 328:                                                                                                   BindingFlags.
 329:                                                                                                       GetProperty |
 330:                                                                                                   BindingFlags.Public);
 331:  
 332:                 scheduleProp.SetValue(jobDef, new SPOneTimeSchedule(DateTime.Now), null);
 333:  
 334:                 // jobDef.Update();
 335:                 MethodInfo update = SPWebApplicationProvisioningJobDefinition.GetMethod("Update",
 336:                                                                                         BindingFlags.NonPublic |
 337:                                                                                         BindingFlags.Public |
 338:                                                                                         BindingFlags.Instance |
 339:                                                                                         BindingFlags.InvokeMethod |
 340:                                                                                         BindingFlags.FlattenHierarchy,
 341:                                                                                         null,
 342:                                                                                         new Type[] {}, null);
 343:  
 344:  
 345:                 update.Invoke(jobDef, new object[] {});
 346:             }
 347:             SPAdministrationWebApplication local = SPAdministrationWebApplication.Local;
 348:             if (local != null)
 349:             {
 350:                 local.RemoveAdministrativeTask("WSSCreateWebApplication");
 351:             }
 352:         }
 353:     }
 354: }

The syntax of the command can be seen below:

C:\>stsadm -help gl-createwebapp

stsadm -o gl-createwebapp

Creates a web application.

Parameters:
        -url <url>
        [-directory <virtual directory path>]
        [-port <web application port>]
        [-ownerlogin <domain\name>]
        [-owneremail <someone@example.com>]
        [-exclusivelyusentlm]
        [-ownername <display name>]
        [-databaseuser <database user>]
        [-databaseserver <database server>]
        [-databasename <database name>]
        [-databasepassword <database user password>]
        [-lcid <language>]
        [-sitetemplate <site template>]
        [-donotcreatesite]
        [-description <iis web site name>]
        [-sethostheader <host header name>]
        [-apidname <app pool name>]
        [-apidtype <configurableid/NetworkService>]
        [-apidlogin <DOMAIN\name>]
        [-apidpwd <app pool password>]
        [-allowanonymous]
        [-ssl]
        [-timezone <time zone ID>]

The following table summarizes the command and its various parameters:

Command Name Availability Build Date
gl-createwebapp WSS v3, MOSS 2007 Released: 10/31/2007
Updated: 8/9/2008

Parameter Name Short Form Required Description Example Usage
url   Yes The load balanced URL is the domain name for all sites users will access in this SharePoint Web application.  This URL domain will be used in all links shown on pages within the web application. -url http://portal
directory dir No The virtual directory of the web application.  If not specified then the directory will be c:\inetpub\wwwroot\wss\virtualdirectories\{hostheader}{port} -directory c:\moss\webs\portal

-dir c:\moss\webs\portal
port p No The port number for the web application.  The default is 80. -port 123

-p 123
ownerlogin ol Yes - unless -donotcreatesite is passed in The login name of the owner of the site if a root site collection is to be created.  This parameter is disabled if -donotcreatesite is passed in. -ownerlogin domain\user1

-ol domain\user1
owneremail oe Yes - unless -donotcreatesite is passed in The email address of the owner of the site collection.  This parameter is disabled if -donotcreatesite is passed in. -owneremail user1@domain.com

-oe user1@domain.com
exclusivelyusentlm ntlm No Sets the site to use NTLM Windows Authentication.  If omitted Kerberos authentication will be enabled. -exclusivelyusentlm

-ntlm
ownername on No The name of the owner of the root site collection if created.  This parameter is disabled if -donotcreatesite is passed in. -ownername "Gary Lapointe"

-on "Gary Lapointe"
databaseuser du No The SQL Server account to use to connect to the content database.  If omitted then Windows Authentication will be used.  It is recommended that you use Windows Authentication and not a SQL Server account. -databaseuser spcontent

-du spcontent
databasepassword dp No - unless databaseuser is provided. The password of the database user specified by the -databaseuser parameter. -databasepassword pa$$w0rd

-dp pa$$w0rd
databasename dn No The name of the content database.  If not specified then a database will be created using "SharePoint - #" where # is a random number. -databasename SharePoint_Portal_Content

-dn SharePoint_Portal_Content
databaseserver ds No The name of the database server on which to store the content database. -databaseserver spsql1

-ds spsql1
lcid   No The local ID of the root site collection. -lcid 1033
sitetemplate st No The template to use when creating the root site collection. -sitetemplate SPSPORTAL#0

-st SPSPORTAL#0
donotcreatesite nosite No If provided then a root site collection will not be created. -donotcreatesite

-nosite
description desc No The description that will be used to identify the web application in IIS. -description "SharePoint Portal (80)"

-desc "SharePoint Portal (80)"
sethostheader sethh No Sets the host header of the site.  The value is optional and if omitted the host of the -url parameter will be used. -sethostheader portal

-sethh portal
apidname apid No The name of the application pool.  If not provided then "DefaultAppPool" will be used. -apidname SharePoint_Portal_AppPool

-apid SharePoint_Portal_AppPool
apidtype   No Either networkservice or configurableid.  Defaults to networkservice.  If the type is set to configurableid then apidlogin and apidpwd is required. -apidtype configurableid
apidlogin apu No - unless apidtype is configurableid The user account that the application pool will run as. -apidlogin "domain\spportalapppool"

-apu "domain\spportalapppool"
apidpwd app No - unless apidtype is configurableid The password for the application pool user account. -apidpwd pa$$w0rd

-app pa$$w0rd
allowanonymous anon No Allows anonymous access to the web application. -allowanonymous

-anon
ssl   No Configures the web application to use SSL. -ssl
timezone tz No The time zone to associate the web application with -timezone 12

-tz 12

Here's an example of how to create a web application using minimal parameters:

stsadm –o gl-createwebapp -url "http://webappname" -donotcreatesite

Running this command will create a content database using the name "SharePoint - {random #}", it will name the web application the same as the database and will use "c:\inetpub\wwwroot\wss\virtualdirectories\{random #}". It will also use the DefaultAppPool application pool.

Here's another example which uses most of the parameters to set as many options as possible:

stsadm -o gl-createwebapp -url "http://testwebapp" -directory "c:\moss\webs\testwebapp" -port 80 -ownerlogin "domain\user1" -owneremail "someone@example.com" -exclusivelyusentlm -databasename "SharePoint_TestWebApp" -sitetemplate "SPS#0" -description "SharePoint_TestWebApp_80" -sethostheader testwebapp -apidname "AppPool1" -apidtype configurableid -apidlogin "domain\user2" -apidpwd "password" -timezone 10

Running this command will create a new application pool named "AppPool1", create a content database named "SharePoint_TestWebApp", name the web application "SharePoint_TestWebApp_80", set the time zone to Eastern, and set the virtual directory path to "c:\moss\webs\testwebapp".

Update 8/9/2008: I've fixed an issue where I wasn't setting the DefaultZoneUri property of the SPWebApplicationBuilder object (corresponds to the Load Balanced URL setting in the UI).  So now the URL that is passed in via the -url parameter will be used to set the load balanced URL (DefaultZoneUri) and if you wish to set a host header you now will need to pass it in as an argument via the -sethostheader parameter (if the value is omitted it will use the host specified in the -url parameter).  See jrabbit's comment for additional information.  I've also added some code that I had previously omitted which creates a timer job to aid in the provisioning of the web application on other servers in the farm.  The code now does exactly what the UI code does so hopefully it will address the issues some have reported.