SharePoint Online: Create subsite using PowerShell script based on CSOM or PnP

Steel Contributor

In Office 365, the SharePoint "Save as Site template" is not available anymore.

 

In many case, this strategy was used by power users to create a templated site they can save and use after for the new site respecting that format (cupcake notion).

In SharePoint Online, that option is not possible and Microsoft will not invite the users to create the subsites as much as possible, the solution concept is now flat and not anymore hierarchal.

SharePoint Online is only a branch of all the applications or tools given with an Office 365 Group.

 

But for many cases, we need to continue that usage of template (preparing the Cupcake) to industrialize some repeated activities (projects, analysis, assessment, …).

 

So now, the only way is to think about that industrialization with PowerShell Scripting.

 

Into that page, you will find 2 versions I wrote for my needs, but you can easily adapt it:

  • First script based on CSOM only
  • Second script based on PnP

---------------------------------------------------------

CSOM PowerShell script:

This one is based on many articles I found on Internet associated with my development, so the sources which help me:

 

Feel free to adapt and use it as you need.

# VALUE TO ADAPT !!!!!
# --------------------------------------------------------------------------------------------
$SiteIntegrationUrl ="https://tenant.sharepoint.com/sites/SiteCollection/Subsite1"
$SubSiteURL = "testsubsite"
$SubSiteName ="_TESTSUBSITE"
$EmailAddressAccessRequest = "emailaddresstosupport@domain.com"
$RootSiteOwnerGroup = "Root Site Collection Owners"
# --------------------------------------------------------------------------------------------


function Load-DLLandAssemblies
{
	[string]$defaultDLLPath = ""

	# Load assemblies to PowerShell session 

	$defaultDLLPath = "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.dll"
	[System.Reflection.Assembly]::LoadFile($defaultDLLPath)

	$defaultDLLPath = "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.Runtime.dll"
	[System.Reflection.Assembly]::LoadFile($defaultDLLPath)

	$defaultDLLPath = "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.Online.SharePoint.Client.Tenant.dll"
	[System.Reflection.Assembly]::LoadFile($defaultDLLPath)
}


#Custom Function to Check if Site Collection Exists in Given URL
Function Check-SiteExists($MySiteURL, $MyCredentials)
{
    #Setup context
    $Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($MySiteURL)
	$Ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($MyCredentials.UserName,$MyCredentials.Password)
    $Web = $Ctx.Web
    $Ctx.Load($web)
     
    Try {
            $Ctx.ExecuteQuery()
            Return $True
        }
    Catch [Exception] {
      Write-host " >>>> ERROR MESSAGE:", $_.Exception.Message -f Red
      Return $False
     }        
}


Function Create-SPWebList([string]$MyShortTitle, [string]$MyDescription, [int]$MyListTemplateType, [Microsoft.SharePoint.Client.ClientContext]$Mycontext)
{
	Write-Host " ---------------------------------------------------------"

	$lci = New-Object Microsoft.SharePoint.Client.ListCreationInformation
	$lci.Title = $MyShortTitle
	$lci.Description = $MyDescription
	$lci.TemplateType = $MyListTemplateType   #see: https://msdn.microsoft.com/en-us/library/microsoft.sharepoint.splisttemplatetype.aspx
	$list = $Mycontext.web.lists.add($lci)
	$Mycontext.load($list)
	#send the request containing all operations to the server
	try{
		$Mycontext.executeQuery()
		write-host "    >>> info: List Created $($MyShortTitle)" -foregroundcolor green
	}
	catch{
		write-host "  >>> List Creation Error info: $($_.Exception.Message)" -foregroundcolor red
	}

	Write-Host " ---------------------------------------------------------"
}


# ---------------------------------------------------------------------------------------------------------------


Load-DLLandAssemblies

#get and save your O365 credentials
[string]$username = "AdminSiteCollection@tenant.onmicrosoft.com"
[string]$PwdTXTPath = "C:\SECUREDPWD\ExportedPWD-$($username).txt"
$secureStringPwd = ConvertTo-SecureString -string (Get-Content $PwdTXTPath)
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList $username, $secureStringPwd

$DocLibTitleLong = "Given Documents"

$DocLibTitle = -join($SubSiteURL, "GivenDocuments")
$DocLibTitleLong = -join($SubSiteName, " Given Documents")
$SubSiteTitle = -join($SubSiteName, " - Subsite Usage")

$SubSiteOwnerOwners = -join($SubSiteName, " Subsite Usage Web Owners")
$SubSiteOwnerMembers = -join($SubSiteName, " Subsite Usage Web Members")
$SubSiteOwnerVisitors = -join($SubSiteName, " Subsite Usage Web Visitors")

$SubSiteFullUrl = -join($SiteIntegrationUrl, "/", $SubSiteURL)

# ---------------------------------------------------------------------------------------------------------------
#connect to the web site using the stored credentials
Write-host " "
Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green
Write-host " ---- CONNECT THE SITE --- " -ForegroundColor green
Write-host "   CONNECTED SITE:", $SiteIntegrationUrl  -ForegroundColor Yellow

$Myctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteIntegrationUrl)

$Myctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($cred.UserName,$cred.Password)
$Myctx.RequestTimeout = 1000000 # milliseconds
$MyspoRootweb = $Myctx.Web
$Myctx.Load($MyspoRootweb)
$Myctx.ExecuteQuery()

Write-Host " "
Write-Host " ---------------------------------------------------------"
Write-Host "  >>>> # Server Version:" $Myctx.ServerVersion " # <<<<<<" -ForegroundColor Green 
Write-Host " ---------------------------------------------------------"
Write-Host " "

Write-host " -------------------------------------------------------- "
Write-host "   -->> RootSite:", $MyspoRootweb.URL -ForegroundColor green

Write-host " "
Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green
Write-host " ---- START THE SUBSITE CREATION  --- " -ForegroundColor green
Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green


$SiteExists = Check-SiteExists $SubSiteFullUrl $cred

if($SiteExists -ne $true)
{

	Write-host "   NEW SITE URL TO CREATE:", $SubSiteFullUrl  -ForegroundColor Yellow
	$Subsite = New-Object Microsoft.SharePoint.Client.WebCreationInformation
	$Subsite.WebTemplate = "STS#0"
	$Subsite.Title = $SubSiteTitle
	$Subsite.Url = $SubSiteURL
	$Subsite.Language = "1033"
	$Subsite.Description = $SubSiteTitle

	$SubWeb = $Myctx.Web.Webs.Add($Subsite)
	$Myctx.Load($SubWeb)
	Try{
		$Myctx.ExecuteQuery()
		Write-host " "
		Write-host "     >> Success info creating site for $SubSiteURL" -ForegroundColor green
	}
	Catch{
		Write-host "     >> Error info creating subsite for $SubSiteURL - $_.Exception.Message" -ForegroundColor red
	}

	$context = New-Object Microsoft.SharePoint.Client.ClientContext($SubSiteFullUrl)
	$context.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($cred.UserName,$cred.Password)
	$web = $context.web
	$context.load($web)
	
	Write-host " "
	Write-host "   Implement the Top menu navigation" -ForegroundColor green
	$web.Navigation.UseShared = $true
	$context.ExecuteQuery();
	
	Write-host " "
	Write-host "   Implement the Access Request settings: ", $EmailAddressAccessRequest -ForegroundColor green
    $Web.RequestAccessEmail = $EmailAddressAccessRequest
	$web.breakroleinheritance($false, $false)
	$web.update()
	$context.ExecuteQuery();

	$AllSiteCollectionGroups = $web.SiteGroups
	$context.Load($AllSiteCollectionGroups)
    $context.executeQuery()
	
	$existingRootSiteOwnerGroup = $AllSiteCollectionGroups.getByName($RootSiteOwnerGroup)
	Write-host " "
	Write-host "   Default Root site group", $existingRootSiteOwnerGroup -ForegroundColor green

	Write-host " "
	$PermissionLevel = $context.Web.RoleDefinitions.GetByName("Full Control")

	#Bind Permission Level to Group
	$RoleDefBind = New-Object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($context)
	$RoleDefBind.Add($PermissionLevel)
	$Assignments = $context.Web.RoleAssignments
	$RoleAssignOneNote = $Assignments.Add($existingRootSiteOwnerGroup,$RoleDefBind)
	$context.Load($existingRootSiteOwnerGroup)
	#send the request containing all operations to the server
	try{
		$context.executeQuery()
		Write-host " "
		write-host "     >> Success info: Added Root site group with Full Control" -foregroundcolor green
	}
	catch{
		Write-host " "
		write-host "     >> Error info: $($_.Exception.Message)" -foregroundcolor red
	}

	
	#Create new groups
	$MysiteGroups = $SubSiteGroupOwners, $SubSiteGroupMembers, $SubSiteGroupVisitors
	foreach ($siteGroup in $MysiteGroups){
		if ($siteGroup -like "*Web Visitors")
		{
			Write-host " "
			Write-host "   Creation of the Visitors group", $SubSiteGroupVisitors -ForegroundColor green
			$gci = New-Object Microsoft.SharePoint.Client.GroupCreationInformation
			$gci.Title = $siteGroup
			$gci.Description = $siteGroup
			$siteGroup = $context.Web.SiteGroups.Add($gci)

			$PermissionLevel = $context.Web.RoleDefinitions.GetByName("Read")

			#Bind Permission Level to Group
			$RoleDefBind = New-Object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($context)
			$RoleDefBind.Add($PermissionLevel)
			$Assignments = $context.Web.RoleAssignments
			$RoleAssignOneNote = $Assignments.Add($siteGroup,$RoleDefBind)
			$context.Load($siteGroup)
			$context.Web.Update()
			#send the request containing all operations to the server
			try{
				$context.executeQuery()
				write-host "     >> Success info: Added visitors group" -foregroundcolor green
			}
			catch{
				write-host "     >> Error info: $($_.Exception.Message)" -foregroundcolor red
			}
		}
		 
		if ($siteGroup -like "*Web Members")
		{
			Write-host " "
			Write-host "   Creation of the Members group", $SubSiteGroupMembers -ForegroundColor green
			$gci = New-Object Microsoft.SharePoint.Client.GroupCreationInformation
			$gci.Title = $siteGroup
			$gci.Description = $siteGroup
			$siteGroup = $context.Web.SiteGroups.Add($gci)

			$PermissionLevel = $context.Web.RoleDefinitions.GetByName("Edit")
			 
			#Bind Permission Level to Group
			$RoleDefBind = New-Object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($context)
			$RoleDefBind.Add($PermissionLevel)
			$Assignments = $context.Web.RoleAssignments
			$RoleAssignOneNote = $Assignments.Add($siteGroup,$RoleDefBind)
			$context.Load($siteGroup)
			$context.Web.Update()
			#send the request containing all operations to the server
			try{
				$context.executeQuery()
				write-host "     >> Success info: Added members group" -foregroundcolor green
			}
			catch{
				write-host "     >> Error info: $($_.Exception.Message)" -foregroundcolor red
			}
		}
		 
		if ($siteGroup -like "*Web Owners")
		{
			Write-host " "
			Write-host "   Creation of the Owners group", $SubSiteGroupOwners -ForegroundColor green
			$gci = New-Object Microsoft.SharePoint.Client.GroupCreationInformation
			$gci.Title = $siteGroup
			$gci.Description = $siteGroup
			$siteGroup = $context.Web.SiteGroups.Add($gci)

			$PermissionLevel = $context.Web.RoleDefinitions.GetByName("Full Control")
			 
			#Bind Permission Level to Group
			$RoleDefBind = New-Object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($context)
			$RoleDefBind.Add($PermissionLevel)
			$Assignments = $context.Web.RoleAssignments
			$RoleAssignOneNote = $Assignments.Add($siteGroup,$RoleDefBind)
			$context.Load($siteGroup)
			$context.Web.Update()
			#send the request containing all operations to the server
			try{
				$context.executeQuery()
				write-host "     >> Success info: Added owners group" -foregroundcolor green
			}
			catch{
				write-host "     >> Error info: $($_.Exception.Message)" -foregroundcolor red
			}
		}
	}

	Write-host " "
	Write-host "   Get the Site groups" -ForegroundColor green

	$context.Load($AllSiteCollectionGroups)
    $context.executeQuery()


	Write-host " "
	Write-host "   Update the Groups owners to:",  $existingRootSiteOwnerGroup.Id -ForegroundColor green
	$MyOwnersGroup = $AllSiteCollectionGroups.GetByName($SubSiteGroupOwners)
	$MyMembersGroup = $AllSiteCollectionGroups.GetByName($SubSiteGroupMembers)
	$MyVisitorsGroup = $AllSiteCollectionGroups.GetByName($SubSiteGroupVisitors)
	
	$MyOwnersGroup.Owner = $existingRootSiteOwnerGroup
	$MyOwnersGroup.Update()
	$MyMembersGroup.Owner = $existingRootSiteOwnerGroup
	$MyMembersGroup.Update()
	$MyVisitorsGroup.Owner = $existingRootSiteOwnerGroup
	$MyVisitorsGroup.Update()
	$context.executeQuery()

	Write-host " "
	Write-host "   >>>>> in Case of error, you have to go on the default group page: $($SubSiteFullUrl)/_layouts/15/permsetup.aspx	" -ForegroundColor magenta  # : /_layouts/15/permsetup.aspx	
	
	Write-host " "
	Write-host "   Define the Default SPWeb groups" -ForegroundColor green

	$context.Load($web)
	$context.executeQuery()

	Write-host "  Check the values !!!"
	Write-host "      >>> AssociatedVisitorGroup:", $web.AssociatedVisitorGroup.Id -ForegroundColor Magenta
	Write-host "      >>> AssociatedMemberGroup:", $web.AssociatedMemberGroup.Id -ForegroundColor Magenta
	Write-host "      >>> AssociatedMemberGroup:", $web.AssociatedOwnerGroup.Id -ForegroundColor Magenta

	Write-host " "
	Write-host "   Define the Visitors group:", $MyVisitorsGroup.Id -ForegroundColor green
	$web.AssociatedVisitorGroup = $MyVisitorsGroup
	$web.AssociatedVisitorGroup.Update()

	Write-host " "
	Write-host "   Define the Members group:", $MyMembersGroup.Id -ForegroundColor green
	$web.AssociatedMemberGroup = $MyMembersGroup
	$web.AssociatedMemberGroup.Update()

	Write-host " "
	Write-host "   Define the Owners group:", $MyOwnersGroup.Id -ForegroundColor green
	$web.AssociatedOwnerGroup = $MyOwnersGroup
	$web.AssociatedOwnerGroup.Update()

	$web.Update()
	$context.executeQuery()


	#Create the Doc Lib
	Write-host " "
	Write-host "   Create Doc Lib:", $DocLibTitle -ForegroundColor green
	Create-SPWebList  $DocLibTitle $DocLibTitleLong 101 $context 

	$MyList = $context.Web.Lists.GetByTitle($DocLibTitle)
	$context.Load($MyList)
	$context.executeQuery()	
	$MyList.Title = $DocLibTitleLong
	$MyList.OnQuickLaunch = $True
	$MyList.Update()
	$context.executeQuery()	
	
	#Rename Shared Document library
	Write-host " "
	Write-host "   Modification of the Shared Document lib" -ForegroundColor green
	$MyList = $context.Web.Lists.GetByTitle("Documents")
	$context.Load($MyList)
	$context.executeQuery()	
	$MyList.Title = "Other Documents"
	$MyList.OnQuickLaunch = $True
	$MyList.Update()
	$context.executeQuery()	
	
	#Create Contact List
	Write-host " "
	Write-host "   Creation of Contacts list" -ForegroundColor green
	Create-SPWebList  "SubsiteContacts" "Subsite Contacts" 105 $context 

	$MyList = $context.Web.Lists.GetByTitle("SubsiteContacts")
	$context.Load($MyList)
	$context.executeQuery()	
	$MyList.Title = "Subsite Contacts"
	$MyList.EnableAttachments = $false
	$MyList.EnableFolderCreation = $false
	$MyList.OnQuickLaunch = $True
	$MyList.Update()
	$context.executeQuery()	
	
	#Create Links List
	Write-host " "
	Write-host "   Creation of Links list" -ForegroundColor green

	Create-SPWebList  "SubsiteLinks" "Subsite Links" 103 $context 
	$MyList = $context.Web.Lists.GetByTitle("SubsiteLinks")
	$context.Load($MyList)
	$context.executeQuery()	
	$MyList.Title = "Subsite Links"
	$MyList.EnableAttachments = $false
	$MyList.EnableFolderCreation = $false
	$MyList.OnQuickLaunch = $false
	$MyList.Update()
	$context.executeQuery()	
	
	#Create Calendar List
	Write-host " "
	Write-host "   Creation of Calendar list" -ForegroundColor green

	Create-SPWebList  "SubsiteCalendar" "Subsite Calendar" 106 $context 
	$MyList = $context.Web.Lists.GetByTitle("SubsiteCalendar")
	$context.Load($MyList)
	$context.executeQuery()	
	$MyList.Title = "Subsite Calendar"
	$MyList.EnableAttachments = $false
	$MyList.EnableFolderCreation = $false
	$MyList.OnQuickLaunch = $True
	$MyList.Update()
	$context.executeQuery()	

	Write-host " "
	Write-host "   Start the Navigation Cleanup" -ForegroundColor green
	
	$QuickLaunchNodes = $web.Navigation.QuickLaunch
	$context.Load($QuickLaunchNodes)
    #send the request containing all operations to the server
    $context.executeQuery()
	for($i=$QuickLaunchNodes.Count-1;$i -ge 0; $i--)
	{
		write-host "     >>> MenuItem $($QuickLaunchNodes[$i].Title) - ID: $($QuickLaunchNodes[$i].Id)"  -foregroundcolor green
		#write-host "          >>> MenuItem Children $($QuickLaunchNodes[$i].Children) "  -foregroundcolor green

		switch ($QuickLaunchNodes[$i].Title)
		{
			"Home" {
					$QuickLaunchNodes[$i].DeleteObject()
					$context.executeQuery()
					write-host "           >>> MenuItem $($QuickLaunchNodes[$i].Title) Link Found !!!!" -foregroundcolor red
			}
			"Recent" {
					$QuickLaunchNodes[$i].DeleteObject()
					$context.executeQuery()
					write-host "           >>> MenuItem $($QuickLaunchNodes[$i].Title) Link Found !!!!" -foregroundcolor red
			}
			"Pages" {
					$QuickLaunchNodes[$i].DeleteObject()
					$context.executeQuery()
					write-host "           >>> MenuItem $($QuickLaunchNodes[$i].Title) Link Found !!!!" -foregroundcolor red
			}
			"Site contents" {
					$QuickLaunchNodes[$i].DeleteObject()
					$context.executeQuery()
					write-host "           >>> MenuItem $($QuickLaunchNodes[$i].Title) Link Found !!!!" -foregroundcolor red
			}
		}
	}

	Write-host "   Add the new Home Link into the Navigation Menu", $SubSiteFullUrl -ForegroundColor green

	$NavigationNode = New-Object Microsoft.SharePoint.Client.NavigationNodeCreationInformation
	$NavigationNode.Title = "Subsite Home"
	$NavigationNode.Url = $SubSiteFullUrl
	#$NavigationNode.AsLastNode = $true          
	$context.Load($QuickLaunchNodes.Add($NavigationNode))
	$context.executeQuery()	

	Write-host "   Add the Link into the Subsite Navigation Menu", $SiteIntegrationUrl -ForegroundColor green
	$QuickLaunchNodes = $Myctx.web.Navigation.QuickLaunch
	$Myctx.Load($QuickLaunchNodes)
	$Myctx.executeQuery()	

	$NavigationNode = New-Object Microsoft.SharePoint.Client.NavigationNodeCreationInformation
	$NavigationNode.Title = $SubSiteName
	$NavigationNode.Url = $SubSiteFullUrl
	$NavigationNode.AsLastNode = $true          
	$Myctx.Load($QuickLaunchNodes.Add($NavigationNode))
	$Myctx.executeQuery()	
	
}
else
{
	Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green
	Write-host " ====>> THE SUBSITE IS YET CREATED:", $SubSiteFullUrl -ForegroundColor red

}


Write-host " "
Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green

---------------------------------------------------------

PnP PowerShell script

The sources used for this second script are:

 

Feel free to adapt and use it as you need.

if (-not (Get-Module -ListAvailable -Name SharePointPnPPowerShellOnline)) 
{
    Install-Module SharePointPnPPowerShellOnline
}
Import-Module SharePointPnPPowerShellOnline


# VALUE TO ADAPT !!!!!
# --------------------------------------------------------------------------------------------
$SiteIntegrationUrl ="https://tenant.sharepoint.com/sites/SiteCollection/Subsite1"
$SubSiteURL = "testsubsite"
$SubSiteName ="_TESTSUBSITE"
$EmailAddressAccessRequest = "emailaddresstosupport@domain.com"
$RootSiteOwnerGroup = "Root Site Collection Owners"
# --------------------------------------------------------------------------------------------



#Custom Function to Check if Site Collection Exists in Given URL
Function Check-SiteExists($MySiteURL, $MyCredentials)
{
    Try {
			Connect-PnPOnline -Url $MySiteURL -Credentials $MyCredentials
			Return $True
        }
    Catch [Exception] {
      Write-host " >>>> ERROR MESSAGE:", $_.Exception.Message -f Red
      Return $False
     }        
}

# ---------------------------------------------------------------------------------------------------------------


#get and save your O365 credentials
[string]$username = "AdminSiteCollection@tenant.onmicrosoft.com"
[string]$PwdTXTPath = "C:\SECUREDPWD\ExportedPWD-$($username).txt"
$secureStringPwd = ConvertTo-SecureString -string (Get-Content $PwdTXTPath)
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList $username, $secureStringPwd

$DocLibTitleLong = "Given Documents"

$DocLibTitle = -join($SubSiteURL, "GivenDocuments")
$DocLibTitleLong = -join($SubSiteName, " Given Documents")
$SubSiteTitle = -join($SubSiteName, " - Subsite Usage")

$SubSiteOwnerOwners = -join($SubSiteName, " Subsite Usage Web Owners")
$SubSiteOwnerMembers = -join($SubSiteName, " Subsite Usage Web Members")
$SubSiteOwnerVisitors = -join($SubSiteName, " Subsite Usage Web Visitors")

$siteUrl = -join($SiteIntegrationUrl, "/", $SubSiteURL)

# ---------------------------------------------------------------------------------------------------------------
#connect to the web site using the stored credentials
Write-host " "
Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green
Write-host " ---- CONNECT THE SITE --- " -ForegroundColor green
Write-host "   CONNECTED SITE:", $SiteIntegrationUrl  -ForegroundColor Yellow
Connect-PnPOnline -Url $SiteIntegrationUrl -Credential $cred

Write-host " "
Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green
Write-host " ---- START THE SUBSITE CREATION  :", $siteUrl -ForegroundColor green
Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green


$SiteExists = Check-SiteExists $siteUrl $cred
if($SiteExists -ne $true)
{
	Write-host "   NEW SITE URL TO CREATE:", $siteUrl  -ForegroundColor Yellow
	#Create the subWeb for the Integration site
	New-PnPWeb -Template "STS#0" -Title $SubSiteTitle -Description $SubSiteTitle -Url $SubSiteURL -Locale "1033" -InheritNavigation -BreakInheritance
	Add-PnPNavigationNode -Title $SubSiteTitle -Url $siteUrl -Location "QuickLaunch"

	#connect to the new web site using the stored credentials
	Write-host " "
	Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green
	Write-host " ---- CONNECT THE SITE --- " -ForegroundColor green
	Write-host "   CONNECTED SITE:", $siteUrl  -ForegroundColor green
	Connect-PnPOnline -Url $siteUrl -Credentials $cred
	$MyCurrentWeb = Get-PnPWeb

	#configure the Access request system
	Set-PnPRequestAccessEmails -Emails $EmailAddressAccessRequest
	
	#Create the Doc Lib
	Write-host " "
	Write-host "   Create Doc Lib:", $DocLibTitle -ForegroundColor green
	New-PnPList -Title $DocLibTitle -Template DocumentLibrary -OnQuickLaunch
	Set-PnPList -Identity $DocLibTitle -Title $DocLibTitleLong -EnableVersioning $true -MajorVersions 100 -EnableMinorVersions $false

	#Rename Shared Document library
	Write-host " "
	Write-host "   Modification of the Shared Document lib" -ForegroundColor green
	Set-PnPList -Identity "Documents" -Title "Other Documents" -EnableVersioning $true -MajorVersions 100 -EnableMinorVersions $false

	#Create Contact List
	Write-host " "
	Write-host "   Creation of Contacts list" -ForegroundColor green
	New-PnPList -Title "SubsiteContacts" -Template Contacts -OnQuickLaunch 
	Set-PnPList -Identity "SubsiteContacts" -Title "Subsite Contacts"

	#Create Links List
	Write-host " "
	Write-host "   Creation of Links list" -ForegroundColor green
	New-PnPList -Title "SubsiteLinks" -Template Links 
	Set-PnPList -Identity "SubsiteLinks" -Title "Subsite Links"

	#Create Calendar List
	Write-host " "
	Write-host "   Creation of Calendar list" -ForegroundColor green
	New-PnPList -Title "SubsiteCalendar" -Template "Events" -OnQuickLaunch
	Set-PnPList -Identity "SubsiteCalendar" -Title "Subsite Calendar"

	#Change the Navifation menu
	Write-host " "
	Write-host "   Modification of the Navigation Menu" -ForegroundColor green
	Remove-PnPNavigationNode -Title "Pages" -Location QuickLaunch -Force 
	Remove-PnPNavigationNode -Title "Recent" -Location QuickLaunch -Force 
	Remove-PnPNavigationNode -Title "Site contents" -Location QuickLaunch -Force 
	Remove-PnPNavigationNode -Title "Home" -Location QuickLaunch -Force 
	Add-PnPNavigationNode -Title "Subsite Home" -Url $siteUrl -Location "QuickLaunch" -First

	#Configure the permission set by default
	Write-host " "
	Write-host "   Get the OwnerGroup from the root site", $RootSiteOwnerGroup -ForegroundColor green
	$owner = Get-PnPGroup -Identity $RootSiteOwnerGroup  #(Get-PnPContext).Credentials.UserName
	Write-host "    >>> Group ID:", $owner.ID -ForegroundColor green
	Set-PnPWebPermission -Identity $MyCurrentWeb.Id -Group $RootSiteOwnerGroup -AddRole "Full Control"  
	
	#Create default groups for the new web
	#here, by default owner will be the person provisioning the groups
	Write-host " "
	Write-host "   Creation of the Owners group", $SubSiteOwnerOwners -ForegroundColor green
	$ownerGroup = New-PnPGroup -Title $SubSiteOwnerOwners -Owner $RootSiteOwnerGroup
	Set-PnPGroup -Identity $SubSiteOwnerOwners -SetAssociatedGroup Owners -AddRole "Full Control"  

	Write-host " "
	Write-host "   Creation of the Members group", $SubSiteOwnerOwners -ForegroundColor green
	$memberGroup = New-PnPGroup -Title $SubSiteOwnerMembers -Owner $RootSiteOwnerGroup
	Set-PnPGroup -Identity $SubSiteOwnerMembers -SetAssociatedGroup Members -AddRole "Edit"

	Write-host " "
	Write-host "   Creation of the Visitors group", $SubSiteOwnerOwners -ForegroundColor green
	$visitorGroup = New-PnPGroup -Title $SubSiteOwnerVisitors -Owner $RootSiteOwnerGroup
	Set-PnPGroup -Identity $SubSiteOwnerVisitors -SetAssociatedGroup Visitors -AddRole "Read"

}
else
{
	Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green
	Write-host " ====>> THE SUBSITE IS YET CREATED:", $siteUrl -ForegroundColor red

}

Write-host " "
Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green
Write-host " ---- DISCONNECT THE SITE --- " -ForegroundColor green
# Quit the script and disconnect from the site
Disconnect-PnPOnline

Write-host " "
Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green

---------------------------------------------------------

 

As you can see the PnP is smaller than the CSOM, because that solution is based on interface solution. Internally PnP is using the CSOM to work with Office 365, but you have to install it and update it too.

 

Fabrice Romelard [MVP]

4 Replies

Bonjour Fabrice,

I followed the pnp powershell path but I am block with an access denied exception.

Set-PnPGroup -Identity 'Cours - Visiteurs' -SetAssociatedGroup Visitors -AddRole 'Read'
Set-PnPGroup : Access denied. You do not have permission to perform this action or access this resource.
Au caractère Ligne:1 : 9
+ Set-PnPGroup -Identity 'Cours - Visiteurs' -SetAssociatedGrou ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (:) [Set-PnPGroup], ServerUnauthorizedAccessException
+ FullyQualifiedErrorId : EXCEPTION,SharePointPnP.PowerShell.Commands.Principals.SetGroup

 

The group exist and I am connected (Connect-PnPOnline) as the tenant administrator who is also the owner of the sub site, the groups, etc. And I am using PnpPowerShellOnline 3.1.1809.0 from the PSGallery.

 

Do you had / have the same problem ?

 

Another point, my intent is to put the subsite provisionning in an Azure function. Is it better to use CSOM or PnP for stability reason.

 

Jean Marie

Dear Jean-Marie,

The part of the code "SetAssociatedGroup" is always the painful one.

It's mainly related to the modification Microsoft is doing on the SharePoint side (with the modern group sites).

This option is blocking without any real reason and the only way I found to execute it is to unlock the site before the command execution with that command:

 - Set-SPOSite [RootSiteCollectionURL] -DenyAddAndCustomizePages 0

 

I'm really not sure that will help you in that case, but in many one it was the solution to.

 

Fab

 

No more authorization error while setting the default group !!

 

Thanks for the tip.

 

I will put the value back for now as I really can't image the side effect of this.