<# 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"