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.

Thursday, January 31, 2008

Create Site in Database

So you've got a content database (perhaps you used the gl-createcontentdb command) and now you want to create a site collection in that database. Problem is that you can't do this easily via the browser and you definitely can't do it via stsadm. There is a createsiteinnewdb command and a createsite command but the first will create a new database and the second will put the site collection in the "best match" database. But you need it in a specific database. What I decided to do was to create a new command: gl-createsiteindb.

The code is pretty straight forward - the bulk of the code is just validation code:

   1: using System;
   2: using System.Collections.Specialized;
   3: using System.IO;
   4: using System.Text;
   5: using System.Web.Configuration;
   6: using Lapointe.SharePoint.STSADM.Commands.SPValidators;
   7: using Microsoft.SharePoint;
   8: using Microsoft.SharePoint.Administration;
   9: using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
  10:  
  11: namespace Lapointe.SharePoint.STSADM.Commands.SiteCollectionSettings
  12: {
  13:     public class CreateSiteInDB : SPOperation
  14:     {
  15:         protected SPWebApplication m_WebApplication;
  16:  
  17:         /// <summary>
  18:         /// Initializes a new instance of the <see cref="CreateSiteInDB"/> class.
  19:         /// </summary>
  20:         public CreateSiteInDB()
  21:         {
  22:             SPParamCollection parameters = new SPParamCollection();
  23:             parameters.Add(new SPParam("url", "url", true, null, new SPUrlValidator()));
  24:             parameters.Add(new SPParam("lcid", "lcid", false, "0", new SPRegexValidator("^[0-9]+$")));
  25:             parameters.Add(new SPParam("sitetemplate", "st", false, null, new SPNullOrNonEmptyValidator()));
  26:             parameters.Add(new SPParam("title", "t", false, null, null));
  27:             parameters.Add(new SPParam("description", "desc", false, null, null));
  28:             parameters.Add(new SPParam("ownerlogin", "ol", false, null, new SPNonEmptyValidator()));
  29:             parameters.Add(new SPParam("ownername", "on", false, null, null));
  30:             parameters.Add(new SPParam("owneremail", "oe", true, null, new SPRegexValidator(@"^[^ \r\t\n\f@]+@[^ \r\t\n\f@]+$")));
  31:             parameters.Add(new SPParam("quota", "quota", false, null, new SPNullOrNonEmptyValidator()));
  32:             parameters.Add(new SPParam("hostheaderwebapplicationurl", "hhurl", false, null, new SPUrlValidator()));
  33:             parameters.Add(new SPParam("secondarylogin", "sl", false, null, new SPNullOrNonEmptyValidator()));
  34:             parameters.Add(new SPParam("secondaryname", "sn", false, null, null));
  35:             parameters.Add(new SPParam("secondaryemail", "se", false, null, null));
  36:             parameters.Add(new SPParam("dbname", "db", true, null, new SPNonEmptyValidator(), "Please specify the database name."));
  37:  
  38:             StringBuilder sb = new StringBuilder();
  39:             sb.Append("\r\n\r\nCreates a new site collection in an existing content database.\r\n\r\nParameters:");
  40:             sb.Append("-url <url>");
  41:             sb.Append("\r\n\t-owneremail <someone@example.com>");
  42:             sb.Append("\r\n\t[-ownerlogin <DOMAIN\\name>]");
  43:             sb.Append("\r\n\t[-ownername <display name>]");
  44:             sb.Append("\r\n\t[-secondaryemail <someone@example.com>]");
  45:             sb.Append("\r\n\t[-secondarylogin <DOMAIN\\name>");
  46:             sb.Append("\r\n\t[-secondaryname <display name>]");
  47:             sb.Append("\r\n\t[-lcid <language>]");
  48:             sb.Append("\r\n\t[-sitetemplate <site template>]");
  49:             sb.Append("\r\n\t[-title <site title>]");
  50:             sb.Append("\r\n\t[-description <site description>]");
  51:             sb.Append("\r\n\t[-hostheaderwebapplicationurl <web application url>]");
  52:             sb.Append("\r\n\t[-quota <quota template>]");
  53:             sb.Append("\r\n\t-dbname <content database name>");
  54:             
  55:             Init(parameters, sb.ToString());
  56:         }
  57:  
  58:         #region ISPStsadmCommand Members
  59:  
  60:         /// <summary>
  61:         /// Gets the help message.
  62:         /// </summary>
  63:         /// <param name="command">The command.</param>
  64:         /// <returns></returns>
  65:         public override string GetHelpMessage(string command)
  66:         {
  67:             return HelpMessage;
  68:         }
  69:  
  70:         /// <summary>
  71:         /// Runs the specified command.
  72:         /// </summary>
  73:         /// <param name="command">The command.</param>
  74:         /// <param name="keyValues">The key values.</param>
  75:         /// <param name="output">The output.</param>
  76:         /// <returns></returns>
  77:         public override int Execute(string command, StringDictionary keyValues, out string output)
  78:         {
  79:             output = string.Empty;
  80:  
  81:             
  82:  
  83:             string dbname = Params["dbname"].Value;
  84:  
  85:             string uriString = Params["url"].Value;
  86:             string webTemplate = Params["sitetemplate"].Value;
  87:             string title = Params["title"].Value;
  88:             string description = Params["description"].Value;
  89:             string ownerName = Params["ownername"].Value;
  90:             string ownerEmail = Params["owneremail"].Value;
  91:             string quota = Params["quota"].Value;
  92:             string secondaryContactName = Params["secondaryname"].Value;
  93:             string secondaryContactEmail = Params["secondaryemail"].Value;
  94:             bool isHostHeaderWebAppUrlTypedIn = Params["hostheaderwebapplicationurl"].UserTypedIn;
  95:             uint nLCID = uint.Parse(Params["lcid"].Value);
  96:             Uri uri = new Uri(uriString);
  97:             SPUrlZone zone = SPUrlZone.Default;
  98:             bool createActiveDirectoryAccounts = m_WebApplication.WebService.CreateActiveDirectoryAccounts;
  99:             string ownerLogin = Utilities.TryGetNT4StyleAccountName(Params["ownerlogin"].Value, m_WebApplication);
 100:             string secondaryContactLogin = null;
 101:             if (!createActiveDirectoryAccounts || !Params["secondaryemail"].UserTypedIn)
 102:             {
 103:                 if (!createActiveDirectoryAccounts && Params["secondarylogin"].UserTypedIn)
 104:                 {
 105:                     secondaryContactLogin = Utilities.TryGetNT4StyleAccountName(Params["secondarylogin"].Value, m_WebApplication);
 106:                 }
 107:             }
 108:             else
 109:             {
 110:                 secondaryContactLogin = @"@\@";
 111:             }
 112:  
 113:             SPContentDatabase database = null;
 114:             foreach (SPContentDatabase tempDB in m_WebApplication.ContentDatabases)
 115:             {
 116:                 if (tempDB.Name.ToLower() == dbname.ToLower())
 117:                 {
 118:                     database = tempDB;
 119:                     break;
 120:                 }
 121:             }
 122:             if (database == null)
 123:                 throw new SPException("Content database not found.");
 124:  
 125:             if (database.MaximumSiteCount <= database.CurrentSiteCount)
 126:                 throw new SPException("The maximum site count for the specified database has been exceeded.  Increase the maximum site count or specify another database.");
 127:  
 128:             SPSite site = null;
 129:             try
 130:             {
 131:                 site = database.Sites.Add(uri.OriginalString, title, description, nLCID, webTemplate, ownerLogin,
 132:                     ownerName, ownerEmail, secondaryContactLogin, secondaryContactName, secondaryContactEmail,
 133:                     isHostHeaderWebAppUrlTypedIn);
 134:  
 135:  
 136:                 if (!string.IsNullOrEmpty(quota))
 137:                 {
 138:                     using (SPSiteAdministration administration = new SPSiteAdministration(site.Url))
 139:                     {
 140:                         SPFarm farm = SPFarm.Local;
 141:                         SPWebService webService = farm.Services.GetValue<SPWebService>("");
 142:  
 143:                         SPQuotaTemplateCollection quotaColl = webService.QuotaTemplates;
 144:                         administration.Quota = quotaColl[quota];
 145:                     }
 146:                 }
 147:                 if (!string.IsNullOrEmpty(webTemplate))
 148:                 {
 149:                     using (SPWeb web = site.RootWeb)
 150:                     {
 151:                         web.CreateDefaultAssociatedGroups(ownerLogin, secondaryContactLogin, string.Empty);
 152:                     }
 153:                 }
 154:                 if (isHostHeaderWebAppUrlTypedIn && !m_WebApplication.IisSettings[zone].DisableKerberos)
 155:                 {
 156:                     Console.WriteLine(SPResource.GetString("WarnNoDefaultNTLM", new object[0]));
 157:                     Console.WriteLine();
 158:                 }
 159:             }
 160:             finally
 161:             {
 162:                 if (site != null)
 163:                     site.Dispose();
 164:             }
 165:  
 166:             return OUTPUT_SUCCESS;
 167:         }
 168:  
 169:         /// <summary>
 170:         /// Validates the specified key values.
 171:         /// </summary>
 172:         /// <param name="keyValues">The key values.</param>
 173:         public override void Validate(StringDictionary keyValues)
 174:         {
 175:             base.Validate(keyValues);
 176:             string uriString = Params["url"].Value;
 177:  
 178:             if (Params["quota"].UserTypedIn)
 179:             {
 180:                 string quota = Params["quota"].Value;
 181:                 SPFarm farm = SPFarm.Local;
 182:                 SPWebService webService = farm.Services.GetValue<SPWebService>("");
 183:  
 184:                 SPQuotaTemplateCollection quotaColl = webService.QuotaTemplates;
 185:  
 186:                 if (quotaColl[quota] == null)
 187:                 {
 188:                     throw new ArgumentException(SPResource.GetString("InvalidQuotaTemplateName", new object[0]));
 189:                 }
 190:             }
 191:  
 192:             m_WebApplication = ValidateWebApplication(this);
 193:             if (m_WebApplication == null)
 194:             {
 195:                 throw new FileNotFoundException(SPResource.GetString("WebApplicationLookupFailed", new object[] { uriString }));
 196:             }
 197:             SPUrlZone zone = SPUrlZone.Default;
 198:             if (!m_WebApplication.WebService.CreateActiveDirectoryAccounts && !Params["ownerlogin"].UserTypedIn)
 199:             {
 200:                 throw new ArgumentException(SPResource.GetString("NoADCreateRequiresOwnerLogin", new object[0]));
 201:             }
 202:             if (m_WebApplication.GetIisSettingsWithFallback(zone).AuthenticationMode == AuthenticationMode.Windows)
 203:             {
 204:                 bool bIsUserAccount;
 205:                 if (Params["ownerlogin"].UserTypedIn)
 206:                 {
 207:                     string strLoginName = Utilities.TryGetNT4StyleAccountName(Params["ownerlogin"].Value, m_WebApplication);
 208:                     if (!Utilities.IsLoginValid(strLoginName, out bIsUserAccount))
 209:                     {
 210:                         throw new ArgumentException(SPResource.GetString("InvalidLoginAccount", new object[] { strLoginName }));
 211:                     }
 212:                     if (!bIsUserAccount)
 213:                     {
 214:                         throw new ArgumentException(SPResource.GetString("OwnerNotUserAccount", new object[0]));
 215:                     }
 216:                 }
 217:                 if (Params["secondarylogin"].UserTypedIn)
 218:                 {
 219:                     string strLoginName = Utilities.TryGetNT4StyleAccountName(Params["secondarylogin"].Value, m_WebApplication);
 220:                     if (!Utilities.IsLoginValid(strLoginName, out bIsUserAccount))
 221:                     {
 222:                         throw new ArgumentException(SPResource.GetString("InvalidLoginAccount", new object[] { strLoginName }));
 223:                     }
 224:                     if (!bIsUserAccount)
 225:                     {
 226:                         throw new ArgumentException(SPResource.GetString("OwnerNotUserAccount", new object[0]));
 227:                     }
 228:                 }
 229:             }
 230:         }
 231:  
 232:  
 233:  
 234:  
 235:         #endregion
 236:  
 237:         internal static SPWebApplication ValidateWebApplication(SPOperation operation)
 238:         {
 239:             if (SPFarm.Local == null)
 240:             {
 241:                 throw new SPException(SPResource.GetString("OperationInvalidInRemoteFarm", new object[0]));
 242:             }
 243:             SPWebApplication application;
 244:             string uriString = operation.Params["url"].Value;
 245:             bool isHostHeaderWebAppUrlTypedIn = operation.Params["hostheaderwebapplicationurl"].UserTypedIn;
 246:             Uri requestUri = new Uri(uriString);
 247:             if ((requestUri.Scheme != Uri.UriSchemeHttps) && (requestUri.Scheme != Uri.UriSchemeHttp))
 248:             {
 249:                 throw new ArgumentException(SPResource.GetString("InvalidSiteName", new object[] { uriString }));
 250:             }
 251:             if (isHostHeaderWebAppUrlTypedIn)
 252:             {
 253:                 Uri hostHeaderWebAppUrl = new Uri(operation.Params["hostheaderwebapplicationurl"].Value);
 254:                 application = SPWebApplication.Lookup(hostHeaderWebAppUrl);
 255:                 if (application == null)
 256:                 {
 257:                     return application;
 258:                 }
 259:                 bool portMatchFound = false;
 260:  
 261:                 foreach (SPIisSettings iisSettings in application.IisSettings.Values)
 262:                 {
 263:                     foreach (SPSecureBinding secureBinding in iisSettings.SecureBindings)
 264:                     {
 265:                         if (requestUri.Port == secureBinding.Port)
 266:                         {
 267:                             portMatchFound = true;
 268:                             break;
 269:                         }
 270:                     }
 271:  
 272:                     if (!portMatchFound)
 273:                     {
 274:                         foreach (SPServerBinding serverBinding in iisSettings.ServerBindings)
 275:                         {
 276:                             if (requestUri.Port == serverBinding.Port)
 277:                             {
 278:                                 portMatchFound = true;
 279:                                 break;
 280:                             }
 281:                         }
 282:                     }
 283:                     if (portMatchFound)
 284:                         return application;
 285:                 }
 286:                 Console.WriteLine(SPResource.GetString("HostHeaderDoesNotMatchWebAppPort", new object[0]));
 287:                 Console.WriteLine();
 288:             }
 289:             else
 290:                 return SPWebApplication.Lookup(requestUri);
 291:  
 292:             return application;
 293:         }
 294:  
 295:     }
 296: }

The syntax of the command can be seen below:

C:\>stsadm -help gl-createsiteindb

stsadm -o gl-createsiteindb

Creates a new site collection in an existing content database.

Parameters:
        -url <url>        
        -owneremail <someone@example.com>        
        [-ownerlogin <DOMAIN\\name>]        
        [-ownername <display name>]        
        [-secondaryemail <someone@example.com>]        
        [-secondarylogin <DOMAIN\\name>        
        [-secondaryname <display name>]        
        [-lcid <language>]        
        [-sitetemplate <site template>]        
        [-title <site title>]        
        [-description <site description>]        
        [-hostheaderwebapplicationurl <web application url>]        
        [-quota <quota template>]        
        -dbname <content database name>

The following table summarizes the command and its various parameters:

Command Name Availability Build Date
gl-createsiteindb WSS 3, MOSS 2007 Released: 1/31/2008
Updated: 4/30/2009 

Parameter Name Short Form Required Description Example Usage
url   Yes The URL to the site collection to create. -url "http://portal/sites/NewSite"
owneremail oe Yes

The site owner's e-mail address.  Must be valid e-mail address, in the form someone@example.com.

-owneremail someone@example.com

-oe someone@example.com
ownerlogin ol

If your farm does not have Active Directory account creation mode enabled, then this parameter is required.

This parameter should not be provided if your farm has Active Directory account creation mode enabled, as Microsoft Office SharePoint Server 2007 will automatically create a site collection owner account in Active Directory based on the owner e-mail address.

The site owner's user account.  Must be a valid Windows user name, and must be qualified with a domain name, for example, domain\name

-ownerlogin domain\name

-ol domain\name
ownername on No

The site owner's display name.

-ownername "Gary Lapointe"

-on "Gary Lapointe"
secondaryemail se No

The secondary site owner's e-mail address.  Must be valid e-mail address, in the form someone@example.com.

-secondaryemail someone@example.com

-se someone@example.com
secondarylogin sl

If your farm does not have Active Directory account creation mode enabled, then this parameter is required.

This parameter should not be provided if your farm has Active Directory account creation mode enabled, as Microsoft Office SharePoint Server 2007 will automatically create a site collection owner account in Active Directory based on the owner e-mail address.

The secondary site owner's user account.  Must be a valid Windows user name, and must be qualified with a domain name, for example, domain\name

-secondarylogin domain\name

-sl domain\login
secondaryname sn No

The secondary site owner's display name.

-secondaryname "Pam Lapointe"

-sn "Pam Lapointe"
lcid   No

A valid locale ID, such as "1033" for English.  You must specify this parameter when using a non-English template.

-lcid 1033
sitetemplate st No

Specifies the type of template to be used by the newly created site.

The value must be in the form name#configuration. If you do not specify the configuration, configuration 0 is the default (for example, STS#0). The list of available templates can be customized to include templates you create.

-sitetemplate STS#0

-st STS#0
title t No

The title of the new site collection

-title "New Site"
description desc No

Description of the site collection.

-description "New Site Description"

-desc "New Site Description"
hostheaderwebapplicationurl hhurl No

A valid URL assigned to the Web application by using Alternate Access Mapping (AAM), such as "http://server_name".

When the hostheaderwebapplicationurl parameter is present, the value of the url parameter is the URL of the host-named site collection and value of the hostheaderwebapplicationurl parameter is the URL of the Web application that will hold the host-named site collection.

-hostheaderwebapplicationurl http://newsite

-hhurl http://newsite
quota   No

The quota template to apply to sites created on the virtual server.

-quota Portal
dbname db Yes

The name of the Microsoft SQL Server database or Microsoft SQL Server 2000 Desktop Engine (Windows) (WMSDE) database used for Windows SharePoint Services data.

-dbname SharePoint_Content1

-db SharePoint_Content1

Here's an example of how to create a site in an existing content database:

stsadm -o gl-createsiteindb -url "http://intranet/sites/testsite1" -owneremail "someone@domain.com" -ownerlogin "domain\someone" -ownername "Some User" -sitetemplate "BLANKINTERNET#2" -title "Test Site" -dbname "SharePoint_ContentDB1"

Update 4/30/2009: I’ve updated the code so that it now creates the default site groups for the site collection.