Tworzenie i konfiguracja kolekcji witryn SharePoint w PowerShell

Jakiego Modułu PowerShell Używać?

O tym pisałem już wcześniej, zapraszam więc do artykułu:
Jaki moduł wybrać, SharePoint PowerShell Online czy PnP

Kolejność Działań

W skrócie, aby utworzyć z poziomu PowerShell kolekcję witryn SharePoint nalezy:

  • Zainstalować odpowiedni moduł PowerShell’a
  • Połączyć się z SharePoint’em
  • Utworzyć kolekcję witryn
  • Połączyć się z nowo utworzoną kolekcją
  • Dostosować ją według własnych potrzeb – wygląd, zawartość, uprawnienia
  • Zamknąć połączenie

Wymagane Uprawnienia

Co będzie nam potrzebne? Potrzebne jest konto z uprawnieniami do wykonania planowanych czynności. Nie musi to być Global Administrator, SharePoint PnP pozwala na połączenie w kontekście innego użytkownika, ważne aby miał odpowiednie uprawnienia do wykonywanych czynności. SharePoint Administrator w zupełności wystarczy do utworzenia Kolekcji witryn.

Instalacja modułu SharePoint PnP

Instalacja modułu:

Install-Module SharePointPnPPowerShellOnline

Aktualizacja wcześniejszej wersji:

Update-Module SharePointPnPPowerShell*

Sprawdzenie aktualnie zainstalowanej wersji:

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

Więcej informacji:

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

Połączenie do SharePoint’a

Podstawową metodą nawiązania połączenia jest wywołanie polecenia:

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

Jeżeli korzystamy z MFA, połączenie możemy nawiązać korzystając z polecenia:

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

Gdy przystępujemy do tworzenia nowej kolekcji, pierwsze połączenie otwieramy do kolekcji najwyższego poziomu. Jako parametr podajemy wtedy: https://TWOJANAZWA.sharepoint.com

Gdy stworzymy już nową kolekcję i będziemy nawiązywać połączenie aby dokonać w niej zmian, połączymy się do adresu postaci: https://TWOJANAZWA.sharepoint.com/sites/NOWAKOLEKCJA

W obu przypadkach zostaniemy przekierowani do strony WWW, gdzie będziemy musieli się zalogować.

W przypadku, gdy chcemy automatyzować działanie naszego skryptu i wykonywać go bez konieczności naszej ingerencji musimy skorzystać z innego rozwiązania.

Przygotowujemy zmienną zawierającą nasze poświadczenia:

$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

I wykorzystujemy ją do logowania. Gotowy fragment skryptu PowerShell otwierającego połączenie do Sharepointa, mógłby wyglądać następująco:

#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

Utworzenie kolekcji witryn

Nową kolekcję witryn tworzymy poleceniem New-PnPSite. Składnia polecenia wygląda następująco:

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

Parametr Type przyjmuje dwie wartości: CommunicationSite i TeamSite.

Dla witryny typu CommunicationSite wymagany jest również parametr Url. Polecenie wygląda wtedy następująco:

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

Parametr SiteDesign, dostępny dla witryn typu CommunicationSite, pozwala nam wybrać układ witryny. Dostępne wartości to: Blank, Showcase i Topic

Po utworzeniu nowej witryny, otwieramy do niej połączenie. Całość mogłaby wyglądać tak:

#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

Dostosowanie wyglądu witryny

Paleta kolorów

Do modyfikacji palety kolorów witryny służy polecenie: Set-PnPTheme 
Format polecenia wygląda następująco:

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

Dodanie i usuniecie elementów z menu

Dodawanie elementów do menu wykonujemy poleceniem:

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

Parametr Location określa do którego menu dodajemy parametr i przyjmuje wartości: TopNavigationBar, SearchNav, QuickLaunch, Footer.

Dostępne są również takie parametry jak:
-First – przełącznik, oznaczający, że link ma byc umieszczony na początku
-Parent – aby wskazać link nadrzędny i budować menu wielopoziomowe

Usunięcie elementu z menu wykonamy poleceniem:

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

Listę dostępnych pozycji menu otrzymamy wykonując polecenie:

Get-PnPNavigationNode

W wyniku otrzymamy listę elementów menu w postaci:

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

Dla wybranej pozycji możemy uzyskać bardziej szczegółowe informacje:

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

Aby usunąć wybrany link:

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

I ostatecznie, polecenie mogłoby wyglądać tak:

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

Dodawanie list i Bibliotek

Nową listę tworzymy przy pomocy polecenia:

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

Gdzie jako parametry możemy wskazać:
-Title jako nazwę nowej listy
-Url jako link do listy, który pojawi się w menu
-OnQuickLaunch jako przełącznik określający czy lista ma być widoczna w menu
oraz
-Template – szablon wg, którego lista zostanie utworzona, dla przykładu mogą to być Contacts czy DocumentLibrary

W naszym wypadku, utworzenie nowej biblioteki dokumentów będzie wyglądać następująco:

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

Dodawanie Webpartów

Dostosowanie zawartości witryny wykonujemy poprzez umieszczenie na niej webpartów ułożonych w sekcje. Aby zrozumieć logikę działania najlepiej potrenować w standardowym edytorze SharePointa w interfejsie WWW a dopiero później spróbować poprzez PowerShell.

Aby uzyskać listę aktualnych składników strony należy użyć polecenia:

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

W wyniku otrzymamy listę elementów strony:

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 zawiera konfigurację danego elementu WebPart. Jeżeli nie wiemy jak powinna wyglądać prawidłowa składnia, rozwiązaniem może być skonfigurowanie elementu poprzez edytor WWW a następnie odczytanie poprawnych ustawień. Na przykład:

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

W wyniku uzyskamy:

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

A zmienna:

$element.Properties

Pozwala uzyskać pełną listę parametrów:

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

Aby skomponować własną witrynę można wykorzystać nastepujący fragment kodu.
Jeżeli strona zawiera jakieś elementy mozna uzyć polecenia ClearPage aby ją wyczyścić.
Nalezy pamietać o opublikowaniu zmian: Publish()

Poniżej przykład:

$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()

Modyfikacja uprawnień

Modyfikując uprawnienia należy zwrócić uwagę na to, że dla każdej kolekcji witryn zostają utworzone doyślne grupy SharePoint, które posiadają uprawnienia do kolekcji.

Są to Owners, Members i Visitors

Można zmienić domyślne uprawnienia dla jednej z tych grup. Mozna stworzyć nową grupę, nadać jej uprawnienia i przyposać do niej użytkowników. Mozna również zmienić domyślne role uprawnień takie jak Reader, Editor czy Contributor

Polecenie get-PnpGroup pozwoli nam uzyskać listę grup SharePoint zdefiniowanych dla danej witryny

Get-PnPGroup

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

Zmieniamy uprawnienia domyślnej grupy Members

$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

Gotowy skrypt – Utwórz i dostosuj

Poniżej zamieszczam kod gotowego skryptu tworzącego i konfigurującego kolekcje witryn na podstawie konfiguracji. Dla ułatwienia skrypt podzieliłem na 2 cześci. Funkcje i kod wywołujący wraz z konfiguracją. Aby skrypt działał należy obie cześci zapisać w jednym pliku i uruchomić lub cześć z funkcjami zapisac w formie modułu i zaimportować w części wywołującej. Te zadania pozostawiam już do samodzielnych eksperymentów.

Funkcje

Connect – połączenie z SharePoint
CreateNewSite – tworzy nową kolekcję i otwiera połączenie do niej
CustomizeNewSite – modyfikuje nową kolekcję na podstawie konfiguracji zapisanej w zmiennej

 
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                        
         }
    } 
}

Wykonanie – zmienna konfiguracyjna oraz wywołanie 3 funkcji. To wszystko, nowa kolekcja witryn jest gotowa.

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