Create SharePoint Online Site Inventory using CSOM


There is no simply method available in SharePoint Online to see the structure of the SharePoint sites.  The script below tries to get some inventory which may help you may be during the migration or may be documentation of SharePoint Online Sites.  The script iterates all sites and sub sites. The script uses Basic Client Side Object Model components.  I suggest you download the latest Components from GitHub.

$Creds = Get-Credential
$Creds.UserName
 
Connect-SPOService -Url https://tenant-Admin.sharepoint.com -Credential $Creds
Connect-MsolService -Credential $Creds
$Users = Get-MsolUser -All
$UnLicensedUsers = Get-MsolUser -UnlicensedUsersOnly
$Users.Count
$UnLicensedUsers.Count
$Users.Count – $UnLicensedUsers.Count
 
Add-Type -Path “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll”
Add-Type -Path “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll”
 
$SitesIncludingPersonal = Get-SPOSite -IncludePersonalSite $true -Limit All -Detailed
$SitesIncludingPersonal | Select * | Export-Csv -Path C:\temp\PondSites1.csv
$Sites = Get-SPOSite -Limit All -Detailed
foreach($asite in $Sites)
{
  Set-SPOUser -Site $asite.Url -LoginName $Creds.UserName -IsSiteCollectionAdmin $true -ErrorAction SilentlyContinue
}
$Sites | Export-Csv -Path C:\temp\PondSites.csv -NoClobber -NoTypeInformation
foreach($asite in $Sites)
{
  $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($asite.Url)
  #Authenticate
  $credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Creds.UserName , $Creds.Password)
  $ctx.Credentials = $credentials
 
  #Fetch the users in Site Collection
  $ctx.Load($ctx.Web.Webs)
  $ctx.ExecuteQuery()
  $Count=0
  foreach($aWEb in $ctx.Web.Webs)
  {
    $Count++;
  }
  Write-Host $asite.Url $Count
}
 
$Databases = $null
$Databases = @();
foreach($aSite in $Sites)
{
  $Users = Get-SPOUser -Site $aSite.Url -Limit All | Select * -Verbose
  foreach($User in $Users)
  {
    $DB = New-Object PSObject
    Add-Member -input $DB noteproperty ‘SiteUrl’ $aSite.Url 
    Add-Member -input $DB noteproperty ‘DisplayName’ $User.DisplayName
    Add-Member -input $DB noteproperty ‘LoginName’ $User.LoginName
    Add-Member -input $DB noteproperty ‘IsSiteAdmin’ $User.IsSiteAdmin
    Add-Member -input $DB noteproperty ‘IsGroup’ $User.IsGroup
    $Databases += $DB
  }
}
$UsersOutput = “C:\temp\AllSitesUsers.csv”
$Databases | Export-Csv -Path $UsersOutput -NoTypeInformation -Force
                                                                      
 
$Databases = $null
$Databases = @();
foreach($asite in $Sites)
{
  $Groups = Get-SPOSiteGroup -Site $asite.Url -Limit 100 | Select *
  foreach($Group in $Groups)
  {
    $DB = New-Object PSObject
    Add-Member -input $DB noteproperty ‘SiteUrl’ $aSite.Url 
    Add-Member -input $DB noteproperty ‘DisplayName’ $Group.Title
    Add-Member -input $DB noteproperty ‘LoginName’ $Group.LoginName
    Add-Member -input $DB noteproperty ‘OwnerLoginName’ $Group.OwnerLoginName
    Add-Member -input $DB noteproperty ‘OwnerTitle’ $Group.OwnerTitle
    $RolesString = “”
    foreach($Role in $Group.Roles)
    {
      $RolesString+=$Role
      $RolesString+=”,”
    }
    Add-Member -input $DB noteproperty ‘Roles’ $RolesString
    $Databases += $DB
  }
}
 
$UsersOutput = “C:\temp\AllSiteGroups.csv”
$Databases | Export-Csv -Path $UsersOutput -NoTypeInformation -Force
 
$Databases = $null
$Databases = @();
foreach($asite in $Sites)
{
  $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($asite.Url)
  #Authenticate
  $credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Creds.UserName , $Creds.Password)
  $ctx.Credentials = $credentials
 
  #Fetch the users in Site Collection
  $ctx.Load($ctx.Web.Webs)
  $Lists = $ctx.Web.Lists
  $ctx.Load($Lists)
  $ctx.ExecuteQuery()
  foreach($List in $Lists)
  {
    if($List.Hidden -eq $false)
    {
      if($List.ItemCount -gt 100)
      {
        $DB = New-Object PSObject
        Add-Member -input $DB noteproperty ‘SiteUrl’ $asite.Url 
        Add-Member -input $DB noteproperty ‘Title’ $List.Title
        Add-Member -input $DB noteproperty ‘ListType’ $List.BaseType
        Add-Member -input $DB noteproperty ‘ItemCount’ $List.ItemCount
        $Databases += $DB
        Write-Host $aSite.Url $List.Title $List.ItemCount
      }
      
    }
  }
 
  foreach($aWeb in $ctx.Web.Webs)
  {
    $Lists = $aWeb.Lists
    $ctx.Load($Lists)
    $ctx.ExecuteQuery()
    foreach($List in $Lists)
    {
      if($List.Hidden -eq $false)
      {
        if($List.ItemCount -gt 100)
        {
          $DB = New-Object PSObject
          Add-Member -input $DB noteproperty ‘SiteUrl’ $aWeb.Url 
          Add-Member -input $DB noteproperty ‘Title’ $List.Title
          Add-Member -input $DB noteproperty ‘ListType’ $List.BaseType
          Add-Member -input $DB noteproperty ‘ItemCount’ $List.ItemCount
          $Databases += $DB
          Write-Host $aSite.Url $List.Title $List.ItemCount
        }
      
      }
    }
  }
  Write-Host $asite.Url
}
 
$UsersOutput = “C:\temp\Libraries.csv”
$Databases | Export-Csv -Path $UsersOutput -NoTypeInformation -Force
$Databases | Out-GridView
 
 
$Databases = $null
$Databases = @();
foreach($asite in $Sites)
{
  #$asite = “https://leapthepond.sharepoint.com”
  $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($asite.url)
  #Authenticate
  $credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Creds.UserName , $Creds.Password)
  $ctx.Credentials = $credentials
 
  #Fetch the users in Site Collection
  $ctx.Load($ctx.Web.Webs)
  $Lists = $ctx.Web.Lists
  $ctx.Load($Lists)
  $ctx.ExecuteQuery()
  foreach($List in $Lists)
  {
    if($List.Hidden -eq $false)
    {
      $ctx.Load($List)
      $ctx.ExecuteQuery()
      if($List.WorkflowAssociations.Count -gt 0)
      {
        $DB = New-Object PSObject
        Add-Member -input $DB noteproperty ‘SiteUrl’ $asite.Url 
        Add-Member -input $DB noteproperty ‘Title’ $List.Title
        Add-Member -input $DB noteproperty ‘ListType’ $List.BaseType
        Add-Member -input $DB noteproperty ‘WorkflowsCount’ $List.WorkflowAssociations.Count
        $Databases += $DB
        Write-Host $List.Title $List.ItemCount
      }
      
    }
  }
 
  foreach($aWeb in $ctx.Web.Webs)
  {
    $Lists = $aWeb.Lists
    $ctx.Load($Lists)
    $ctx.ExecuteQuery()
    foreach($List in $Lists)
    {
      if($List.Hidden -eq $false)
      {
        if($List.ItemCount -gt 100)
        {
          $DB = New-Object PSObject
          Add-Member -input $DB noteproperty ‘SiteUrl’ $aWeb.Url 
          Add-Member -input $DB noteproperty ‘Title’ $List.Title
          Add-Member -input $DB noteproperty ‘ListType’ $List.BaseType
          Add-Member -input $DB noteproperty ‘WorkflowsCount’ $List.WorkflowAssociations.Count
          $Databases += $DB
          Write-Host $List.Title $List.ItemCount
        }
      
      }
    }
  }
  Write-Host $asite.Url
}
 
$UsersOutput = “C:\temp\Libraries.csv”
$Databases | Export-Csv -Path $UsersOutput -NoTypeInformation -Force
$Databases | Out-GridView

Bulk Upload Files to SharePoint Online with Metadata


There are many scripts on Technet that helps you upload the content from File Shares to SharePoint Online or OneDrive for Business but none of them actual copy the user information from file shares.  The script below migrates metadata like created by and created and modificed dates along with the files.  The script uses SharePoint Online Client Side Object Model.  I hope this helps someone.  

Note: The actual script was written by someone at Technet which I do not remember, I just made serious modifications to make it possible to extract and upload metadata.

[CmdletBinding()]
param(
  [Parameter(Mandatory=$True,Position=1)]
  [String]$UserName,
  [Parameter(Mandatory=$True,Position=2)]
  [String]$Password,
  [Parameter(Mandatory=$True, Position=3)]
  [String]$SiteURL,
  [Parameter(Mandatory=$True, Position=4)]
  [String]$DocLibName,
  [Parameter(Mandatory=$True, Position=5)]
  [String]$Folder,
  [Parameter(Mandatory=$False, Position=6)]
  [Switch]$Checkin,
  [Parameter(Mandatory=$False, Position=7)]
  [string]$MetaDataCSV,
  [Parameter(Mandatory=$False, Position=8)]
  [Switch]$O365
)

<#
    Add references to SharePoint client assemblies and authenticate to Office 365 site – required for CSOM

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

Add-Type -Path “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll”
Add-Type -Path “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll”

function GetUserLookupString{
    [CmdletBinding()]
    param($context, $userString)
   
    try{
        $user = $context.Web.EnsureUser($userString)
        $context.Load($user)
        ExecuteQueryWithIncrementalRetry -context $Context -retryCount 5 -delay 500
       
        # The “proper” way would seem to be to set the user field to the user value object
        # but that does not work, so we use the formatted user lookup string instead
        #$userValue = New-Object Microsoft.SharePoint.Client.FieldUserValue
        #$userValue.LookupId = $user.Id
        $userLookupString = “{0};#{1}” -f $user.Id, $user.LoginName
    }
    catch{
        Write-Host “Unable to ensure user ‘$($userString)’.”
        $userLookupString = $null
    }
   
    return $userLookupString
}

function ExecuteQueryWithIncrementalRetry([Microsoft.SharePoint.Client.ClientContext] $context, $retryCount, $delay)
{
    if ($retryCount -eq $null) { $retryCount = 5 } # default to 5
    if ($delay -eq $null) { $delay = 500 } # default to 500
    $retryAttempts = 0
    $backoffInterval = $delay
    if ($retryCount -le 0)
    {
        throw New-Object ArgumentException(“Provide a retry count greater than zero.”)
    }

    if ($delay -le 0)
    {
        throw New-Object ArgumentException(“Provide a delay greater than zero.”)
    }

    # Do while retry attempt is less than retry count
    while ($retryAttempts -lt $retryCount)
    {
        try
        {
            $context.ExecuteQuery()
            return
        }
        catch [System.Net.WebException]
        {
            $response = [System.Net.HttpWebResponse]$_.Exception.Response
            # Check if request was throttled – http status code 429
            # Check is request failed due to server unavailable – http status code 503
            if ($response -ne $null -and ($response.StatusCode -eq 429 -or $response.StatusCode -eq 503))
            {
                # Output status to console. Should be changed as Debug.WriteLine for production usage.
                Write-Host “CSOM request frequency exceeded usage limits. Sleeping for $backoffInterval seconds before retrying.”

                # Add delay for retry
                Start-Sleep -m $backoffInterval

                # Add to retry count and increase delay.
                $retryAttempts++
                $backoffInterval = $backoffInterval * 2
            }
            else
            {
                throw
            }
        }
    }
    throw New-Object Exception(“Maximum retry attempts $retryCount, has be attempted.”)
}

<#
    Define Functions
#>

<#
    Upload File – This function performs the actual file upload
#>
function UploadFile($DestinationFolder, $File)
{
    #Get the datastream of the file, assign it to a variable
    $FileStream = New-Object IO.FileStream($File.FullName,[System.IO.FileMode]::Open)

    #Create an instance of a FileCreationInformation object
    $FileCreationInfo = New-Object Microsoft.SharePoint.Client.FileCreationInformation

    #Indicate whether or not you would like to overwrite files in the event of a conflict
    $FileCreationInfo.Overwrite = $True

    #Make the datastream of the file you wish to create equal to the datastream of the source file
    $FileCreationInfo.ContentStream = $FileStream

    #Make the URL of the file equal to the $File variable which was passed to the function.  This will be equal to the source file name
    $FileCreationInfo.url = $File

    #Add the file to the destination folder which was passed to the function, using the FileCreationInformation supplied.  Assign this to a variable so that it can be loaded into context.
    $Upload = $DestinationFolder.Files.Add($FileCreationInfo)
    if($Checkin)
    {
        $Context.Load($Upload)
        #$Context.ExecuteQuery()
        ExecuteQueryWithIncrementalRetry -context $Context -retryCount 5 -delay 500
        if($Upload.CheckOutType -ne “none”)
        {
            $Upload.CheckIn(“Checked in by Administrator”, [Microsoft.SharePoint.Client.CheckinType]::MajorCheckIn)
        }
    }
    $Context.Load($Upload)
    #$Context.ExecuteQuery()
    ExecuteQueryWithIncrementalRetry -context $Context -retryCount 5 -delay 500
   
    if($MetaDataCSVData)
    {
      $MetaDataItem = $MetaDataCSVData | ? FilePath -eq $File.FullName
      Write-Host “User does not exist in metadata file.  Standard metadata will be used…” -ForegroundColor Yellow
      if($MetaDataItem)
      {
        Write-Host “Adding Metadata to File Item…$($File.FullName)”
        $listItem = $Upload.ListItemAllFields
        $Context.Load($listItem)
        #$Context.ExecuteQuery()
        ExecuteQueryWithIncrementalRetry -context $Context -retryCount 5 -delay 500
   
        $Item = $List.GetItemById($listItem.Id);
        $Context.Load($Item)
        #$Context.ExecuteQuery()
        ExecuteQueryWithIncrementalRetry -context $Context -retryCount 5 -delay 500
     
        $Item[“Description0”] = $MetaDataItem.Description      
        $Author = GetUserLookupString $Context $MetaDataItem.Author
        if($Author)
        {
          $Item[“Author”] = $Author
        }
        $Editor = GetUserLookupString $Context $MetaDataItem.Editor
        if($Editor)
        {
          $Item[“Editor”] = $Editor
        }
       
        [System.DateTime]$CreatedOnDate = $MetaDataItem.CreatedOn
        [System.DateTime]$ModifiedOnDate = $MetaDataItem.ModifiedOn
        $Item[“Created”] = $CreatedOnDate
        $Item[“Modified”] = $ModifiedOnDate
        try
        {
          $Item.Update()
        }
        catch
        { Write-Host “Exception Occured… But script will continue” }
        #$Context.ExecuteQuery()
        ExecuteQueryWithIncrementalRetry -context $Context -retryCount 5 -delay 500
      }
    }
   
   
 
}

<#
    Create Folder Function.
#>
function PopulateFolder($ListRootFolder, $FolderRelativePath)
{
    #split the FolderRelativePath passed into chunks (between the backslashes) so that we can check if the folder structure exists
    $PathChunks = $FolderRelativePath.split(“\”)

    #Make sure we start with a fresh WorkingFolder for every folder passed to the function
    if($WorkingFolder)
    {
        Remove-Variable WorkingFolder
    }

    #Start with the root folder of the list, load this into context
    $WorkingFolder = $ListRootFolder
    $Context.load($WorkingFolder)
    $Context.ExecuteQuery()

    #Load the folders of the current working folder into context
    $Context.load(($WorkingFolder.folders))
    #$Context.executeQuery()
    ExecuteQueryWithIncrementalRetry -context $Context -retryCount 5 -delay 500

    #Set the FileSource folder equal to the absolute path of the folder that passed to the function
    $FileSource = $Folder + $FolderRelativePath
   
    #Loop through the folder chunks, ensuring that the correct folder hierarchy exists in the destination
    foreach($Chunk in $PathChunks)
    {
        #Check to find out if a subfolder exists in the current folder that matches the patch chunk being evaluated
        if($WorkingFolder.folders | ? {$_.name -eq $Chunk})
        {
            #Log the status to the PowerShell host window
            Write-Host “Folder $Chunk Exists in” $WorkingFolder.name -ForegroundColor Green

            #Since we will be evaluating other chunks in the path, set the working folder to the current folder and load this into context.
            $WorkingFolder = $WorkingFolder.folders | ? {$_.name -eq $Chunk}
            $Context.load($WorkingFolder)
            $Context.load($WorkingFolder.folders)
            #$Context.ExecuteQuery()
            ExecuteQueryWithIncrementalRetry -context $Context -retryCount 5 -delay 500

        }
        else
        {
            #If the folder doesn’t exist, Log a message indicating that the folder doesn’t exist, and another message indicating that it is being created
            Write-Host “Folder $Chunk Does Not Exist in” $WorkingFolder.name -ForegroundColor Yellow
            Write-Host “Creating Folder $Chunk in” $WorkingFolder.name -ForegroundColor Green
           
            #Load the working folder into context and create a subfolder with a name equal to the chunk being evaluated, and load this into context
            $Context.load($WorkingFolder)
            $Context.load($WorkingFolder.folders)
            #$Context.ExecuteQuery()
            ExecuteQueryWithIncrementalRetry -context $Context -retryCount 5 -delay 500
            $WorkingFolder= $WorkingFolder.folders.add($Chunk)
            $Context.load($WorkingFolder)
            $Context.load($WorkingFolder.folders)
            #$Context.ExecuteQuery()
            ExecuteQueryWithIncrementalRetry -context $Context -retryCount 5 -delay 500
           
        }

    }

    #Folder is confirmed existing or created – now it’s time to list all files in the source folder, and assign this to a variable
    $FilesInFolder = Get-ChildItem -Path $FileSource | ? {$_.psIsContainer -eq $False}
   
    #For each file in the source folder being evaluated, call the UploadFile function to upload the file to the appropriate location
    Foreach ($File in ($FilesInFolder))
    {

        #Notify the operator that the file is being uploaed to a specific location
        Write-Host “Uploading file ” $file.Name “to” $WorkingFolder.name -ForegroundColor Cyan

        #Upload the file
        UploadFile $WorkingFolder $File

    }
   
   
   
}

<#
    Bind your context to the site collection
#>
$Context = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)

<#
    Create a credential object using the username and password supplied
#>
if($O365)
{
    $Creds = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserName,(ConvertTo-SecureString $Password -AsPlainText -Force))
}
else
{
    $Creds = New-Object System.Net.NetworkCredential($UserName, (ConvertTo-SecureString $Password -AsPlainText -Force))

}

<#
    Set the credentials that are used in the context.
#>
$Context.Credentials = $Creds

<#
    Retrieve the library, and load it into the context
#>
$List = $Context.Web.Lists.GetByTitle($DocLibName)
$Web = $Context.Web;
$Context.Load($Web)
$Context.Load($List)
$List.EnableVersioning = $false
$List.Fields.GetByInternalNameOrTitle(“Author”).ReadOnlyField = $false;
$List.Fields.GetByInternalNameOrTitle(“Editor”).ReadOnlyField = $false;
$List.Fields.GetByInternalNameOrTitle(“Created”).ReadOnlyField = $false;
$List.Fields.GetByInternalNameOrTitle(“Modified”).ReadOnlyField = $false;
$List.Update()
#$Context.ExecuteQuery()
ExecuteQueryWithIncrementalRetry -context $Context -retryCount 5 -delay 500

$MetaDataCSV = “C:\temp\Metadata.csv”
$MetaDataCSVData = Import-Csv -Path $MetaDataCSV

#Get a recursive list of all folders beneath the folder supplied by the operator
$AllFolders = Get-ChildItem -Recurse -Path $Folder |? {$_.psIsContainer -eq $True}

#Get a list of all files that exist directly at the root of the folder supplied by the operator
$FilesInRoot = Get-ChildItem -Path $Folder | ? {$_.psIsContainer -eq $False}

#Upload all files in the root of the folder supplied by the operator
Foreach ($File in ($FilesInRoot))
{

    #Notify the operator that the file is being uploaded to a specific location
    Write-Host “Uploading file ” $File.Name “to” $DocLibName -ForegroundColor Cyan

    #Upload the file
    UploadFile($list.RootFolder) $File
   

}

#Loop through all folders (recursive) that exist within the folder supplied by the operator
foreach($CurrentFolder in $AllFolders)
{
    #Set the FolderRelativePath by removing the path of the folder supplied by the operator from the fullname of the folder
    $FolderRelativePath = ($CurrentFolder.FullName).Substring($Folder.Length)
   
    #Call the PopulateFolder function for the current folder, which will ensure that the folder exists and upload all files in the folder to the appropriate location
    PopulateFolder ($list.RootFolder) $FolderRelativePath
}

$List.Fields.GetByInternalNameOrTitle(“Author”).ReadOnlyField = $true;
$List.Fields.GetByInternalNameOrTitle(“Editor”).ReadOnlyField = $true;
$List.Fields.GetByInternalNameOrTitle(“Created”).ReadOnlyField = $true;
$List.Fields.GetByInternalNameOrTitle(“Modified”).ReadOnlyField = $true;
$List.EnableVersioning = $True
$List.Update()
#$Context.ExecuteQuery()
ExecuteQueryWithIncrementalRetry -context $Context -retryCount 5 -delay 500

Script is also available below

https://gallery.technet.microsoft.com/Bulk-Upload-Files-to-b9ed3126

Add Owner to SharePoint Online List PowerShell


This script uses SharePoint Client Side Object Model to Add Owner to selected SharePoint Online Libraries and Lists.  You need to install the latest SharePoint Online Client Components from Github or download them from Microsoft download center.  You can change the script to only find list by title and then add the owner.  The script creates the required objects and then add the User as owner. 

$User = “user@domain.onmicrosoft.com”
#$User = Read-host “Please enter Office365 Admin User name username@domain.onmicrosoft.com. “
$Creds = Get-Credential

Connect-SPOService -Url https://tenant-admin.sharepoint.com -Credential $Creds
$site = Get-SPOSite https://site.sharepoint.com/teams/HCPTS

Add-Type -Path ‘C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll’
Add-Type -Path ‘C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll’

#Get the Client Context and Bind the Site Collection
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($site.Url)

#Authenticate
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Creds.UserName , $Creds.Password)
$ctx.Credentials = $credentials

#Fetch the users in Site Collection

$Web = $ctx.Web;
$ctx.Load($Web)
$Lists = $Web.Lists
$ctx.Load($Lists)
$ctx.ExecuteQuery()

$SAPLists = $Lists | ?{$_.Title -Like “SAP Data Quality Review*” } | Select Title
$SAPLists | Select Title

foreach($alist in $SAPLists)
{

   Write-Host $alist.Title
  $OwnersGroupTitle = “Site Owners”
  $OwnerGroup = $Web.SiteGroups.GetByName($OwnersGroupTitle)

  $FullControl = $web.RoleDefinitions.GetByName(“Full Control”)
  # Create a role assignment and apply the ‘read’ role.
  $roleAssignment = New-Object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($ctx)
  $roleAssignment.Add($FullControl)

 
  $SelectedList = $Web.Lists.GetByTitle($alist.Title)
  $ctx.Load($SelectedList)
  $ctx.Load($SelectedList.RoleAssignments.Add($OwnerGroup,$roleAssignment))
  $SelectedList.Update()
  $ctx.ExecuteQuery()
}

Script is also available below

https://gallery.technet.microsoft.com/Add-Owner-to-SharePoint-4f13d94f

SharePoint 2013 CSOM and REST Improvements


I still remember back in the time of SharePoint 2007 and before the only way to create Applications for SharePoint was either use the Server Side Object Model or SOAP based web services.  Server side object mode was great but not having the ability to run out of SharePoint Server made is limited for SharePoint web parts and custom console application only.  Web services were there but they were not enough for everything that we need and as everything was XML it was sometimes very difficult to control and extend.  I still remember that I was part of the team that developed a project that uses SharePoint Lists Web Services to get and set data from Biztalk and Project Server and I have to build classes to build CAML and ViewFields.

SharePoint 2010 came up of couple of great ways to extend application development possibilities.  The Client Object Model and REST based listdata.svc made it possible to develop remote applications for SharePoint. 

There are three flavors of Client OM in SharePoint 2010.

  • .NET Managed Client OM
  • Silverlight Managed Client OM
  • JavaScript Client OM

It is important to know that the backbone of SharePoint Client OM was client.svc.  The service which was commonly reffered as WCF Entry point was response for executing the code that you write using the proxy writing using either of the above flavors.  The code is passed to client.svc as XMLhttpRequest execution happens as Batch.  client.svc handles the serilization and parsing of replies.  Once finished processing the response is send as JSON.

Managed Client OM.

NET Client Object Model and Silverlight Client OM were almost same in the implementation and to use .NET Client OM from either a Windows or Console application you have to add references to two Microsoft.SharePoint.Client and Microsoft.SharePoint.Client.Runtime and then write the proxy code which will later executed by client.svc.  Remember that code is Client OM can execute Synchronously.

You call ClientContext.ExecuQuery() method.

Silverlight Client OM

The difference with Silverlight was the difference of DLLs. The SharePoint Root Folder Client Bin folder had two Silverlight Client Dlls that you have to add to your Silverlight Class library.  The implementation is almost same other than the fact calls to SharePoint from Silverlight must be done using asynchronously callback. 

You call ClientContext.ExecuQueryAsync(SucessDelegate, FailureDelegate); method.

JavaScript Client OM.

The third and final way of writing client side applications for SharePoint is/was JavaScript OM.  JavaScript Client OM uses SP.JS, SP.Core.JS files which contains the proxy implementation calls to client.svc.  The pattern or writing the app was same but there was difference between Windows/Silverlight and JavaScript by JavaScript using methods instead of properties and method name used in JavaScript were not same.  JS Client OM works perfectly with jQuery, JSRender and other libraries and provide support for Atom & oData standards.

By far JavaScript client OM was the most difficult way of writing SharePoint applications. Reason was Visual Studio providing strongly typed object, type checking and debug support for SharePoint objects while for debugging in JS OM we have to live with debugging in IE Developer tools only.  The place where I was during the 2010 implementation time frame I saw few people developing anything in JavaScript. 

One of the major drawbacks of Client Object Model in SharePoint was limited access to SharePoint functionality. 

image

Client Object Model Changes in SharePoint 2013

Although the overall architecture of Client OM in SharePoint 2013 remains the same but there are some major improvement.  The biggest improvement is that the all time hidden Client.svc is directly available for developers to make REST calls to do Get and POST Requests.  Client.svc implements the core features of OData specifications.  For more information please visit www.odata.org or http://msdn.microsoft.com/en-us/library/fp142385.aspx.  Please note that you can continue to use the 3 traditional methods to reach SharePoint Data.

SharePoint 2013 Client OM Extends reach to SharePoint Data by providing access to

  • User Profiles
  • Search
  • Social Feeds
  • Sharing
  • BCS
  • IRM
  • Analytics
  • E-Discoverry
  • Publishing
  • Workflow
  • Taxonomy
  • List Data

image

For Developing Managed and Silverlight

Based application using CSOM new set of assemblies have been added to SharePoint Root Bin Folder that you can access to use as proxy to write Client side code.  The methods in these assemblies use the same syntax that was used by 2010 Client OM Implement using ClientContext.  Now using Client OM you can add pages to a publishing site as well as traverse a Term Set and get or set status of your workflow. 

image

Let’s Create Couple of Examples.  There are so many possibilities when it comes to the new client OM.  So I just created small application to test some of the functionality.

image

Let’s check some basic SharePoint List Code.  In this Managed Client OM code I am connecting to my Web and adding a custom list then adding a Field using XML and then iterating to all the lists to display a message box. 

using(ClientContext context = new ClientContext(new Uri("http://intranet.contoso.com")))
{
Web RootWeb = context.Web;

ListCreationInformation newList = new ListCreationInformation();
newList.Title = "CSOM Custom List";
newList.Url = "Lists/CsomCustomList";
newList.QuickLaunchOption = QuickLaunchOptions.On;
newList.TemplateType = (int)ListTemplateType.GenericList;
List NewCustomList = RootWeb.Lists.Add(newList);


context.Load(NewCustomList);

string FieldAsXML = "<Field Name='ClientOMField' DisplayName='CustomField' Type='Text' Hidden='False' Description='Client OM Field' />";
Field fld = NewCustomList.Fields.AddFieldAsXml(FieldAsXML, true, AddFieldOptions.DefaultValue);

ListCollection allLists = RootWeb.Lists;
context.Load(allLists);

context.ExecuteQuery();
MessageBox.Show("List Created Successfully");
string strlist = "";
foreach(List aList in allLists)
{
strlist += aList.Title.ToString() + Environment.NewLine;
}

MessageBox.Show(strlist);

}

Now the below code will add a new item in this list.

using (ClientContext context = new ClientContext(new Uri("http://intranet.contoso.com")))
{
Web RootWeb = context.Web;

ListItemCreationInformation NewListItme = new ListItemCreationInformation();
List MyList = RootWeb.Lists.GetByTitle("CSOM Custom List");
ListItem MyItem = MyList.AddItem(NewListItme);
if (textBox1.Text.Length == 0)
{
MessageBox.Show("Please enter Title in Text Box to Save.");
return;
}

MyItem["Title"] = textBox1.Text;
MyItem["CustomField"] = textBox1.Text;
MyItem.Update();

context.Load(MyItem);
context.ExecuteQuery();
MessageBox.Show("Item Created Successfully with ID :" + MyItem.Id.ToString());
}

Here is an example of reading User Profile using CSOM.

using (ClientContext context = new ClientContext(new Uri("http://intranet.contoso.com")))
{
Microsoft.SharePoint.Client.UserProfiles.ProfileLoader loader = ProfileLoader.GetProfileLoader(context);

context.Load(loader);

UserProfile profile = loader.GetUserProfile();
context.Load(profile);
context.Load(profile.PersonalSite);
context.ExecuteQuery();

MessageBox.Show(profile.PersonalSite.Url);

}
Another Example of using Search Client OM.
using (ClientContext context = new ClientContext(new Uri("http://intranet.contoso.com/sites/Search/")))

   {

       context.Credentials = System.Net.CredentialCache.DefaultCredentials;

       KeywordQuery keywordQuery = new KeywordQuery(context);

       if (textBox1.Text.Length == 0)

       {

           MessageBox.Show("Please enter Title in Text Box to Save.");

           return;

       }

       keywordQuery.QueryText = textBox1.Text;

 

       SearchExecutor searchExec = new SearchExecutor(context);

       ClientResult<ResultTableCollection> Results = searchExec.ExecuteQuery(keywordQuery);

       context.ExecuteQuery();

 

       if (Results != null)

       {

           if (Results.Value[0].RowCount > 0)

           {

               DataTable dt = new DataTable();

 

               dt.Columns.Add("Title");

               dt.Columns.Add("Path");

               dt.Columns.Add("Write");

               dt.Columns.Add("Author");

               dt.Columns.Add("Rank");

 

               foreach (var Row in Results.Value[0].ResultRows)

               {

                   DataRow dr = dt.Rows.Add();

                   if (Row["Title"] != null)

                   {

                       dr[0] = Row["Title"].ToString();

                   }

                   else

                       dr[0] = Row["AccountName"].ToString();

 

                   dr[1] = Row["Path"].ToString();

                   dr[2] = Row["Write"].ToString();

                   dr[3] = Row["Author"].ToString();

                   dr[4] = Row["Rank"].ToString();

               }

 

 

               dataGridView1.DataSource = dt.DefaultView;

           }

           else

           {

               MessageBox.Show("No Results Found for your Query " + textBox1.Text);

           }

       }

       else

       {

           MessageBox.Show("No Results Found for your Query " + textBox1.Text);

       }

       

   }

Here is an example of adding Page to a Publishing Site using the Publishing Client OM.

 
if (textBox1.Text.Length == 0)

{

MessageBox.Show("Please Enter Page Title...");

this.textBox1.Focus();

return;

}

using (ClientContext context = new ClientContext(new Uri("http://intranet.contoso.com/sites/Pub")))

{

Web webSite = context.Web;

context.Load(webSite);

 

PublishingWeb web = PublishingWeb.GetPublishingWeb(context, webSite);

context.Load(web);

if (web != null)

{

    List PublishingLayouts = context.Site.RootWeb.Lists.GetByTitle("Master Page Gallery");

    ListItemCollection allItems = PublishingLayouts.GetItems(CamlQuery.CreateAllItemsQuery());

    ListItem Layout = allItems.GetById(35);

    context.Load(Layout);

 

    PublishingPageInformation PubPageInfo = new PublishingPageInformation();

    PubPageInfo.Name = textBox1.Text + DateTime.Now.Date.Hour + DateTime.Now.Date.Minute + ".aspx";

    PubPageInfo.PageLayoutListItem = Layout;

 

    PublishingPage page = web.AddPublishingPage(PubPageInfo);

    context.Load(page);

    context.ExecuteQuery();

    

    MessageBox.Show("Success....: ");

 

}

}

Now the same stuff can be done using REST and oData Interface.  I will explain this in another post.