SharePoint site collection provisioning in powershell

What PowerShell Module To Use?

I wrote about it before, so I invite you to the article:
Which module to choose, SharePoint PowerShell Online or PnP

Action Order

In short, to create a PowerShell site collection from PowerShell:

  • Install the appropriate PowerShell module
  • Connect to SharePoint
  • Create a site collection
  • Connect to the newly created collection
  • Customize it according to your needs – appearance, content, permissions
  • Close connection

Required Permissions

What will we need? You need an account with permissions to perform the planned activities. It does not have to be a Global Administrator, SharePoint PnP allows you to connect in the context of another user, it is important that he has the appropriate permissions to perform the tasks. SharePoint Administrator is enough to create a Site Collection.

Installing the SharePoint PnP module

Module installation:

Install-Module SharePointPnPPowerShellOnline

Upgrading from an earlier version:

Update-Module SharePointPnPPowerShell*

Checking the currently installed version:

Get-Module SharePointPnPPowerShell* -ListAvailable | Select-Object Name,Version | Sort-Object Version -Descending

More information:

GitHub: https://github.com/SharePoint/PnP-PowerShell
MS Doc: https://docs.microsoft.com/en-us/powershell/…

Connection to SharePoint

The basic method for establishing a connection is to run the command:

Connect-PnPOnline –Url https://yoursite.sharepoint.com –Credentials (Get-Credential)

If we use MFA, we can connect using the command:

Connect-PnPOnline -Url https://yoursite.sharepoint.com -UseWebLogin

When we start creating a new collection, we open the first connection to the top-level collection. As a parameter, we give then: https://YOURTENANT.sharepoint.com

Once we create a new collection and make a connection to make changes to it, we’ll connect to the character’s address: https://YOURTENANT.sharepoint.com/sites/NEWSITE

In both cases, we will be redirected to a website where we will have to log in.

In the event that we want to automate the operation of our script and execute it without the need for our interference, we must use a different solution.

We prepare a variable containing our credentials:

$adminLogin = ‘adminroleuser@YOURTENANT.onmicrosoft.com’
$adminPassword = ‘yourtopsecretpassword’

$secstr = New-Object -TypeName System.Security.SecureString
$adminPassword.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
$userCredential = new-object -typename  System.Management.Automation.PSCredential -argumentlist $adminLogin, $secstr

And we use it to login. The finished fragment of the PowerShell script opening the connection to Sharepoint could look like this:

#config

$sharepointUrl = ‘https://YOURTENANT.sharepoint.com’
$adminLogin = “adminroleuser@yourdomain”
$adminPassword = ‘yourtopsecretpassword’

#prepare user credentials

$secstr = New-Object -TypeName System.Security.SecureString
$adminPassword.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
$userCredential = new-object -typename System.Management.Automation.PSCredential -argumentlist $adminLogin, $secstr

#connect

$pnpConnection = Connect-PnPOnline –Url $sharepointUrl -ReturnConnection –Credentials $userCredential

Create a site collection

We create a new collection of sites using the New-PnPSite command. The command syntax is:

$newSiteUrl = New-PnPSite -Type TeamSite -Owner ‘user@yourdomain’ -Title ‘Your Site Title’ -Alias ‘YourSiteAlias’ -Wait

The Type parameter has two values: CommunicationSite and TeamSite.

For a CommunicationSite site, the Url parameter is also required. The command looks like this:

$newSiteUrl = New-PnPSite -Connection $pnpConnection -Type CommunicationSite -Title ‘New Communication Site’ -Url ‘https://YOURTENANT.sharepoint.com/sites/newsitename’ -SiteDesign Showcase

The SiteDesign parameter, available for CommunicationSite sites, allows us to choose a site layout. Available values are: Blank, Showcase and Topic

After creating a new site, we open a connection to it. The whole thing could look like this:

#create new site

$newSiteUrl = New-PnPSite -Connection $pnpConnection -Type TeamSite -Owner ‘user@yourdomain’ -Title ‘Your Site Title’ -Alias ‘YourSiteAlias’ -Wait

#connect to it

$pnpNewConnection = Connect-PnPOnline –Url $newSiteUrl -ReturnConnection –Credentials $userCredential

Customizing layout

Color palette

To modify the site’s color palette, use the command: Set-PnPTheme
The format of the command is:

Set-PnPTheme -Connection $pnpNewConnection -ColorPaletteUrl ‘/_catalogs/theme/15/palette025.spcolor’

Adding And Removing Items From The Menu

Adding items to the menu is done by:

Set-PnPTheme -Connection $pnpNewConnection -ColorPaletteUrl ‘/_catalogs/theme/15/palette025.spcolor’

The Location parameter specifies to which menu we add the parameter and takes the values: TopNavigationBar, SearchNav, QuickLaunch, Footer.

The following parameters are also available:
-First – switch indicating that the link should be placed at the beginning
-Parent – to point to the parent link and build a multi-level menu

The item can be removed from the menu with the command:

Remove-PnPNavigationNode -Connection $pnpNewConnection -Id $linkId -Force

We will get a list of available menu items by following the command:

Get-PnPNavigationNode

As a result, we will get a list of menu items in the form:

Id Title Visible Url
— —– ——- —
1031 Home True /sites/newTeamSite
2002 Conversations True /sites/newTeamSite/_layouts/15/groupstatus.aspx
2003 Documents True /sites/newTeamSite/Shared Documents/Forms/AllItems.aspx
2004 Notebook True /sites/newTeamSite/_layouts/15/groupstatus.aspx?Target=NOTEBOOK
2005 Pages True /sites/newTeamSite/SitePages/Forms/ByAuthor.aspx
1034 Site contents True /sites/newTeamSite/_layouts/15/viewlsts.aspx

For the selected item we can get more detailed information:

Get-PnPNavigationNode -Id 2003|fl

AudienceIds :
Children : {}
Id : 2003
IsDocLib : True
IsExternal : False
IsVisible : True
ListTemplateType : DocumentLibrary
Title : Documents
Url : /sites/newTeamSite/Shared Documents/Forms/AllItems.aspx
Context : OfficeDevPnP.Core.PnPClientContext
Tag :
Path : Microsoft.SharePoint.Client.ObjectPathIdentity
ObjectVersion :
ServerObjectIsNull : False
TypedObject : Microsoft.SharePoint.Client.NavigationNode

To delete the selected link:

Get-PnPNavigationNode| Where-Object {$_.Title -in ‘Pages’,’Site contents’}

And finally, the command would look like this:

Get-PnPNavigationNode -Connection $pnpNewConnection|Where-Object {$_.Title -in 'Pages','Site contents'}|Remove-PnPNavigationNode
-Connection $pnpNewConnection -Force

Adding Lists and Libraries

We create a new list using the command:

New-PnPList -Template Contacts -Title “New List Title” -Url ‘Link to List’ -OnQuickLaunch

Where as parameters we can indicate:
-Title as the name of the new list
-Url as a link to the list that appears in the menu
-OnQuickLaunch as a switch that determines whether the list should be visible in the menu
and
-Template – template by which the list will be created, for example, they can be Contacts or DocumentLibrary

In our case, creating a new document library will look like this:

$newList = New-PnPList -Connection $pnpNewSiteConnection -Template DocumentLibrary -Title “Archive Docs” -Url ‘Archive’ -OnQuickLaunch

Adding Webparts

We customize the site by placing webpages in sections. To understand the logic of operation, it is best to train in the standard SharePoint editor in the web interface and later try to do the same through PowerShell.

To get a list of current page components, use the command:

$page = Get-PnPClientSidePage -Identity ‘Home’
Get-PnPClientSideComponent -Page $page

As a result, we will get a list of page elements:

InstanceId Type Title Section Column Position PropertiesJson
———- —- —– ——- —— ——– ————–
b8246ff8-eb43-4bac-a907-926177278caa ClientSideWebPart News 1 1 1 {“carouselSett…
15737a91-1038-4e68-a9f3-900b85d640fb ClientSideWebPart Events 1 2 1 {“selectedList…
a000c570-f4ce-49c1-bcab-fe081d612352 ClientSideWebPart Planner 2 1 1 {“planId”:””,”…
4522ddff-8754-43d1-9397-1603cb5523c1 ClientSideWebPart List 2 1 2 {“isDocumentLi…

Element PropertiesJson contains the configuration of the given WebPart element. If you do not know what the correct syntax should look like, the solution may be to configure the element through a web editor and then read the correct settings. E.g:

$element = Get-PnPClientSideComponent -Page $page|Where-Object {$_.Title -eq ‘List’}
$element.PropertiesJson

As a result we will get:

{“isDocumentLibrary”:”true”,”filterBy”:{},”title”:”Team Documents”}

And the variable:

$element.Properties

It allows you to get a full list of parameters:

HasValues : False
Type : String
Parent : {}
Root : {isDocumentLibrary, filterBy, title}
Next :
Previous :
Path : isDocumentLibrary
First :
Last :
LineNumber : 1
LinePosition : 27

Type : Object
HasValues : False
First :
Last :
Count : 0
Parent : {{}}
Root : {isDocumentLibrary, filterBy, title}
Next :
Previous :
Path : filterBy
LineNumber : 1
LinePosition : 40
IsReadOnly : False
AllowNew : True
AllowEdit : True
AllowRemove : True
SupportsChangeNotification : True
SupportsSearching : False
SupportsSorting : False
IsSorted : False
SortProperty :
SortDirection : Ascending
IsFixedSize : False
SyncRoot : System.Object
IsSynchronized : False
Keys : {}

HasValues : False
Type : String
Parent : {}
Root : {isDocumentLibrary, filterBy, title}
Next :
Previous :
Path : title
First :
Last :
LineNumber : 1
LinePosition : 69

To compose your own site, you can use the following code snippet. If the page contains any elements, you can use the ClearPage command to clear it. Remember to post your changes: Publish ()

An example below:

$page = Get-PnPClientSidePage -Identity ‘Home’
$page.ClearPage()

Add-PnPClientSidePageSection -Page $page -SectionTemplate TwoColumnLeft -Order 1
Add-PnPClientSidePageSection -Page $page -SectionTemplate OneColumn -Order 2

Add-PnPClientSideWebPart -Page $page -Section 1 -Column 1 -DefaultWebPartType News -WebPartProperties @{layoutId=”FeaturedNews”;title=”Aktualności”}
Add-PnPClientSideWebPart -Page $page -Section 1 -Column 2 -DefaultWebPartType Events -WebPartProperties @{layout=”Compact”;layoutId=”Flex”;dataSource=3;maxItemsPerPage=5;dataProviderId=”Search”;title=”Nadchodzące wydarzenia”}

$element = Get-PnPList -Identity ‘Documents’
Add-PnPClientSideWebPart -Page $page -Section 2 -Column 1 -DefaultWebPartType List -WebPartProperties @{isDocumentLibrary=”true”;title=”Dokumenty Zespołu”;selectedListId = $element.Id}


page.Publish()

Permission Modification

When modifying permissions, please note that default SharePoint groups are created for each site collection. The groups are:

Owners, Members i Visitors

You can change the default permissions for each of these groups. You can create a new group, give it permissions and assign users to it. You can also change the default permission roles such as Reader, Editor or Contributor

The get-PnpGroup command allow getting a list of SharePoint groups defined for a given site

Get-PnPGroup

Id Title LoginName
— —– ———
6 Site Members Site Members
4 Site Owners Site Owners
5 Site Visitors Site Visitors

We change the permissions of the default Members group

$membersGroup = Get-PnPGroup -AssociatedMemberGroup

Get-PnPGroupPermissions -Identity $membersGroup.Id

#Result
#
#Name RoleTypeKind Hidden Order
#—- ———— —— —–
#Contribute Contributor False 64

Set-PnPGroupPermissions -Identity $membersGroup.Id -RemoveRole @(‘Contribute’) -AddRole @(‘Edit’)

Get-PnPGroupPermissions -Identity $membersGroup.Id

#Result after change
#
#Name RoleTypeKind Hidden Order
#—- ———— —— —–
#Edit Editor False 48

Ready Script – Create And Customize

The code of the ready script creating and configuring site collections based on the configuration. For convenience, the script is divided into two parts. Functions and calling code along with the configuration. For the script to work, you must save both parts in one file and start or part with the functions save as a module and import in the calling part. I leave these tasks for independent experiments.

Functions
Connect – open connection to SharePoint
CreateNewSite – creates new site collection and connects to it
CustomizeNewSite – customizes new collection, configuration is reades from a variable

 
function Connect($_tenant, $_adminLogin, $_adminPassword){
    $secstr = New-Object -TypeName System.Security.SecureString
    $_adminPassword.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
    $Global:userCredential = new-object -typename System.Management.Automation.PSCredential -argumentlist $_adminLogin, $secstr        
    $Global:pnpSPOConnection = Connect-PnPOnline –Url https://$_tenant.sharepoint.com -ReturnConnection –Credentials $Global:userCredential
}



function CreateNewSite([ref]$config){         
    #create new site collection
    $newSiteUrl = New-PnPSite -Type TeamSite -Owner $config.Value.SPOConfiguration.newSiteOwner -Title $config.Value.newTeamName -Alias $config.Value.newTeamAlias -Lcid $config.Value.SPOConfiguration.newSiteLanguage -Wait

    #connect to it and save connection
    $Global:pnpNewSiteConnection = Connect-PnPOnline –Url $newSiteUrl -ReturnConnection –Credentials $Global:userCredential

    #save new site url into config
    $config.Value.SPOConfiguration += @{ newSiteUrl = $newSiteUrl }

    $membersGroup = Get-PnPGroup -Connection $Global:pnpNewSiteConnection -AssociatedMemberGroup              

    #get from config permissions to change
    $removeRoles = $config.Value.SPOConfiguration.newSiteMembersPermissions.removeroles
    $addRoles = $config.Value.SPOConfiguration.newSiteMembersPermissions.addroles       

    #set new permissions
    Set-PnPGroupPermissions -Connection $Global:pnpNewSiteConnection -Identity $membersGroup.Id -RemoveRole @($removeRoles) -AddRole @($addRoles)               
}



function CustomizeNewSite($configuration){
    $configuration.keys|sort|ForEach-Object{  
        $config = $($configuration[$_])               
        if ($_ -eq 'SPOConfiguration'){
                            $paletteUrl = $config.SPOColorPaletteUrl                                                        
                            Set-PnPTheme -Connection $Global:pnpNewSiteConnection -ColorPaletteUrl $paletteUrl 
        }
        if ($_ -eq 'SPONavigationNodes'){
                    $config.Keys | ForEach-Object{                               
                        if ($($config[$_].op) -eq 'add'){                            
                            $ret = Add-PnPNavigationNode -Connection $Global:pnpNewSiteConnection -Title $_ -Url $($config[$_].url) -Location $($config[$_].location) -External            
                        }
                        if ($($config[$_].op) -eq 'del'){
                            Remove-PnPNavigationNode -Connection $Global:pnpNewSiteConnection -Id $($config[$_].id) -Force
                        }
                    }                     
        }
        if ($_ -eq 'SPOLists'){
            $config.Keys | ForEach-Object{
                $ret = New-PnPList -Connection $Global:pnpNewSiteConnection -Title $($config[$_].title) -Template $($config[$_].template) -Url $($config[$_].url) -OnQuickLaunch:$($config[$_].OnQuickLaunch)
            }
        }
        if ($_ -eq 'SPOWebparts'){
            $page = Get-PnPClientSidePage  -Connection $Global:pnpNewSiteConnection -Identity "Home"
            $order=0;                        
            $config.Keys|sort|foreach{
                $s=1
                if ($($config[$_].object) -eq 'section'){
                    $order++                                
                    $ret = Add-PnPClientSidePageSection -Connection $Global:pnpNewSiteConnection -Page $page -SectionTemplate $($config[$_].SectionTemplate) -Order $order -ZoneEmphasis 0
                }              
                if ($($config[$_].object) -eq 'webpart'){ 
                    if ( $config[$_].ListIdentity.Length -gt 0){                                                                                                  
                        $sharedDocuments = Get-PnPList -Connection $Global:pnpNewSiteConnection -Identity $($config[$_].ListIdentity)
                        $selectedListId = $sharedDocuments.Id
                        $config[$_].wpproperties += @{selectedListId = $selectedListId}
                    }                    
                    $ret = Add-PnPClientSideWebPart -Connection $Global:pnpNewSiteConnection -Page $page -Section $order -Column $($config[$_].column) -DefaultWebPartType $($config[$_].Type) -WebPartProperties $($config[$_].wpproperties)
                 }
            }            
            $ret = Set-PnPClientSidePage -Connection $Global:pnpNewSiteConnection -Identity $page -LayoutType Home -Publish                        
         }
    } 
}

Execution- configuration variable and our 3 functions execution . That’s all, new site collection is ready.

#
# New Site Config
#
$newSiteConfig = @{

    'newTeamName'     = 'New Site Name'
    'newTeamAlias'    = 'NewSiteAlias'

    'SPOConfiguration'=    @{                                
            newSiteOwner    = 'MrNobody@TenantName.OnMicrosoft.com'
            newSiteLanguage = 1033      # only 1033 and 1045 allowed here                                    
            newSiteDesign   = 'Topic'                                    
            SPOColorPaletteUrl = '/_catalogs/theme/15/palette025.spcolor'  
            newSiteMembersPermissions = @{ removeroles = 'Edit';   addroles = 'Contribute' }                                                                        
    } 
        
    'SPONavigationNodes' = @{
            'Pages' = @{ 'op' = 'del'; 'id' = 2005 }
            'Site contents' = @{ 'op' = 'del'; 'id' = 1034 }
            'Link1' = @{ 'op' = 'add'; url = 'http://site1.com'; location = 'TopNavigationBar' }
            'Link2' = @{ 'op' = 'add'; url = 'https://site2.com'; location = 'TopNavigationBar' }
    }

    'SPOLists' =           @{
            'iportant contacts1' = @{'title' = 'Important Contacts'; 'template' = 'Contacts'; url = 'Important Contacts'; 'OnQuickLaunch' = $true }
            'archive docs' = @{'title' = 'Archive'; 'template' = 'DocumentLibrary'; url = 'Archive Doc'; 'OnQuickLaunch' = $true }
    }

    'SPOWebparts' =         @{                                    
            1 = @{ object = 'section'; SectionTemplate = 'TwoColumnLeft';  }
            2 = @{ object = 'webpart'; column = '1'; Type = 'News'; wpproperties = @{layoutId='FeaturedNews';title='Corp News'} }
            3 = @{ object = 'webpart'; column = '2'; Type = 'Events'; wpproperties = @{layout="Compact";layoutId=”Flex”;dataSource=3;maxItemsPerPage=5;dataProviderId="Search";title=”Upcoming Events”}  }
            4 = @{ object = 'section'; SectionTemplate = 'OneColumn';  }
            5 = @{ object = 'webpart'; column = '1'; Type = 'Planner'; wpproperties = @{plannerViewMode='board';isFullScreen=$false} }
            6 = @{ object = 'webpart'; column = '1'; Type = 'List'; wpproperties = @{isDocumentLibrary='true';title='Dokumenty Zespołu'} ; ListIdentity='Documents'}
    }              
}

#
# Credentials
#
$tenant = 'TenantName'
$adminLogin = 'admin@TenantName.onmicrosoft.com'
$adminPassword = 'TopSecretPassword'

#
# Create and customize SharePoint site collection
#
Connect $tenant $adminLogin $adminPassword
CreateNewSite ([ref]$newSiteConfig)
CustomizeNewSite $newSiteConfig