Create Publishing Sites with PowerShell

 

Often, we want to provision publishing sites within a site collection without using the interface. Adding publishing sites to a site collection is time consuming, and it gets more complicated if you are using SharePoint Variations. If you are using the Out of the Box Publishing Site template, you have no choice to pick any custom Page Layout you have created, but it uses the default out of the box Page Layout. Of course, you can create your own Publishing Site template and use it, but this takes some time to develop and you will need to deploy it across your WFE servers. Also, creating your own Publishing Site template will not work if you are using Variations. The target site during the Variation site provision will always use the default Publishing Site or Publishing Site with Workflow template.

This becomes very frustrating if you are creating many sites and you have to do it manually. Typically, in a variation site, let’s say we are doing an English and French variations, the site URLs for English and French are different. For example, the English URL of a specific site will be http://contoso/en/EnglishSite and in French, you may want it to be http://contoso/fr/FrenchSite. This different URLs is, most of the times, required in global organization where they need to have localization I their URLs. This process can very time consuming after creating the site because you have to perform many manual steps like:

  1. Changing Page Layout of the welcome page
  2. Changing the Title of the welcome page
  3. Adding any custom list to the sites
  4. Changing the URL of the target site
  5. Changing the Page Layout of the target site welcome page
  6. Changing the Title of the target site
  7. Changing the Title of the target site welcome page
  8. Adding any custom list to the sites

In one of the cases I have encountered, the number of steps were up to 40, which is insane if you have to repeat it 500 times. So, I came up with a script that can eliminate all of these steps with one single PowerShell script that read the info from a CSV file, provisions the site, then configure the sites in English and French. The CSV file has 5 main variables (of course you can/remove as many as you want):

  1. Provide the full path to the site to be provisioned
  2. Provide the title of the site
  3. Provide the Page Layout for the landing page
  4. Provide the unique URL of the French site
  5. Provide the French title

The script will perform the following actions:

  1. Create a log file of every action, in case there is an error
  2. Create the site
  3. Modify the page layout
  4. Change the title of the welcome page
  5. Wait until the Variation French site has been created to resume
  6. Change the French URL
  7. Change the French Title
  8. Change the Title of the welcome page

Here is the script, but you can download the script here along with the CSV file.

#Load sharepoint snapin

$snapin = Get-PSSnapin | Where-Object {$entry.Name -eq 'Microsoft.SharePoint.Powershell'}

if ($snapin -eq $null)

{

    Add-PSSnapin "Microsoft.SharePoint.Powershell"

}

#Define variables

$logMessage =""

$siteURL = "http://contoso"

$site = Get-SPSite $siteURL

$sitesCSV = Import-Csv C:\Publishing-Sites.csv

#sort the object so that top level sites get created before lower level ones in order to avoid errors

$sitesCSV = $sitesCSV | Sort-Object {$_.EN_URL}

#array to contain the sites that are created

$added = @()

#CSV file is now in memory delete the original so that a new one can be added.

Remove-Item D:\Hub\Hub-Sites.csv

$currentDate = get-date

$logFile = "C:\CreationLog.csv"

foreach($entry in $sitesCSV){

                $WebURL = $entry.EN_URL

                $WebURLFR = $entry.FR_URL

                $WebTitle = $entry.EN_Title

                $WebTitleFR = $entry.FR_Title

                $PageLayoutName = $entry.Template + ".aspx"

    #check row values for proper input

    #check if blank fields

    if(($webURL -eq "") -or ($WebTitle -eq "") -or ($PageLayoutName -eq ".aspx") -or ($WebURLFR -eq "") -or ($WebTitleFR -eq ""))

    {

        #blank fields found

        $row = "$currentDate,ERROR,Blank fields found,$entry"

        $row | Out-File -Append $logFile

        continue

    }

    #check if french URL is full URL or more than one level

    if($WebURLFR -contains "/")

    {

        #french URL is bad

        $row = "$currentDate,ERROR,Bad French URL,$entry"

        $row | Out-File -Append $logFile

        continue

    }

    #check for over 256 characters for title and truncate

    if(($WebTitle.length -gt 254) -or ($WebTitleFR.length -gt 254))

    {

                $WebTitle = $WebTitle.substring(0,253);

                $WebTitleFR = $WebTitleFR.substring(0,253);

    }

    #check if english URL is full URL

    if(($WebURL -like "http*") -eq $false)

    {

        #french URL is bad

        $row = "$currentDate,ERROR,Bad english URL,$entry"

        $row | Out-File -Append $logFile

        continue

    }

    #check if URL contains special characters

    if(($WebURL.Replace("http://","") -notmatch "^[a-zA-Z0-9\s-]") -or ($WebURLFR -notmatch "^[a-zA-Z0-9\s-]"))

    {

        $row = "$currentDate,ERROR,URL contains special characters,$entry"

        $row | Out-File -Append $logFile

        continue

    }

    #check if site exists

    $exists = (Get-SPWeb $WebURL -ErrorAction SilentlyContinue) -ne $null

    if($exists -eq $true)

    {

        #site exists. Write to log file and move to next entry

        $row = "$currentDate,WARNING,Duplicate Site,$entry"

        $row | Out-File -Append $logFile

        continue

    }

    try{

                #Create English Site

        $logMessage = "Creating site"

                New-SPWEB -Url $WebUrl -Template CMSPUBLISHING#0 -Name $WebTitle -AddToQuickLaunch -UseParentTopNav -erroraction stop

                $web = Get-SPWeb $WebUrl

                #Enable French alternate language

        $logMessage = "Setting english alternate language"

                $web.IsMultilingual = $true;

                $web.AddSupportedUICulture(1036);

                $web.update();

                #Get Publishing Site and Web

        $logMessage = "Getting english publishing site"

                $PublishingSite = New-Object Microsoft.SharePoint.Publishing.PublishingSite($Web.Site)

                $PublishingWeb = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($web)

                #Get New Page Layout

        $logMessage = "Getting english page layout"

                $SitePageLayouts = $PublishingSite.GetPageLayouts($false)

                $NewPageLayout = $SitePageLayouts | ? {$_.Name -eq $PageLayoutName}

                #update landing page Page Layout and title

        $logMessage = "Setting English layout and title"

                $PublishingPage = $PublishingWeb.GetPublishingPage($WebURL + "/Pages/default.aspx") 

                $PublishingPage.CheckOut()

                $PublishingPage.Title = $WebTitle

        if($NewPageLayout -eq $null)

        {

            $row = "$currentDate,WARNING,Bad Template name,$entry"

            $row | Out-File -Append $logFile

        }

        else

        {

                    $PublishingPage.Layout = $NewPageLayout

        }

                    $PublishingPage.ListItem.Update();

                    $PublishingPage.CheckIn("Page layout Updated via PowerShell")

                $web.Update()

        #add the row to the added array

        $added += $entry

        #log site creation

        $row = "$currentDate,INFO,English Site Created,$entry"

        $row | Out-File -Append $logFile

    }

    catch

    {

        #there was an error.  Log error message to log file and move to next entry

        $row = "$currentDate,ERROR,$logMessage,$entry"

        $row | Out-File -Append $logFile

        continue

    }

    finally

    {

                $web.dispose()

    }   

}

if($added.length -le 0)

{

    return

}

#wait 5 minutes for variations to be created

Start-Sleep -Seconds 300

#start french site creation

$logMessage = "Start french site creation"

$siteURL = "http://contoso"

$RelationShipListName = "Relationships List"

$FieldName = "ObjectID"

$site = Get-SPSite $siteURL

$spWeb = Get-SPWeb $SiteURL

#iterate through @added as it now contains all the sites that have been created

foreach ($frenchRow in $added)

{  

                try{

        $numberOfTries = 0

        $exists = $true

        do{

            $spRelationshipList = $spweb.GetList($RelationShipListName)

            $ENUrl = $frenchRow.EN_URL

                    $FRTitle = $frenchRow.Fr_Title

                    $NewFRUrl = $frenchRow.FR_URL

                    #Get French site URL

                    foreach($EN_Item in $spRelationshipList.Items)

                    {

                                $EN_hyperlink = New-Object Microsoft.SharePoint.SPFieldUrlValue($EN_Item[$FieldName])

                                if ($EN_hyperlink.URL -eq $ENUrl)

                                {

                                                $GroupGUID = $EN_Item["GroupGuid"]

                                                $ID = $EN_Item[“ID”]

                                }

                    }

 

                    foreach($FR_Item in $spRelationshipList.Items)

                    {

                                $FR_hyperlink = New-Object Microsoft.SharePoint.SPFieldUrlValue($FR_Item[$FieldName])

                                if (($GroupGUID -eq $FR_Item["GroupGuid"]) -and ($ID -ne $FR_Item[“ID”]))

                                {

                                                $CurrFRUrl = $FR_hyperlink.URL

                                }

                    }           

            #has variation been created? 

            #if not we wait 5 minutes for up to two tries in order to let the variation timer job do its thing

            $exists = (Get-SPWeb $CurrFRUrl -ErrorAction SilentlyContinue) -ne $null

            if($exists -eq $false)

            {

                Start-Sleep -Seconds 300

            }

            $numberOfTries++

        }while(($numberOfTries -le 6) -and ($exists -eq $false))

        if($exists -eq $false)

        {

            $row = "$currentDate,ERROR,Variation does not exist,$entry.ToString()"

            $row | Out-File -Append $logFile

            continue

        }

                    #Change the site Title

            $logMessage = "Change french title"

                    $web = Get-SPWeb $CurrFRUrl

                    $web.Title = $FRTitle        

                    #Enable English alternate language

            $logMessage = "Enable French site alternate language"

                    $web.IsMultilingual=$true

                    $web.AddSupportedUICulture(1033)

                    $web.Update()

                    #Get Publishing Site and Web

            $logMessage = "Get french puglishing site"

                    $PublishingSite = New-Object Microsoft.SharePoint.Publishing.PublishingSite($Web.Site)

                    $PublishingWeb = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($web)

                    #update landing page  title

            $logMessage = "Update french landing page title"

                    $PublishingPage = $PublishingWeb.GetPublishingPage($CurrFRUrl + "/Pages/default.aspx") 

                    $PublishingPage.CheckOut()

                    $PublishingPage.Title = $FRTitle

                    $PublishingPage.ListItem.Update();

                    $PublishingPage.CheckIn("Page layout Updated via PowerShell")

                    $web.Update()

            $row = "$currentDate,INFO,French Site Customized,$entry"

            $row | Out-File -Append $logFile

    }

    catch

    {

        $row = "$currentDate,ERROR,$logMessage,$entry.ToString()"

        $row | Out-File -Append $logFile

        continue

    }

    finally

    {             

                    $web.dispose()

    }

}

#sort the @added array so that we can change the URL without breaking

$added = $added | Sort-Object {$_.EN_URL} -Descending

$logMessage = "Change French URL"

#iterate through @added a second time in order to change the URLs of the french sites now that they have been customized

foreach ($frenchRow in $added)

{

    $spRelationshipList = $spweb.GetList($RelationShipListName)

                try{

        $ENUrl = $frenchRow.EN_URL

                    $FRTitle = $frenchRow.Fr_Title

                    $NewFRUrl = $frenchRow.FR_URL

                    #Get French site URL

                    foreach($EN_Item in $spRelationshipList.Items)

                    {

                                    $EN_hyperlink = New-Object Microsoft.SharePoint.SPFieldUrlValue($EN_Item[$FieldName])

                                    if ($EN_hyperlink.URL -eq $ENUrl)

                                    {

                                                    $GroupGUID = $EN_Item["GroupGuid"]

                                                    $ID = $EN_Item[“ID”]

                                    }

                    }

                    foreach($FR_Item in $spRelationshipList.Items)

                    {

                                    $FR_hyperlink = New-Object Microsoft.SharePoint.SPFieldUrlValue($FR_Item[$FieldName])

                                    if (($GroupGUID -eq $FR_Item["GroupGuid"]) -and ($ID -ne $FR_Item[“ID”]))

                                    {

                                                    $CurrFRUrl = $FR_hyperlink.URL

                                    }

                    }

                    $web = Get-SPWeb $CurrFRUrl

        #Change the French Site URL

                    Get-SPWeb $CurrFRUrl | Set-SPWeb -RelativeURL $NewFRUrl -ErrorAction Stop

        $web.Update()

        $row = "$currentDate,INFO,French Site URL Changed,$entry"

        $row | Out-File -Append $logFile

    }

    catch

    {

        $row = "$currentDate,ERROR,$logMessage,$entry.ToString()"

        $row | Out-File -Append $logFile

        continue

    }

    finally

    {

                    $web.dispose()

    }

}