none
Create a SharePoint Site Collection with a custom Template using Powershell in SharePoint Online 2013 RRS feed

  • Question

  • Hi,

    I had a requirement in my organization that we want to create some basic custom template based on Team Site, Publishing Site in SharePoint Online 2013 (Office 365). Can we apply and activate those custom templates at Tenancy level? So that when next time if Tenant Admin required to create fresh site collection based on the custom Template. So custom template will appear in the custom part section and it will directly select and created the new site collection by the tenant Admin using the browser. Or any other way that we can create a PowerShell script in SPO 2013.So that after the activation of the script the custom template will be available in the template tag section at the time of new site collection creation.

    Your reply will be highly appreciated.

    Thanks & Regards

    Rakesh

    Friday, April 28, 2017 11:39 AM

All replies

  • Hi Rakesh,

    The following PowerShell Tommy for your reference:

    <# 
    
        Before using this script, install the SharePoint Online Client Components SDK
        https://www.microsoft.com/en-us/download/details.aspx?id=42038
    
    #>
    
    Set-PSDebug -Strict
    Set-StrictMode -Version 2
    
    <#
    .Synopsis
       Use this script to create site collections in SharePoint Online
    .DESCRIPTION
       This script supports normal, as well as custom templates
       The default template is "STS#0"
    .EXAMPLE
       CreateSiteCollections -NumberOfSites 5 -Prefix "Custom"
    .EXAMPLE
       CreateSiteCollections -NumberOfSites 5 -Prefix "Custom" -Suffix "_Portal" -SiteTemplate "BLANKINTERNETCONTAINER#0"
    .EXAMPLE
       CreateSiteCollections -NumberOfSites 5 -Prefix "Custom" -SiteTemplate "MyTemplate.wsp" -IsCustomTemplate
    .FUNCTIONALITY
       Create SharePoint Online site collections
    #>
    
    function CreateSiteCollections{
        [CmdletBinding()]
        param(
            #security
            [string]$Domain = "*domainprefix*.onmicrosoft.com",
            [string]$UserName = "*youradminaccount*@$domain",
            [string]$UserPassword = "*youradminpassword*",
            [string]$AdminUrl = "https://*domainprefix*-admin.sharepoint.com",
            #site details
            [string]$SiteUrl = "https://*domainprefix*.sharepoint.com",
            [string]$SiteTitle = "My Team Site",
            [string]$SiteOwner = "*owneraccount*@$domain",
            [string]$Prefix = "Custom", #Site names will be *Prefix*ID*_*Suffix*
            [string]$Suffix = "",
            [string]$SiteTemplate = "STS#0", #Team Site is the default template used
            [switch]$IsCustomTemplate,
            #site numbers
            [int]$StartIndex = 1,
            [Parameter(Mandatory=$true)]
            [ValidateRange(1,100)]
            [int]$NumberOfSites,
            [int]$BatchSize = 5
        )
    
        function GetContext($Url){
    
            $Context = New-Object Microsoft.SharePoint.Client.ClientContext($Url)      
              
            $credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserName, $(ConvertTo-SecureString $UserPassword -AsPlainText -Force))
            
            $Context.Credentials = $credentials
            $Context.RequestTimeout = -1
            $Context.PendingRequest.RequestExecutor.RequestKeepAlive = $true
            $Context.PendingRequest.RequestExecutor.WebRequest.KeepAlive = $false
            $Context.PendingRequest.RequestExecutor.WebRequest.ProtocolVersion = [System.Net.HttpVersion]::Version10
    
            return $Context
        }
    
        function ExecuteLoad([Microsoft.SharePoint.Client.ClientContext]$ExecContext, $ReloadObject, $Retry = 0){
            try{
                $ExecContext.ExecuteQuery()
            }catch{
                Write-Host "Something went wrong..." -ForegroundColor Yellow
                Write-Host $Error[0].Exception.Message -ForegroundColor Yellow
                if($Retry -lt 5){
                    #Implement your retry logic here
                }else{
                    ProcessError -Message $Error[0].Exception.Message
                }
            }
        }
    
        function WaitForOperations($JobContext, $Operations, $Description){
            
            Write-Host "$Description executing..."
            $TotalOperations = $Operations.length
            if($TotalOperations -eq 0){
                Write-Host "Nothing to execute!"
                return
            }
            while ($true)
            {       
                $CompletedJobs = (($Operations | ? IsComplete -EQ $true) | Measure-Object).Count
                Write-Host "$Description status: $CompletedJobs of $TotalOperations completed!"
                if($CompletedJobs -eq $TotalOperations){
                    Write-Host "Operation completed!" -ForegroundColor Green
                    break
                }
                Sleep -Seconds 10
                $Operations | % { 
                    $_.RefreshLoad() 
                }
                ExecuteLoad -ExecContext $JobContext -ReloadObject $Operations
            }
        }
    
        function ProcessError($Message){
            Write-Host "$Message`n" -ForegroundColor Red
            Write-Host $Error[0].Exception.Message -ForegroundColor Red
            $ErrorLog += $Message
        }
    
        function DeleteSites ($Sites){
            Write-Host "Getting Sites"
            #Set Admin Context
            $Context = GetContext -Url $AdminUrl
            #Get the Tenant object
            $Tenant = New-Object Microsoft.Online.SharePoint.TenantAdministration.Tenant($Context)
            #Get all site collections
            $TenantSiteCollections=$Tenant.GetSiteProperties(0,$false) 
            $Context.Load($TenantSiteCollections) 
            ExecuteLoad -ExecContext $Context
            $Operations = @()
            $TenantSiteCollections | % {
                if($_.Url -in $Sites){
                    $SiteDeleteUrl = $_.Url
                    try{
                        Write-Host "Site with url $SiteDeleteUrl will be deleted" -ForegroundColor Yellow
                        $Operation = $Tenant.RemoveSite($SiteDeleteUrl)
                        $Context.Load($Operation)
                        $Operations += $Operation
                    }catch{
                        ProcessError("Error deleting $SiteDeleteUrl")
                    }
                }
            }
    
            ExecuteLoad -ExecContext $Context -ReloadObject $Operations
    
            WaitForOperations -JobContext $Context -Operations $Operations -Description "Site deletion"
    
            $Context.Dispose()
            
        }
    
        function RecycleSites($Sites){
            Write-Host "Getting Deleted Sites"
            #Set Admin Context
            $Context = GetContext -Url $AdminUrl
            #Get the Tenant object
            $Tenant = New-Object Microsoft.Online.SharePoint.TenantAdministration.Tenant($Context)
            $TenantDeletedSiteCollections=$Tenant.GetDeletedSiteProperties(0) 
            $Context.Load($TenantDeletedSiteCollections) 
            ExecuteLoad -ExecContext $Context
            $Operations = @()
            $OperationsStarted = 0
            $TenantDeletedSiteCollections | % {
                if($_.Url -in $Sites){
                    $SiteRecycleUrl = $_.Url
                    try{
                        Write-Host "Site with url $SiteRecycleUrl will be removed from the recycle bin" -ForegroundColor Yellow
                        $Operation = $Tenant.RemoveDeletedSite($SiteRecycleUrl)
                        $Context.Load($Operation)       
                        $Operations += $Operation
                    }catch{
                        ProcessError("Error removing $SiteRecycleUrl from recycle bin")
                    }
                }
            }
            ExecuteLoad -ExecContext $Context -ReloadObject $Operations
    
            WaitForOperations -JobContext $Context -Operations $Operations -Description "Recycled sites deletion"
    
            #dispose
            $Context.Dispose()
        }
    
        function CreateSites($Sites){
            #Set Admin Context
            $Context = GetContext -Url $AdminUrl
            #Get the Tenant object
            $Tenant = New-Object Microsoft.Online.SharePoint.TenantAdministration.Tenant($Context)
            $Operations = @()
            $Sites | % {
                $SiteCreateUrl = $_
                try{
                    #Set the Site Creation Properties values
                    $properties = New-Object Microsoft.Online.SharePoint.TenantAdministration.SiteCreationProperties
                    $properties.Url = $SiteCreateUrl
                    $properties.Title = $SiteTitle
                    if(!$IsCustomTemplate){
                        $properties.Template = $SiteTemplate
                    }
                    $properties.Owner = $UserName
                    $properties.StorageMaximumLevel = 250
                    $properties.UserCodeMaximumLevel = 50
                    $properties.TimeZoneId = 3
                    $properties.Lcid = 1033
     
                    #Create the site using the properties
                    Write-Host "Creating site collection at url $SiteCreateUrl"
                    $Operation = $Tenant.CreateSite($properties)
                    $Context.Load($Operation)          
                    $Operations += $Operation
                }catch{
                    ProcessError("Error creating site $SiteCreateUrl")
                }
            }
    
            ExecuteLoad -ExecContext $Context
    
            WaitForOperations -JobContext $Context -Operations $Operations -Description "Site creation"
    
            #dispose
            $Context.Dispose()
    
        }
    
        function ApplyTemplate($Sites){
            
            $Sites | % {
                $SiteApplyUrl = $_
                try{
    
                    Write-Host "Uploading and applying template $SiteTemplate to $SiteApplyUrl" -ForegroundColor White
    
                    $Context = GetContext -Url $SiteApplyUrl
    
                    $PackageFileStream = New-Object System.IO.FileStream($PackagePath, [System.IO.FileMode]::Open) 
                
                    Write-Host "Uploading template" -ForegroundColor Gray
                    $SolutionGallery =  $Context.Web.Lists.GetByTitle("Solution Gallery") 
                    $SolutionGalleryRootFolder = $solutionGallery.RootFolder
    
                    $SPFileInfo = New-Object Microsoft.SharePoint.Client.FileCreationInformation 
                    $SPFileInfo.Overwrite = $true 
                    $SPFileInfo.ContentStream = $PackageFileStream 
                    $SPFileInfo.URL = $SiteTemplate 
    
                    $UploadedFile = $SolutionGalleryRootFolder.Files.Add($SPFileInfo)
                    $Context.Load($UploadedFile)
                    ExecuteLoad -ExecContext $Context
    
                    Write-Host "Activating template" -ForegroundColor Gray
    
                    $PackageInfo = New-Object Microsoft.SharePoint.Client.Publishing.DesignPackageInfo
                    $PackageInfo.PackageName = $SiteTemplate 
                    $PackageInfo.PackageGUID = [GUID]::Empty 
                    $PackageInfo.MajorVersion = "1" 
                    $PackageInfo.MinorVersion = "0"
                
                    [Microsoft.SharePoint.Client.Publishing.DesignPackage]::Install($Context, $Context.Site, $PackageInfo, $UploadedFile.ServerRelativeUrl)
                    ExecuteLoad -ExecContext $Context
    
                    Write-Host "Applying template" -ForegroundColor Gray
    
                    $AvailableTemplates = $Context.Site.GetWebTemplates(1033, 0)
                    $Context.Load($AvailableTemplates)
                    ExecuteLoad -ExecContext $Context
    
                    #$AvailableTemplates
                    $Context.Site.RootWeb.ApplyWebTemplate($AvailableTemplates[$AvailableTemplates.Count - 1].Name)
                    ExecuteLoad -ExecContext $Context
    
                    Write-Host "Deleting temporary file" -ForegroundColor Gray
                    $UploadedFile.DeleteObject()
                    ExecuteLoad -ExecContext $Context
    
                    Write-Host "`n"
                }catch{
                    ProcessError("Error applying template to site $SiteApplyUrl")
                }finally{
                    $PackageFileStream.Dispose()
                    $Context.Dispose()
                }
            }
                            
        }
    
        function SetSecurity ($Sites){
            $Sites | % {
                try{
                    Write-Host "Adding site collection admin $SiteOwner to $_" -ForegroundColor White
    
                    $Context = GetContext -Url $_
    
                    $User = $Context.Web.EnsureUser($SiteOwner);
    	            $Context.Load($User);
    	            ExecuteLoad -ExecContext $Context
    
    	            $User.IsSiteAdmin = $true
    	            $User.Update()
    	            ExecuteLoad -ExecContext $Context
    
                    
                }catch{
                    ProcessError("Error applying security to site $_")
                }finally{
                    $Context.Dispose()
                }
            }
        }
    
        Write-Host "`n`n`t`t`t- U2U SPO Site Creation Script -`n`n" -ForegroundColor Cyan   
    
        $ErrorLog = @()
    
        #import required modules
        try{
            Write-Host "Loading Assemblies`n" -ForegroundColor Magenta
            $ClientAssembyPath = resolve-path("C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI") -ErrorAction Stop
            $TenantAssembyPath = resolve-path("C:\Program Files\SharePoint Client Components\16.0\Assemblies") -ErrorAction Stop
            Add-Type -Path ($ClientAssembyPath.Path + "\Microsoft.SharePoint.Client.dll")
            Add-Type -Path ($ClientAssembyPath.Path + "\Microsoft.SharePoint.Client.Runtime.dll")
            Add-Type -Path ($TenantAssembyPath.Path + "\Microsoft.Online.SharePoint.Client.Tenant.dll")
    
            if($IsCustomTemplate){
                Add-Type -Path ($ClientAssembyPath.Path + "\Microsoft.SharePoint.Client.Publishing.dll")
                $CurrentPath = Convert-Path(Get-Location)
                $PackagePath = resolve-path($CurrentPath + "\Templates\" + $SiteTemplate) -ErrorAction Stop
            }
        }catch{
            Write-Host "Can't load assemblies..." -ForegroundColor Red
            Write-Host $Error[0].Exception.Message -ForegroundColor Red
            exit
        }
    
        try{
        
            $indexes = $StartIndex..($StartIndex + $NumberOfSites - 1)
            [string[]]$SiteNames = @() 
            $indexes | % { $SiteNames += ($SiteUrl + "/sites/" + $Prefix + $_ + $Suffix) }
            Write-Host "The following sites will be created:" -ForegroundColor Magenta
            $SiteNames
    
            $CurrentIndex = 0
    
            While($CurrentIndex -lt $SiteNames.Length){ 
            
                if($CurrentIndex + $BatchSize -le $SiteNames.Length){
                    $BatchSiteNames = $SiteNames[$CurrentIndex..($CurrentIndex+($BatchSize - 1))] 
                }else{
                    $BatchSiteNames = $SiteNames[$CurrentIndex..($SiteNames.Length - 1)] 
                }
                Write-Host "`nProcessing batch of sites $CurrentIndex -> $($CurrentIndex + $BatchSiteNames.Length)" -ForegroundColor Yellow
        
                #Delete sites first if they exist!
                Write-Host "`nDeleting existing sites`n" -ForegroundColor Magenta
                DeleteSites -Sites $BatchSiteNames
    
                #Recycle sites first if they exist!
                Write-Host "`nRecycling deleted sites`n" -ForegroundColor Magenta
                RecycleSites -Sites $BatchSiteNames
    
                #now create the sites   
                Write-Host "`nCreating the site collections`n" -ForegroundColor Magenta    
                CreateSites -Sites $BatchSiteNames
    
                #now apply the template if needed
                if($IsCustomTemplate){   
                    Write-Host "`nApplying Custom Template`n" -ForegroundColor Magenta    
                    ApplyTemplate -Sites $BatchSiteNames
                }
    
                Write-Host "`nSetting Security`n" -ForegroundColor Magenta    
                SetSecurity -Sites $BatchSiteNames
    
                $CurrentIndex += $BatchSiteNames.Length
    
            }
    
            if($ErrorLog.Count -ne 0){
                Write-Host "`nThe script was executed successfully, but some errors occured:" -ForegroundColor Red
                $ErrorLog | % {
                    Write-Host "`n$_" -ForegroundColor Red
                }
                Write-Host 'Check the $Error variable for more information' -ForegroundColor Red
            }else{
                Write-Host "`nALL DONE! Virtual pat on the shoulder and everything!`n" -ForegroundColor Magenta   
            }
                
            Write-Host "`n`t`t`t- WRITTEN BY U2U -`n`n" -ForegroundColor Cyan   
        }
        catch{
                Write-Host "Something went wrong..." -ForegroundColor Red
                throw $Error[0]
                exit
            }
        }
    
    #Uncomment any of these to get started
    
    #CreateSiteCollections -NumberOfSites 10 -BatchSize 5 -Prefix "Custom"
    #CreateSiteCollections -NumberOfSites 20 -BatchSize 5 -Prefix "Custom" -SiteTemplate "MyTemplate.wsp" -IsCustomTemplate
    #CreateSiteCollections -NumberOfSites 5 -Prefix "Custom" -Suffix "_Portal" -SiteTemplate "BLANKINTERNETCONTAINER#0"
    

    More information is here:

    Use PowerShell and CSOM to batch create SharePoint Online Site Collections with a custom template

    http://blogs.u2u.be/u2u/post/2016/01/31/Batch-create-Site-Collections-with-a-custom-template-in-SharePoint-Online-using-PowerShell.aspx

    Best Regards,

    Dennis


    Please remember to mark the replies as answers if they help.
    If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com

    Monday, May 1, 2017 7:15 AM
    Moderator