SharePoint Online: Create Site Columns, Content Type and Associate with Custom List or Library at site collection level using PowerShell and CSOM

Functional Requirement

Admin wants to create Content Type, Site Columns and associate them with Custom List or Document Library at the site level to his O365 SharePoint Online tenant from any remote machine.

Environment Setup for remote machine

In order to enable SharePoint Online PowerShell Script execution from your remote machine. Click here for setup steps

Download

Source Code

Solution Architecture

This tool is responsible for creation of Content Type, Site Columns and associate them with Custom List or Document Library at the site level by using array of Site Columns, Content Type etc. The tool is designed for PowerShell SharePoint Online Tool. This is using CSOM as client object model. Architecture of the tool is depicted as below,

ProvisioningPatternToolContentTypeSiteColumnCreation

A. Tenant Site

Admin has setup an Office 365 tenant account with Microsoft. The O365 E1, E2, E3 or above plan has been provided. The architecture is based on the O365 mentioned plan.

B. Location Site Collection

There will be separate site collection for every identified capability location.

C. Site

There will be separate sub site for every identified location site collection.

D. Site Column

Site Columns will be created at Site Collection level.

E. Content Type

Content Type will be created at Site Collection level. These Content Type would be referring to available site columns.

F. Document Library or Custom List

Content Type will be associated to site level Document Library or Custom List.

Z. Remote Provisioning Pattern Tool

Remote Provision pattern tool is responsible for creation of sub site based on the defined array.

Interface Architecture of Provisioning Pattern

Interface Architecture of Provisioning Pattern Tool

1. InstallProvisioning.ps1

This consists of specific logic of PowerShell scripting relevant to  Subsites creation.

#This file consist of function calls to generic functions which are used to deploy Site Provisioning to specified Site Collection
#  .\InstallProvisioning.ps1 -Username "admin@XXX.sharepoint.com" -AdminUrl "https://XXX.sharepoint.com " -siteURL "https://XXX.sharepoint.com/sites/US " -groupName "US"
# Global Parameters Defined
param(
        [string]$Username= "",
    [string]$siteCollectionUrl="",
    [string]$siteURL = "",
        [string[]]$arrSubSite = ("HR", "Finance", "IT"),
        [string]$groupName = "US",
        [string[]]$arrsiteColumns = ("FileSize125", "FileType125"),
    [string[]]$arrcontentTypeName = ("CTFinanancial", "CTAccounts"),       
        [string]$workFolder = (Get-Item (“.”)).FullName,
        [string]$Logfile = "$workFolder\$(gc env:computername).log"
 )
#Locate the path where Site Provisioning code files are stored
$workFolder = (Get-Item (“.”)).FullName
. "$workFolder\ProvisioningLibrary.ps1"
#These are the Function calls to Execute Generic Site Provisioning Functions
#This call is responsible for creating site column at site collection level
Add-SPOSiteColumns $Username $AdminPassword $siteCollectionUrl $arrsiteColumns
#This call is responsible for creating Content Type and adding site columns to it
Add-SPOContentType $Username $AdminPassword $siteCollectionUrl $arrsiteColumns $arrcontentTypeName
#This call is responsible for associating the Content Type with a List or Library
Add-SPOAddContentTypeToList $Username $AdminPassword $siteURL $arrcontentTypeName

2. ProvisioningLibrary.ps1

This consists of PowerShell scripting reusable logic for Provisioning pattern.

#This file consist of generic functions used to deploy Site Provisioning to specified Site Collection
#Locate the path where Site Provisioning code files are stored
$workFolder = (Get-Item (".")).FullName
"$workFolder\CommonLibrary.ps1"
#This function used to create site columns to the specific site collection
function Add-SPOSiteColumns{
# Catch the parameters received through the function call
param(
      [Parameter(Mandatory=$true,Position=1)]
      [string]$Username,
      [Parameter(Mandatory=$true,Position=2)]
      [string]$AdminPassword,
      [Parameter(Mandatory=$true,Position=3)]
      [string]$Url,
      [Parameter(Mandatory=$true,Position=4)]
      [string[]]$arrsiteColumns
     )
    
# Creating client context object
    $context = New-Object Microsoft.SharePoint.Client.ClientContext($Url)  
    $context.credentials = $SPOCredentials
    $fields = $context.web.fields
    $context.load($fields)
     
#Send the request containing all operations to the server
    try{
        $context.executeQuery()
    LogProcess "Loaded all the Site Columns from the site Successfully" "Green"
    }
    catch{
    LogProcess "$($_.Exception.Message)" "Red"
    }
     
#Loop through each entry and create the columnGroup
    foreach ($column in $arrsitecolumns){
        #check if column already exists
        foreach($field in $fields){
            if ($field.internalname -eq $column.name){
                $columnExists = 1
            }
            else{
                $columnExists = 0
            }       
        }
#Executes if Site Column does not exist already        
        if ($columnExists -eq 0){
            #create XML entry for a new field
            $fieldAsXML = "<Field Type='Text'
            DisplayName='$column'
            Name='$column'
            Group='$groupName'/>"
             
            #See tips below for info about fieldOptions
            $fieldOption = [Microsoft.SharePoint.Client.AddFieldOptions]::AddFieldInternalNameHint
            $field = $fields.AddFieldAsXML($fieldAsXML, $true, $fieldOption)
            $context.load($field)
             
            #Send the request containing all operations to the server
            try{
                $context.executeQuery()
        LogProcess "Site Column : $column created Successfully" "Green"
            }
            catch{
        LogProcess "$($_.Exception.Message)" "Red"
            }
        }
        else{
    LogProcess "WARNING: The Site Column : $column already exists." "yellow"
        }
    }
}
#This function used to create Content Type to the specific site collection
function Add-SPOContentType{
# Catch the parameters received through the function call
param(
    [Parameter(Mandatory=$true,Position=1)]
    [string]$Username,
    [Parameter(Mandatory=$true,Position=2)]
    $AdminPassword,
    [Parameter(Mandatory=$true,Position=3)]
    [string]$Url,
    [Parameter(Mandatory=$true,Position=4)]
    [string[]]$arrsiteColumns,
    [Parameter(Mandatory=$true,Position=5)]
    [string[]]$arrcontentTypeName
     )
# Local Variables
    $contentTypeGroup = "$groupName"
    $parentContentTypeID = "0x0101"
     
# Creating client context object
    $context = New-Object Microsoft.SharePoint.Client.ClientContext($Url)
    $context.credentials = $SPOCredentials
    $fields = $context.web.fields
    $contentTypes = $context.web.contenttypes
    $context.load($fields)
    $context.load($contentTypes)
     
# Send the request containing all operations to the server
    try{
        $context.executeQuery()
    LogProcess "Loaded all the Fields and Content Types from the site Successfully" "Green"
    }
    catch{
    LogProcess "$($_.Exception.Message)" "Red"
    }
         
# Loop through all content types to verify it doesn't exist
    foreach ($contentType in $contentTypes){
    for($i = 0; $i-le $arrcontentTypeName.Length; $i++){
        if ($contentType.name -eq $arrcontentTypeName[$i]){
            LogProcess "WARNING: The content type $($contentType.name) already exists." "Yellow"
            $contentTypeExists = $true
        }
        else{
            $contentTypeExists = $false
        }
      }
    }
         
# Create content type if it doesnt exist based on specified Content Type ID
    if($contentTypeExists -eq $false){
        # load parent content type
        $parentContentType = $contentTypes.GetByID($parentContentTypeID)
        $context.load($parentContentType)
         
        # Send the request containing all operations to the server
        try{
            $context.executeQuery()
        LogProcess "Loaded parent Content Type Successfully" "Green"
        }
        catch{
        LogProcess "$($_.Exception.Message)" "Red"
        }
         
      # Create Content Type using ContentTypeCreationInformation object (ctci)
      foreach ($contenttypename in $arrcontentTypeName){
        $ctci = new-object Microsoft.SharePoint.Client.ContentTypeCreationInformation
        $ctci.Name = $contenttypename
        $ctci.ParentContentType = $parentContentType
        #$ctci.Id = $ContentTypeID
        $ctci.group = $contentTypeGroup       
        $ctci = $contentTypes.add($ctci)       
        $context.load($ctci)
         
        # Send the request containing all operations to the server
        try{
            $context.executeQuery()
        LogProcess "Content Type : $contenttypename created Successfully " "Green"
        }
        catch{
            LogProcess "$($_.Exception.Message)" "Red"
        }
       
         
        # Get the new content type object
        #$newContentType = $context.web.contenttypes.getbyid($ctci.Name)
         
        # Loop through all the columns that needs to be added
        foreach ($column in $arrsiteColumns){
            $field = $fields.GetByInternalNameOrTitle($column)
            #create FieldLinkCreationInformation object (flci)
            $flci = new-object Microsoft.SharePoint.Client.FieldLinkCreationInformation
            $flci.Field = $field
            $addContentType = $ctci.FieldLinks.Add($flci)
            LogProcess "Added Site Columns: $column to the array Successfully" "Green"
        }
         
        #$newContentType.Update($true)
         $ctci.Update($true)
        }
        # send the request containing all operations to the server
        try{
            $context.executeQuery()
        LogProcess "Added Site Columns : $arrsiteColumns to Content Types : $arrcontentTypeName Successfully" "Green"
        }
        catch{
            LogProcess "$($_.Exception.Message)" "Red"
        }
    }
}
#This function used to create sub site to the specific site collection
function Add-SPOSubsite{
# Catch the parameters received through the function call
param(
    [Parameter(Mandatory=$true,Position=1)]
    [string]$Username,
    [Parameter(Mandatory=$true,Position=2)]
    $AdminPassword,
    [Parameter(Mandatory=$true,Position=3)]
    [string]$Url,
    [Parameter(Mandatory=$true,Position=4)]
    [string]$SubSite
     )
# Creating client context object
    $context = New-Object Microsoft.SharePoint.Client.ClientContext($Url)      
    $context.credentials = $SPOCredentials
    $rootWeb = $Context.Web
    $Context.Load($rootWeb)
    $childWebs = $rootWeb.Webs
    $Context.Load($childWebs)
    try{
        $Context.executeQuery()
    LogProcess "Loaded all Subsites Successfully" "Green"
       }
    catch{
    LogProcess "$($_.Exception.Message)" "Red"
       }
    
#Create Subsite
    $WCI = New-Object Microsoft.SharePoint.Client.WebCreationInformation
    $WCI.WebTemplate = "BDR#0"
    $WCI.Description = "Finance"
    $WCI.Title = "Finance"
    $WCI.Url = "Finance"
    $WCI.Language = "1033"
    foreach($subweb in $childwebs.Title){       
    if($subweb -eq "$SubSite"){
     $columnExists = 1
         LogProcess "WARNING: Subsite $SubSite already Exists" "Yellow"
    }
    else
    {
         $columnExists = 0
    }  
}
if ($columnExists -eq 0){
     try{
        $Sub = $Context.Web.Webs.Add($WCI)
                $context.executeQuery()
        LogProcess "Subsite $SubSite created Successfully" "Green"
            }
            catch{
        LogProcess "$($_.Exception.Message)" "Red"
            }   
    }
}
#This function used to associate Content Type to specific document library
function Add-SPOAddContentTypeToList {
# Catch the parameters received through the function call
param(
    [Parameter(Mandatory=$true,Position=1)]
    [string]$Username,
    [Parameter(Mandatory=$true,Position=2)]
    $AdminPassword,
    [Parameter(Mandatory=$true,Position=3)]
    [string]$Url,
    [Parameter(Mandatory=$true,Position=4)]
    [string[]]$arrcontentTypeName
     )
# Local Variables
    $listTitle = "Document Record"
    $listDescription = "This is Testing Library"
    $listTemplate = 101
# Creating client context object
    $context = New-Object Microsoft.SharePoint.Client.ClientContext($Url)
    $context.credentials = $SPOCredentials
    $web = $context.Web
    #$web1 = $web.Webs
    $site = $context.Site 
    $context.Load($web)
    #$context.Load($web1)
    $context.Load($site)
try
    {
     $context.ExecuteQuery()
     LogProcess "Authentcated and Connected to SharePoint Online Tenant site $Url Successfully" "Green"
    }
    catch
    {
     LogProcess "Not able to authenticate to SharePoint Online $_.Exception.Message" "Red"
     return
    }
#Getting all the list information 
    $Lists = $web.Lists
    $Context.Load($Lists)
    $ContentTypes = $web.AvailableContentTypes
    $Context.Load($ContentTypes)
    try
    {
     $Context.ExecuteQuery()
     LogProcess "Successfully retrived all the List and Content Type information from the site" "Green"
    }
    catch
    {
     LogProcess "Error while getting the List and Content Type information from the site" "Red"
    }
    # send the request containing all operations to the server
    try{
        $context.executeQuery()
    LogProcess "Loaded all the Content Types and Lists from the site Successfully" "Green"
    }
    catch{
    LogProcess "$($_.Exception.Message)" "Red"
    }
 #Loop through all Document library to verify it doesn't exist
    foreach ($list in $Lists){
        if ($list.Title -eq  $listTitle){
        LogProcess "WARNING: The list : $listTitle already exists." "Yellow"
            $listExists = $true
        }
        else{
           $listExists = $false
        }
    }
# create list if it doesnt exist
    if($listExists -eq $false){
       #create list using ListCreationInformation object (lci)
    $lci = New-Object Microsoft.SharePoint.Client.ListCreationInformation
    $lci.title = $listTitle
    $lci.description = $listDescription
    $lci.TemplateType = $listTemplate
    $lci = $lists.add($lci)
    $context.load($lci)
    #send the request containing all operations to the server
    try{
        $context.executeQuery()
    LogProcess "Created List : $listTitle Successfully" "Green"
    }
    catch{
    LogProcess "info: $($_.Exception.Message)" "Red"
    }
# get the new list object
   $newList = $context.Web.Lists.GetByTitle($lci.title)
# loop through all the Content Types that needs to be added  
   # create Content Type using ContentTypeCreationInformation object (ctci)
   $ctci = new-object Microsoft.SharePoint.Client.ContentTypeCreationInformation
   foreach($ct in $ContentTypes){
    $CTNAME = $ct.Name
     for($i = 0; $i-le $arrcontentTypeName.Length; $i++){
        if($CTNAME -eq $arrcontentTypeName[$i]){
      $ctci = $context.Web.AvailableContentTypes.GetById($ct.Id)
      $newList.ContentTypesEnabled=$true
      $addList = $newList.ContentTypes.AddExistingContentType($ctci)               
      $newList.Update()
     
    }
   }
         
# send the request containing all operations to the server
        try{
            $context.executeQuery()
        LogProcess "Added ContentTypes : $arrcontentTypeName to List : $listTitle Successfully " "Green"
        }
        catch{
        LogProcess "info: $($_.Exception.Message)" "Red"
        }
  }  
}

3. CommonLibrary.ps1

This consists of PowerShell scripting reusable logic that can be use across the different projects.

# This file consist of Reusable Components amongst all the generic functions that are used to deploy Site Provisioning to specified Site Collection
# Paths to SDK. Please verify location on your computer
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
# Accept the Password of the admin site
$AdminPassword=Read-Host -Prompt "Password"-AsSecureString
$SPOCredentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Username, $AdminPassword)
#This funtion Logs the entire activity of the script to console and write it to $Logfile
Function LogProcess
{
   Param ([string]$logstring,
          [string]$color
         
  
   if($color -eq "Red")
   {
    $now = Get-Date -f "yyyy-MM-dd HH:mm:ss"  
    Add-content $Logfile -value "Error Details: $($now) $($logstring)"
   }
   else
   {
    $now = Get-Date -f "yyyy-MM-dd HH:mm:ss"  
    write-host $logstring -foregroundcolor $color
    Add-content $Logfile -value "$($now) $($logstring)"
   }     
}
Hope this article should be useful.
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s