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, October 31, 2007

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:

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;
  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"));
  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));
  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()));
  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]+$")));
  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 <>]\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");
 108:             Init(parameters, sb.ToString());
 109:         }
 111:         #region ISPStsadmCommand Members
 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:         }
 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;
 136:             Uri uri = new Uri(Params["url"].Value);
 138:             SPWebApplicationBuilder builder = GetWebAppBuilder(uri);
 140:             SPWebApplication app = builder.Create();
 142:             ProvisionTimerJob(app, false);
 144:             // Set the TimeZone of the Application
 145:             if (Params["timezone"].UserTypedIn)
 146:                 app.DefaultTimeZone = ushort.Parse(Params["timezone"].Value);
 148:             app.Update();
 149:             app.Provision();
 152:             // Upload the newly created WebApplication to the List 'Web Application List' in Central Administration:
 153:             SPWebService.AdministrationService.WebApplications.Add(app);
 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;
 164:                 app.Sites.Add(uri.AbsolutePath, null, null, nLCID, webTemplate, ownerLogin, ownerName, ownerEmail, null,
 165:                               null,
 166:                               null);
 167:             }
 170:             Console.WriteLine(SPResource.GetString("PendingRestartInExtendWebFarm", new object[0]));
 171:             Console.WriteLine();
 173:             if (!Params["donotcreatesite"].UserTypedIn)
 174:                 Console.WriteLine(SPResource.GetString("AccessSiteAt", new object[] {uri.ToString()}));
 175:             Console.WriteLine();
 177:             return OUTPUT_SUCCESS;
 178:         }
 180:         private SPWebApplicationBuilder GetWebAppBuilder(Uri uri)
 181:         {
 182:             SPWebApplicationBuilder builder = new SPWebApplicationBuilder(SPFarm.Local);
 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);
 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;
 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;
 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;
 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:             }
 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:         }
 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:         }
 278:         #endregion
 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:         }
 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.
 306:                 // SPWebApplicationProvisioningJobDefinition definition = new SPWebApplicationProvisioningJobDefinition(application, resetIis);
 307:                 Type SPWebApplicationProvisioningJobDefinition =
 308:                     Type.GetType(
 309:                         "Microsoft.SharePoint.Administration.SPWebApplicationProvisioningJobDefinition, Microsoft.SharePoint, Version=, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
 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});
 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);
 332:                 scheduleProp.SetValue(jobDef, new SPOneTimeSchedule(DateTime.Now), null);
 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);
 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.

        -url <url>
        [-directory <virtual directory path>]
        [-port <web application port>]
        [-ownerlogin <domain\name>]
        [-owneremail <>]
        [-ownername <display name>]
        [-databaseuser <database user>]
        [-databaseserver <database server>]
        [-databasename <database name>]
        [-databasepassword <database user password>]
        [-lcid <language>]
        [-sitetemplate <site template>]
        [-description <iis web site name>]
        [-sethostheader <host header name>]
        [-apidname <app pool name>]
        [-apidtype <configurableid/NetworkService>]
        [-apidlogin <DOMAIN\name>]
        [-apidpwd <app pool password>]
        [-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

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

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

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

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

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 "" -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.


Anonymous said...

Nice follow up article, but how about doing a start to finish on how to get your code to work.

Thanks, hope to see updated documentation on this.

Gary Lapointe said...

For information about how all this works see this article:

For install instructions look at the info.txt file in the download. This is basically it though:
1. Copy the <stsadmcommands.edfinancial.xml> to <%WebExtensions%\12\CONFIG>
2. Deploy the Assembly Edfinancial.SharePoint.STSADM.Commands.dll to the GAC (Global Assembly Cache) found in %SYSTEM%\Windows\Assembly.

You use the code just like any other stsadm command - each is documented.

Remon said...

Hello Garry,

I don't know if this is the right place to ask.

Trying your command I get:
Object reference not set to an instance of an object.

To be honnest, I tried this command because the web interface gives me 'Unknown Error'. I hoped your command worked or gave me more info on the error.

Strange thing is, it works when being Site Administrator for Central Administration. But being a Farm Administrator is not enough.

I can only add 2 Site Administrators and not more like for Site Collections.

Any help is welcome! Thank you.

David Tappan said...

Hi, love your commands! I tried to use createwebapp, but while it made the web app, it didn't put it in the directory I directed--it put it in a randome one. It also selected a random port, and didn't give it the URL I asked for. Here's the command I ran (anonymized). Can you help?

stsadm -o createwebapp -url "" -directory "C:\Inetpub\wwwroot\wss\VirtualDirectories\portal-80" -port 80 -ownerlogin "MOSS\administrator" -owneremail "administrator@moss.local" -exclusivelyusentlm -databasename "WSS_Content_Portal-80" -sitetemplate "CUSTOMSPSPORTAL#0" -apidname "Portal-80" -apidtype configurableid -apidlogin "moss\administrator" -apidpwd "xxxxxxx" -timezone 10

Didn't see much in the logs using Baretail in realtime. Any suggestions?

Gary Lapointe said...

David - unfortunately I'm not really sure why it's not working right for you. I've used the command to create dozens of web applications with different settings and I've not yet had any issues with it. Is there any way you can attach a debugger to it to step through the code?

Anonymous said...

Great stuff Gary!

I am trying to extend stsadm to add two parameters to the extendvsinwebfarm operation. port # and "description (V Dir)

I am using this class:

My approach must be off becuase what seems simple is giving me problems.
Is that the correct class to build from?

Sorry if this is not the right place for this question

Gary Lapointe said...

Dennis - you could cetainly disassemble the current stsadm command and just pull all the code and add the two new parameters you need. Just keep in mind that it uses the SPGlobalAdmin object which has been marked as obsolete and I don't actually see a way to add the new parameters you want. According to Microsoft the way to do this is to add an additional entry to the IIS Settings via the IisSettings property of the SPWebApplication object ( I haven't looked too closely but it looks straight forward enough.

Anonymous said...

Excellent article.

One thing which seem impossible with stsadm (and extension thereof) is the programmatic extension of an existing web application..

I have the following but can't set port or url of new IIS site..

SPIisSettings iis_extranet = new SPIisSettings();
iis_extranet.AuthenticationMode = System.Web.Configuration.AuthenticationMode.Extranet;
iis_extranet.Path=new System.IO.DirectoryInfo("C:\\Inetpub\\wwwroot\\wss\\VirtualDirectories\\mypath");
iis_extranet.DisableKerberos = true;
webApp.IisSettings.Add(SPUrlZone.Extranet, iis_extranet);

Any ideas how to do this?


Gary Lapointe said...

Phil - sorry for not responding earlier - I haven't documented it yet but if you download the latest code you'll see that there's a new command called gl-extendwebapplication (note that I'm starting to prefix all my commands). You do in fact use the SPIisSettings object - you just need to set the SPServerBindings and SPSecureBindings objects. Download the code and you can see how I did this (note that I'm using some reflection to get the timer job to fire and using these objects does not allow you to create a new application pool - I'm mimicking the functionality that you get via the browser). If you want to be able to set the port, path, and create a new app pool then you have to use the obsolete SPGlobalAdmin.ExtendVirtualServerInWebFarm() method.

Anonymous said...

Gary, we love your extensions pack. I have a question about the GL-CreateWebApp extension. We used this to create a new webapp with all the parameters we wanted to specific and it worked great. However, this is a farm with multiple web front-ends and it only creates and extends it on the WFE where the command is run. Is there a way to have it create and extend on all the WFEs in the farm like the GUI does?


Gary Lapointe said...

It shouldn't be limited to just the machine it's run on - have you tried running "stsadm -o execadmsvcjobs" on all the WFEs?

Anonymous said...

Gary, thanks for your quick response. However, I tried using GL-CreateWebApp again to create a site and it still only created it on the WFE where the command was issued. Here's the farm configuration and the steps I completed:
We have 3 servers in the farm, a SQL server, a WFE/Application server and an additional WFE.

1. ran the following command on the WFE/Application server:
stsadm -o gl-createwebapp -url "" -directory "D:\inetpub\www_SiteA" -port 80 -exclusivelyusentlm -databasename "MOSClient_SiteA" -description "SiteA" -sethostheader -apidname "SharePoint - Web App Pool" -donotcreatesite
2. Ran "stsadm -o EXECADMSVCJOBS" on the WFE/Application server
3. Ran "stsadm -o EXECADMSVCJOBS" on the additional WFE server

Does the GL-CreateWebApp need to run on each front-end?

Any help would be greatly appreciated.


Henrik Kim said...

Hi Gary,

Love your tools and use them in so many projects. It's on the top 5 list of things you need in your toolbox creating SharePoint solutions!

One note however, we are experiencing a similar issue with the "CreateWebApp" command. The Web App is created on our CA server (from where we run the command line) but nothing is propogated to the two web front-ends.

Any input would be much appreciated.

Thank you,
Henrik Kim

Gary Lapointe said...

Henrik - Thanks for the feedback! Unfortunately I don't have a farm available at the moment to try and replicate but I had another MVP test it out and it worked for him. There's gotta be some environment specific issue that's preventing it from working (note that I'm just using the SPWebApplicationBuilder object - nothing special). Feels like a permissioning issue though - like the account you're using doesn't have rights (maybe you're using Kerberos and the SPN for the new app wasn't setup?)?

Henrik Kim said...

Gary, thank you for the quick response. We are running NTLM on the whole farm and Web Applications can be created successfully from the Central Administration interface. The same account is used in both scenarios. All Service accounts used for the installation are configured by SharePoint and no customization of the permissions in any way - and goes without saying: no domain administrators amongst them :-)

I have also checked that all jobs are running successfully from the “Job Status List” in Operations.


Gary Lapointe said...

Are there any errors in the event log or ULS? If not I'm at a loss - I suppose you could contact microsoft support.

jrabbit said...

I've noticed there is one minor difference between applications created with this account and those created through the UI.

The UI has a "Load balanced URL" field. The URL entered here is the one used to reference the web application / site collections in any future stsadm commands that take a URL argument.

With your command, if you do not use -sethostheader, the URL of the web application becomes http://<servername>:<port>

If you do use the -sethostheader option then the URL becomes the url you specified in the -url argument.

However, I can't find a way to set the URL for referencing the site in future stsadm commands without also setting the host header (which I want to avoid so alternate access mappings work). In the UI, the Load Balanced URL and Set Host Header check box are independent, so it is possible to do this.

If it's not possible, is there a way to script removing the host header immediately afterwards? I know I can do it via IIS Admin scripts but I'd rather do it via sharepoint if possible.

Gary Lapointe said...

Oops - nice catch - yeah, I forgot to set the DefaultZoneUri property. I'm just about to push out a change which now sets this property using the value passed in via the -url parameter and changed the -sethostheader parameter to take in a value to use as the host header (if the value is specified then it will use the host name from the -url property). I'll have the fixed build up this evening.

Troy said...


We love your commands. Thanks for sharing!!

One issue I've found is that I want to use the -sethostheader switch, but it won't accept a URL like "".
Have you tested a more complex Host Header? I noticed in your instructions that you used something very simple.

Gary Lapointe said...

Troy - I have tested this with more complex host names and it works just fine for me - are you getting any errors?

Steve Schofield said...

The link to the original post that got you going has been update.

Here is the link that works, the redirect didn't seem to work for me.

Btw: Great tool set. I'm just beginning to get into MOSS admin so this looks to be a well-timed find to help automate many MOSS items of our install. Thank you for making these available.

Steve Schofield
Microsoft MVP - IIS

Steve Schofield said...

Hi Gary,

This example you've posted is absolutely awesome. I'm trying to use gl-createwebapp to automate some moss configuration on our end. Our databases are created ahead of time with the appropriate permissions. I can't tell if the gl-createwebapp accounts for this situation. Have you came across a situation where you want to create a site, application to an existing database. If this is possible, can you let me know. The docs don't explicitly state if the db already existed, do this...

Steve Schofield
Microsoft MVP - IIS

Gary Lapointe said...

Steve - thanks for the feedback - check out this article:

Basically just go ahead and create your databases and then run either the extendvs or my gl-createwebapp commands and pass in the name of that existing database (the database just has to be empty).

Steve Schofield said...

Hi Gary,

I wanted to let you know your tool was a life saver! Your advice worked, I was able to create the apps and sites while the databases were already created. Thanks again!

Steve Schofield
Microsoft MVP - IIS

Marina said...

Hello Garry,

Thank you for your great commands-pack!
I have a question about "Create Web Application" command: when I create web application through UI I have possibility to choose server for WSS search. If I choose one, the "Search" node will be created for my site. But how can I choose search server with "gl-createwebapp" command (or may be I need another one command)? Because now the Search node isn't created and I have an error when trying to use search button in my site created by "gl-createwebapp"

Gary Lapointe said...

I'm currently only doing what the out of the box extendvs command does but just exposing additional parameters. If you configure osearch before you create the web app then it should set that automatically (at least it does for me). Otherwise you'll have to do it through the browser - there's not currently a command to set the default search indexer for the web application.

M said...

Thanks for your extension; I just started to use it and it's great.

I post this comment because I've encounted a problem.
I was trying to create a Web Appl (an intranet zone with Windows Authentication) and then to extend to an internet zone (internet zone with form authentication)

Everytime I try to use gl-createwebapp or gl-extendwebapp, it returns the following message:
"Method not found: 'void Microsoft.Sharepoint.Administration.SPWebApplication.ProvisionGlobally()'."

I suppose it's releted with my SP version; I tried both with MOSS 2007 SP Enterprise and Standard Edition.

Gary Lapointe said...

It requires SP2 - I may go back and add some error handling and make it revert back to using reflection to run the job but I'd rather not do that.

Praveen Kumar said...

hi Gary,

I was able to create web application using batch files on single machine and multi machine environment, but im not able to create using power shell on multi machine environment.

multi environment details:

Database on one machine and share point on another machine.

please let me know how can i create web application for multi machine environment.

Gary Lapointe said...

Are you getting an error creating the web app - just because it's a multi-machine environment shouldn't make any difference at all.

bscholl said...

Gary, first of all thanks for sharing your hard work with us. Much appreciated!!

I have an issue which is maybe nor explicitely related to your work, but you might have some input for me.

I use gl-createwebapp to create new webapplications, but i cannot see these webapps in IIS nor are the sites browsable.

Restarting the SPWebService fixes this issue for me, but creates a new one: I lose all custom entries in all web.configs.

It may be coincidence, but having the webapps not showing in IIS only happens when i use gl-createwebapp, not when i create a web app in Central Administration.

Do you have an idea why this happens or even any advice how to fix that without loosing all custom web.configs?

Kind regards,

Gary Lapointe said...

This is the first time I've ever heard of it not creating the actual web apps in IIS. Can you reproduce the behavior on more than one environment?

bscholl said...

Gary, i can reproduce this on my dev environment, our integration environment and on customer site too.

Gary Lapointe said...

I honestly am not sure what is going on - very odd that you can reproduce in mutiple environments but I can't in any of mine. I wonder if maybe it's a permissions issue? I always run as my setup account which is a local admin; I also do most of my testing on Svr2008R2 (not sure if that makes a difference but...).