Creating a SharePoint 2010 Site Structure Using PowerShell

Posted on Posted in Scripts, SharePoint 2010

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:

<WebApplications>
<WebApplication Name="SharePoint Portal (80)"
DefaultTimeZone="12"
DefaultQuotaTemplate="Portal"
AllowAnonymous="false"
AuthenticationMethod="NTLM"
HostHeader="portal"
Path="c:\sharepoint\webs\portal"
Port="80"
LoadBalancedUrl="http://portal"
Ssl="false">
<ApplicationPool Name="SharePoint Portal App Pool"
Account="sp2010\spportalapppool" />
<SPDesigner AllowDesigner="true" AllowRevertFromTemplate="true"
AllowMasterPageEditing="true" ShowURLStructure="true" />
<ContentDatabases>
<ContentDatabase Server="spsql1"
Name="SharePoint_Content_Portal1"
MaxSiteCount="100" WarningSiteCount="80"
Default="true">
<SiteCollections>
<SiteCollection Name="Portal"
Description=""
Url="http://portal"
LCID="1033"
Template="SPSPORTAL#0"
OwnerLogin="sp2010\siteowner1"
OwnerEmail="siteowner1@sp2010.com"
SecondaryLogin="sp2010\spadmin"
SecondaryEmail="spadmin@sp2010.com">
</SiteCollection>
</SiteCollections>
</ContentDatabase>
</ContentDatabases>
</WebApplication>
</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:

function Start-WebApplicationsBuild(
    [string]$settingsFile = "Configurations.xml") {
    [xml]$config = Get-Content $settingsFile

    #Creating individual web applications
    $config.WebApplications.WebApplication | ForEach-Object {
        $webAppConfig = $_
        $webApp = New-WebApplication $webAppConfig

        #Configuring SharePoint Designer Settings
        $spd = $webAppConfig.SPDesigner
        $allowRevert = ([bool]::Parse($spd.AllowRevertFromTemplate))
        $allowMasterEdit = ([bool]::Parse($spd.AllowMasterPageEditing))
        Write-Host "Setting SP Designer settings..."
        $webApp | Set-SPDesignerSettings `
            -AllowDesigner:([bool]::Parse($spd.AllowDesigner)) `
            -AllowRevertFromTemplate:$allowRevert `
            -AllowMasterPageEditing:$allowMasterEdit `
            -ShowURLStructure:([bool]::Parse($spd.ShowURLStructure))

        $webAppConfig.ContentDatabases.ContentDatabase | ForEach-Object {
            #Creating content database
            Write-Host "Creating content database $($_.Name)..."
            $db = New-SPContentDatabase -Name $_.Name `
                -WebApplication $webApp `
                -DatabaseServer $_.Server `
                -MaxSiteCount $_.MaxSiteCount `
                -WarningSiteCount $_.WarningSiteCount

            $_.SiteCollections.SiteCollection | ForEach-Object {
                #Creating site collection
                Write-Host "Creating site collection $($_.Url)..."
                $gc = Start-SPAssignment
                $site = $gc | New-SPSite `
                    -Url $_.Url `
                    -ContentDatabase $db `
                    -Description $_.Description `
                    -Language $_.LCID `
                    -Name $_.Name `
                    -Template $_.Template `
                    -OwnerAlias $_.OwnerLogin `
                    -OwnerEmail $_.OwnerEmail `
                    -SecondaryOwnerAlias $_.SecondaryLogin `
                    -SecondaryEmail $_.SecondaryEmail
                Stop-SPAssignment -SemiGlobal $gc
            }
        }
    }
}

function New-WebApplication([System.Xml.XmlElement]$webAppConfig) {
    $poolAccount = $null
    $tempAppPool = $null
    $poolName = $webAppConfig.ApplicationPool.Name
    if ([Microsoft.SharePoint.Administration.SPWebService]::ContentService.ApplicationPools.Count -gt 0) {
        $tempAppPool = [Microsoft.SharePoint.Administration.SPWebService]::ContentService.ApplicationPools | ? {$_.Name -eq $poolName}
    }
    if ($tempAppPool -eq $null) {
        Write-Host "Getting $($webAppConfig.ApplicationPool.Account) account for application pool..."
        $accountCred = Get-Credential $webAppConfig.ApplicationPool.Account
        $poolAccount = (Get-SPManagedAccount -Identity $accountCred.Username -ErrorVariable err -ErrorAction SilentlyContinue)
        if ($err) {
            $poolAccount = New-SPManagedAccount -Credential $accountCred
        }
    }

    $allowAnon = [bool]::Parse($webAppConfig.AllowAnonymous.ToString())
    $ssl = [bool]::Parse($webAppConfig.Ssl.ToString())

    $db = $null
    if ($webAppConfig.ContentDatabases.ChildNodes.Count -gt 1) {
        $db = $webAppConfig.ContentDatabases.ContentDatabase | `
            where {$_.Default -eq "true"}
        if ($db -is [array]) {
            $db = $db[0]
        }
    } else {
        $db = $webAppConfig.ContentDatabases.ContentDatabase
    }

    #Create the web application
    Write-Host "Creating web application $($webAppConfig.Name)..."
    $webApp = New-SPWebApplication -SecureSocketsLayer:$ssl `
        -AllowAnonymousAccess:$allowAnon `
        -ApplicationPool $poolName `
        -ApplicationPoolAccount $poolAccount `
        -Name $webAppConfig.Name `
        -AuthenticationMethod $webAppConfig.AuthenticationMethod `
        -DatabaseServer $db.DatabaseServer `
        -DatabaseName $db.DatabaseName `
        -HostHeader $webAppConfig.HostHeader `
        -Path $webAppConfig.Path `
        -Port $webAppConfig.Port `
        -Url $webAppConfig.LoadBalancedUrl `
        -ErrorVariable err

    return $webApp
}

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.

14 thoughts on “Creating a SharePoint 2010 Site Structure Using PowerShell

  1. I get this errors when i run the script

    PS C:\work\SiteCreation> ..\script.ps1
    The term ‘..\script.ps1’ is not recognized as the name of a cmdlet, function, s
    cript file, or operable program. Check the spelling of the name, or if a path w
    as included, verify that the path is correct and try again.
    At line:1 char:14
    + ..\script.ps1 <<<<
    + CategoryInfo : ObjectNotFound: (..\script.ps1:String) [], Comma
    ndNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

    PS C:\work\SiteCreation> Start-WebApplicationsBuild “WebAppConfigurations.xml “
    The term ‘Start-WebApplicationsBuild’ is not recognized as the name of a cmdlet
    , function, script file, or operable program. Check the spelling of the name, o
    r if a path was included, verify that the path is correct and try again.
    At line:1 char:27
    + Start-WebApplicationsBuild <<<< “WebAppConfigurations.xml “
    + CategoryInfo : ObjectNotFound: (Start-WebApplicationsBuild:Stri
    ng) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

  2. Hi Gary, Great work! I’m new to powershell and what you do here is perfect to learn it the right way.

    In your fallowing script i found a trivial error:
    $webApp = New-SPWebApplication -SecureSocketsLayer:$ssl ` -AllowAnonymousAccess:$allowAnon ` -ApplicationPool $pool.Name ` -ApplicationPoolAccount $pool.ProcessAccount ` -Name $webAppConfig.Name ` -AuthenticationMethod $webAppConfig.AuthenticationMethod ` -DatabaseServer $db.DatabaseServer ` -DatabaseName $db.DatabaseName ` -HostHeader $webAppConfig.HostHeader ` -Path $webAppConfig.Path ` -Port $webAppConfig.Port ` -Url $webAppConfig.LoadBalancedUrl ` -ErrorVariable err

    Should be:
    $webApp = New-SPWebApplication -SecureSocketsLayer:$ssl -AllowAnonymousAccess:$allowAnon -ApplicationPool $pool.Name -ApplicationPoolAccount $pool.ProcessAccount -Name $webAppConfig.Name -AuthenticationMethod $webAppConfig.AuthenticationMethod -DatabaseServer $db.Server -DatabaseName $db.Name -HostHeader $webAppConfig.HostHeader -Path $webAppConfig.Path -Port $webAppConfig.Port -Url $webAppConfig.LoadBalancedUrl -ErrorVariable err

    Well…

    -DatabaseServer $db.Server -DatabaseName $db.Name

    Instead of

    DatabaseServer $db.DatabaseServer ` -DatabaseName $db.DatabaseName

    —–
    I would like to know if it’s possible to call a function in multi-line for readability the ` char doesn’t work for me…

    Also is it possible to re-initialize all variable in a shell because re-running the script doesn’t for me. I have to open a new shell.

    Tank You
    ex:

  3. Unfortunately if you want to call a cmdlet and break it onto multiple lines then you have to use the tick character. To reload the script just re-dot source it “. .\filename.ps1”.

    Thanks for the catch on the script – I thought I’d fixed that but must have missed it in the version I posted here.

  4. Rich – I haven’t had time to update my post yet but the cmdlet was simply renamed to New-SPServiceApplicationPool – use that instead and it will work just fine.

  5. Hi Gary,
    How to execute this script…
    Even when I give:
    . .\CreateSiteStructure.ps1
    .\CreateSiteStructure.ps1

    Nothing happens.. It doesn’t go to the method at all.. Just comes out of it….

  6. Gary,
    Great script! I’ve written a variation that appears to be working up to a point. The New-SPWebApplication creates a default database just fine, but when I loop through attempting to create a new content database for a new site collection, I get:

    New-SPContentDatabase : Cannot open database “” requested by the login. The login failed.

    I’ve tried using my domain admin account. I’ve tried the Farm Account. I’ve even tried the Setup Account. I’ve even promoted all 3 to SQL Server ‘SA’, but nothing works. The SQL Server error is 18456 with a state of 38. All the literature I can find on this specific error typically points back to Test-SPContentDatabase and migration scenarios. But that’s not what I’m doing. I don’t know what else to do.
    Appreciate any suggestions or guidance you can provide.

    1. I figured it out. I was setting the -AssingNewDatabaseId attribute to $true and the -ClearChangeLog attribute to $false on my New-SPContentDatabase cmdlet call. After removing those parameters, the script functioned normally. Wow. There’s 48 hours I’ll never get back.

  7. Hi Gary, Nice work!.
    i have requirement like this…

    I need to create 30 site collections with different Content Database in a single web Application. how can i achieve this using powershell scripts.

Leave a Reply

Your email address will not be published. Required fields are marked *

*