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:
1using System;
2using System.IO;
3using System.Reflection;
4using System.Security;
5using System.Text;
6using Lapointe.SharePoint.STSADM.Commands.SPValidators;
7using Microsoft.SharePoint;
8using Microsoft.SharePoint.Administration;
9using Microsoft.SharePoint.Search.Administration;
10using Microsoft.SharePoint.Utilities;
11using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
12
13namespace 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 <a href="mailto:user1@domain.com">user1@domain.com</a> |
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). 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.