In a previous post I detailed how to use PowerShell to perform what would be otherwise done using PSConfig to create an initial SharePoint Farm. In this post I will continue the example and show how to create your web applications using a simple XML configuration file and a reusable script.

Like the previous example I have a very basic XML file that defines my web application structure. In this example I’ve included not only the web application and application pool but also the content databases and site collections, along with the SharePoint Designer settings. Consider the XML and corresponding PowerShell a starting place to extend further if needed by adding elements for managed paths, quota templates, sites and even lists. Here’s the XML which I store in a file called WebAppConfigurations.xml:

 1<WebApplications>
 2    <WebApplication Name="SharePoint Portal (80)" 
 3                    DefaultTimeZone="12" 
 4                    DefaultQuotaTemplate="Portal" 
 5                    AllowAnonymous="false" 
 6                    AuthenticationMethod="NTLM" 
 7                    HostHeader="portal" 
 8                    Path="c:\sharepoint\webs\portal" 
 9                    Port="80" 
10                    LoadBalancedUrl="http://portal" 
11                    Ssl="false">
12        <ApplicationPool Name="SharePoint Portal App Pool" 
13                         Account="sp2010\spportalapppool" />
14        <SPDesigner AllowDesigner="true" AllowRevertFromTemplate="true" 
15                    AllowMasterPageEditing="true" ShowURLStructure="true" />
16        <ContentDatabases>
17            <ContentDatabase Server="spsql1" 
18                             Name="SharePoint_Content_Portal1" 
19                             MaxSiteCount="100" WarningSiteCount="80" 
20                             Default="true">
21                <SiteCollections>
22                    <SiteCollection Name="Portal" 
23                                    Description="" 
24                                    Url="http://portal" 
25                                    LCID="1033" 
26                                    Template="SPSPORTAL#0"
27                                    OwnerLogin="sp2010\siteowner1" 
28                                    OwnerEmail="siteowner1@sp2010.com" 
29                                    SecondaryLogin="sp2010\spadmin" 
30                                    SecondaryEmail="spadmin@sp2010.com">
31                    </SiteCollection>
32                </SiteCollections>
33            </ContentDatabase>
34         </ContentDatabases>
35    </WebApplication>
36</WebApplications>

Note that you could easily adapt the file by having the <WebApplications /> element be a child of the <Farm /> element shown in my previous post resulting in a single configuration file rather than multiple files. One thing to note is that I’m not storing the password for the application pool account which I assume exists – the password will be asked for when the script runs.

Let’s take a look at the script that does all the work:

 1function Start-WebApplicationsBuild(
 2    [string]$settingsFile = "Configurations.xml") {
 3    [xml]$config = Get-Content $settingsFile
 4
 5    #Creating individual web applications
 6    $config.WebApplications.WebApplication | ForEach-Object {
 7        $webAppConfig = $_
 8        $webApp = New-WebApplication $webAppConfig
 9
10        #Configuring SharePoint Designer Settings
11        $spd = $webAppConfig.SPDesigner
12        $allowRevert = ([bool]::Parse($spd.AllowRevertFromTemplate))
13        $allowMasterEdit = ([bool]::Parse($spd.AllowMasterPageEditing))
14        Write-Host "Setting SP Designer settings..."
15        $webApp | Set-SPDesignerSettings `
16            -AllowDesigner:([bool]::Parse($spd.AllowDesigner)) `
17            -AllowRevertFromTemplate:$allowRevert `
18            -AllowMasterPageEditing:$allowMasterEdit `
19            -ShowURLStructure:([bool]::Parse($spd.ShowURLStructure))
20
21        $webAppConfig.ContentDatabases.ContentDatabase | ForEach-Object {
22            #Creating content database
23            Write-Host "Creating content database $($_.Name)..."
24            $db = New-SPContentDatabase -Name $_.Name `
25                -WebApplication $webApp `
26                -DatabaseServer $_.Server `
27                -MaxSiteCount $_.MaxSiteCount `
28                -WarningSiteCount $_.WarningSiteCount
29
30            $_.SiteCollections.SiteCollection | ForEach-Object {
31                #Creating site collection
32                Write-Host "Creating site collection $($_.Url)..."
33                $gc = Start-SPAssignment
34                $site = $gc | New-SPSite `
35                    -Url $_.Url `
36                    -ContentDatabase $db `
37                    -Description $_.Description `
38                    -Language $_.LCID `
39                    -Name $_.Name `
40                    -Template $_.Template `
41                    -OwnerAlias $_.OwnerLogin `
42                    -OwnerEmail $_.OwnerEmail `
43                    -SecondaryOwnerAlias $_.SecondaryLogin `
44                    -SecondaryEmail $_.SecondaryEmail
45                Stop-SPAssignment -SemiGlobal $gc
46            }
47        }
48    }
49}
50
51function New-WebApplication([System.Xml.XmlElement]$webAppConfig) {
52    $poolAccount = $null
53    $tempAppPool = $null
54    $poolName = $webAppConfig.ApplicationPool.Name
55    if ([Microsoft.SharePoint.Administration.SPWebService]::ContentService.ApplicationPools.Count -gt 0) {
56        $tempAppPool = [Microsoft.SharePoint.Administration.SPWebService]::ContentService.ApplicationPools | ? {$_.Name -eq $poolName}
57    }
58    if ($tempAppPool -eq $null) {
59        Write-Host "Getting $($webAppConfig.ApplicationPool.Account) account for application pool..."
60        $accountCred = Get-Credential $webAppConfig.ApplicationPool.Account
61        $poolAccount = (Get-SPManagedAccount -Identity $accountCred.Username -ErrorVariable err -ErrorAction SilentlyContinue)
62        if ($err) {
63            $poolAccount = New-SPManagedAccount -Credential $accountCred
64        }
65    }
66
67    $allowAnon = [bool]::Parse($webAppConfig.AllowAnonymous.ToString())
68    $ssl = [bool]::Parse($webAppConfig.Ssl.ToString())
69
70    $db = $null
71    if ($webAppConfig.ContentDatabases.ChildNodes.Count -gt 1) {
72        $db = $webAppConfig.ContentDatabases.ContentDatabase | `
73            where {$_.Default -eq "true"}
74        if ($db -is [array]) {
75            $db = $db[0]
76        }
77    } else {
78        $db = $webAppConfig.ContentDatabases.ContentDatabase
79    }
80
81    #Create the web application
82    Write-Host "Creating web application $($webAppConfig.Name)..."
83    $webApp = New-SPWebApplication -SecureSocketsLayer:$ssl `
84        -AllowAnonymousAccess:$allowAnon `
85        -ApplicationPool $poolName `
86        -ApplicationPoolAccount $poolAccount `
87        -Name $webAppConfig.Name `
88        -AuthenticationMethod $webAppConfig.AuthenticationMethod `
89        -DatabaseServer $db.DatabaseServer `
90        -DatabaseName $db.DatabaseName `
91        -HostHeader $webAppConfig.HostHeader `
92        -Path $webAppConfig.Path `
93        -Port $webAppConfig.Port `
94        -Url $webAppConfig.LoadBalancedUrl `
95        -ErrorVariable err
96
97    return $webApp
98}

I’ve put the script in two different functions with Start-WebApplicationsBuild being the primary function that is called by the logged in user. The other function, New-WebApplication, is just there for readability (I wanted to separate out the code that created the application pool and web application itself). Note that, like in my previous post, I use a more complex version of this script which has the various elements broken out into many different shared helper functions and considerably more tracing and error handling added – this script is a fairly simplistic version which lets you focus on the core SharePoint 2010 PowerShell stuff without polluting the code with lots of plumbing.

With this script and XML file structure you can create as many web applications, content databases, and site collections as needed by only modifying the XML file – the script will support any number of each. One thing to be careful of – make sure you have only one <ContentDatabase /> element with a Default attribute set to “true” (this is the database that will be created when the web application is created – you may have as many <ContentDatabase /> elements as needed but you need at least one with a Default value of true).

Hopefully this script proves useful to anyone who needs to automatically create their SharePoint 2010 site structure. Stay tuned for the next piece of the scripts which will cover provisioning service applications.