Scripting SharePoint Online with PowerShell using Client Object Model

June 7th, 2012 | Posted by Jeffrey Paarhuis in Configuring | SharePoint Talk

EDIT: THIS PROJECT IS CONTINUED ON CODEPLEX!

Due to the success of this post I continued this as a project on CodePlex: https://sharepointpowershell.codeplex.com

 

But if you still want to continue reading this post, go ahead.

How it works

Download this file and extract the DLL to an easy location to reach from PowerShell. Because this assembly uses the Client Object Model you need to install the COM Redistributable as well.

These are the PowerShell commands to run:

You need to run PowerShell in single-threaded mode (the authentication requires it).

powershell -STA

Import the DLL.

[Reflection.Assembly]::LoadFile("D:\ClaimsAuth.dll")

Instantiate a new SPOContext providing your SharePoint Online site URL. (Don’t forget the https)

$spoContext = new-object SPOContext("https://jeffreypaarhuis.sharepoint.com")

Now, let’s test it:

$spoContext = new-object SPOContext("https://jeffreypaarhuis.sharepoint.com")
$web = $spoContext.Web
$spoContext.Load($web)
$spoContext.ExecuteQuery()
Write-Host $web.Title

As you already might understand, this solution is based on the SharePoint Client Object Model. This means that everything that’s possible with the COM is possible to script. This post explains how to work with Sites, Permissions, Documents, etc. with the COM. It is written in C# but it’s fairly easy to translate to PowerShell.

Samples

-------------------
Init spoContext
-------------------

powershell -sta
[Reflection.Assembly]::LoadFile("D:\ClaimsAuth.dll")
$spoContext = New-Object SPOContext("https://mysharepointonline.sharepoint.com")

-------------------
Print sitename
-------------------

$web = $spoContext.Web
$spoContext.Load($web)
$spoContext.ExecuteQuery()
$web.Title

-------------------
Add property to bag
-------------------

$web.AllProperties.FieldValues.Add("propA","Property A")
$spoContext.ExecuteQuery()

$web.AllProperties.Item("propA")
-------------------
Show features
-------------------

$site = $spoContext.Site
$spoContext.Load($site)
$spoContext.ExecuteQuery()
$features = $site.Features
$spoContext.Load($features)
$spoContext.ExecuteQuery()

$features
-------------------
Permissions stuff
-------------------

Function GetWeb
{
 $ctx.ExecuteQuery()
 Return $ctx.Web
}

Function GetList ($name)
{
 $web = GetWeb
 if ($web -ne $null)
 {
 $lists = $web.Lists
 $ctx.Load($lists)
 $ctx.ExecuteQuery()
 $list = $lists | where {$_.Title -eq $name}
 return $list
 }
 return $null
}

Function GetRole ($rType)
{
 $web = GetWeb
 if ($web -ne $null)
 {
 $roleDefs = $web.RoleDefinitions
 $ctx.Load($roleDefs)
 $ctx.ExecuteQuery()
 $roleDef = $roleDefs | where {$_.RoleTypeKind -eq $rType}
 return $roleDef
 }
 return $null
}

Function GetPrincipal ($name)
{
 $web = GetWeb
 if ($web -ne $null)
 {
 $principal = $web.EnsureUser($name)
 $ctx.Load($principal)
 $ctx.ExecuteQuery()
 return $principal
 }
 return $null
}

Function GetGroup ($name)
{
 $web = GetWeb
 if ($web -ne $null)
 {
 $groups = $web.SiteGroups
 $ctx.Load($groups)
 $ctx.ExecuteQuery()
 $group = $groups | where {$_.Title -eq $name}

 return $group
 }
 return $null
}

Function GetDocumentLibrary ($name)
{
 $web = GetWeb
 if ($web -ne $null)
 {
 $docLibs = $web.Lists
 $ctx.Load($docLibs)
 $ctx.ExecuteQuery()
 $docLib = $docLibs | where {$_.Title -eq $name}
 return $docLib
 }
 return $null
}

$web = GetWeb

$web.BreakRoleInheritance($true, $false);
$principal = GetGroup "MyGroup"
$roleType = [Microsoft.SharePoint.Client.RoleType]"Contributor"
$role = GetRole $roleType

$collRdb = new-object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($ctx)
$collRdb.Add($role)
$collRoleAssign = $web.RoleAssignments
$collRoleAssign.Add($principal, $collRdb)

$ctx.ExecuteQuery()

$list = GetList "Shared Documents"

$list.BreakRoleInheritance($false, $false);
$roleType = [Microsoft.SharePoint.Client.RoleType]"Reader"
$role = GetRole $roleType

$collRdb = new-object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($ctx)
$collRdb.Add($role)
$collRoleAssign = $list.RoleAssignments
$collRoleAssign.Add($principal, $collRdb)

$ctx.ExecuteQuery()

Errors?

You might see the following errors from time to time, which aren’t a big deal:

– “The requested site does not appear to have claims enabled or the Login Url has not been set.”.
Problem: This usually means that no session can be instantiated (SharePoint Online bug).
Solution: Navigate to your site in Internet Explorer, when your site doesn’t show, refresh it a few times until it shows, or go to https://portal.microsoftonline.com. When you get a login page, go back to your script.

– “The remote name could not be resolved: ‘mysp.sharepoint.com'”
Problem: During scripting, the context timed out.
Solution: Re-instantiate the $spoContext object. When running a script in one go, this error shouldn’t pop because the Context will not prematurely expire.

Under the hood

How does it work under the hood? What’s in the DLL? What makes SharePoint Online so difficult?

In line with this one I’ve written a post that explains what the problem with SharePoint Online is and how to build your own DLL. Read it here.

UPDATE (11-7-2012):

Forgot to mention you need to install the COM Redistributable.

UPDATE (3-9-2012):

Added samples.

You can follow any responses to this entry through the RSS 2.0 You can leave a response, or trackback.

39 Responses

  • Pingback: PowerShell for SharePoint Online: Under the hood | Jeffrey Paarhuis SharePoint Blog

  • Alan says:

    Hi there,

    I’m trying to follow these instructions and I am getting the following error:
    New-Object : Cannot find type [SPOContext]: make sure the assembly containing this type is loaded.

    Even though it claims the DLL is loaded:

    GAC Version Location
    — ——- ——–
    False v2.0.50727 c:\ClaimsAuth.dll

    I am have tried running PS as Admin or normal user and also tried the -STA mode.

    Any ideas as to what I am doing wrong?

  • Pingback: What is PowerShell and why should I use it?

  • hobelinm says:

    Thanks! Awesome article!
    Is it possible to automate the credential login that appears when instantiating a new SPOContext, say passing a PSCredential?

    • The CSOM needs a session that can only be instantiated via the HTML form of Office 365. That’s why you’re getting the popup. However, I think it might be possible with some kind of JavaScript insertion or a scripted https/html poster in powershell (don’t know if it exists). Either way, there’s no neat solution to this. But if you got it working, please let me know, cause it’s always one less step to take.

  • I always get the error with loading the ClaimsAuth.dll:

    Exception calling “LoadFile” with “1” argument(s): “An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the assembly, please enable the loadFromRemoteSources switch. See http://go.microsoft.com/fwlink/?LinkId=155569 for
    more information.”
    At C:\Scripts\Deployment\Deploy.ps1:XX char:XX
    + $assembly = [Reflection.Assembly]::LoadFile <<<< ($file); + CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : DotNetMethodException

    I already tried to create a new powershell.exe.config file. What am i doin wrong?

    Thx in advance…

  • Sylvain says:

    Hi !

    first of all.. awesome article !

    I use this trick a lot.

    However, there is something I can’t get done. I don’t know if it’s feasible or not.

    When I retrieve a SPWeb with the Load method of the client object model, not all properties are returned. By example “HasUniqueRoleAssignments” is missing. This is the normal behaviour.
    I know there is a way using c# or javascript
    http://msdn.microsoft.com/en-us/library/ee534974(v=office.14).aspx

    but I can’t manage to convert it to PowerShell…

    Have you heard about this issue ?

    Thanks

    • Thanks!

      About the missing properties, you can make it possible. Have you looked at my “under the hood” post? Here is it: http://jeffreypaarhuis.com/2012/06/14/powershell-for-sharepoint-online-under-the-hood/

      You can build your own DLL and write a function in the SPOContext class that retrieves your missing properties.

      Please let me know if you need any help.

      • Sylvain says:

        Thanks for your reply ! I will try your technique pretty soon.

      • Sylvain says:

        I just tested it and… it worked 🙂 !
        I took the liberty to post the code here because it took me some time to figure out how to do this.

        Here is an example of Load function that can be used to retrieve “HasUniqueRoleAssignments” property.

        I put it in SPOContext class and could use it in powershell instead of the traditionnal Load method.

        public void LoadSPWebWithBreakRoleInheritance(ClientObject objectToLoad)
        {
        Web webToRetrieve = (Web) objectToLoad;
        base.Load(webToRetrieve, website => website.Title, website => website.HasUniqueRoleAssignments);
        }

        The “website => website.Title,” part is useless because the Title is retrieved by default, but you can retrieve other properties instead.

        Again thanks for sharing your discoveries.

  • Christian says:

    Hi!
    We’re using Sharepoint Online with AD Integration, and if i try to authenticate, I get an http error 401:
    1 first broweser call is working korrektly, but if i enter my username
    2 press tap
    3 got the message that I’ll be redirected to loginpage of my organisation
    4 got the http error 401

  • Chris says:

    Hi,

    Superb and very helpful article, though I have got myself stuck in a little problem.

    I am trying to get sites created within the site collection using this method.
    I am presuming it is possible since client object model supports it.

    Using your examples I have the following code working:

    [Reflection.Assembly]::LoadFile(“D:\ClaimsAuth.dll”)
    $spoContext = new-object SPOContext(“https://xxx.sharepoint.com/sites/Main”)
    $web = $spoContext.Web
    $spoContext.Load($web)
    $spoContext.ExecuteQuery()

    $web = $spoContext.Web
    $spoContext.Load($web)
    $spoContext.ExecuteQuery()

    $webs = $web.Webs
    $spoContext.Load($webs)
    $spoContext.ExecuteQuery()

    I have a class (WCInformation) which has a method (GetCreationInfo) that returns a WebCreationInformation object which is valid and is being used as:

    $wcInfo = new-object WCInformation
    $testSite = $wcInfo.GetCreationInfo(“test”)

    $webs.Add($testSite)

    The final line is causing the issue.
    webs is not null but is returning:

    format-default : The collection has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested.

    Any help or wisdom would be much appreciated.

    Many thanks
    Chris

    • If I were you, I would find the C# or VB method for this and then translate it to PowerShell. I believe there are some examples on the MSDN. Let me know if you can get it working.

      • Chris says:

        Hi Jeffrey,
        Thanks for your reply.
        We currently use C# to create sites on feature activation.
        The code used is:
        rootWeb.Webs.Add(url, siteName, siteDesc, Convert.ToUInt16(1033), siteTemplate, false, false);
        Unfortunately, as the requirement is SharePoint Online, we get awful timeouts and have to split the site creation into multiple manually activated features.
        Could the above code be used as I believe that the client object model would be the only way forward with PowerShell.

        Many thanks for your time
        Chris

        • Well, if the timeouts are the problem, then I might have a workaround for that because I’ve faced the same limitation with SharePoint online.

          What I’ve used is JavaScript on the page created that does the stuff you normally would use the feature receiver for. You can use Sharepoint ECMA-script for that.

  • Have you an sample with download file from document library?

  • John Donnelly says:

    Beginner here so real quick: How is the variable $ctx set? Is it the same as $spoContext?

    thanks for this though – great stuff

  • Pingback: Using Powershell and CSOM to remotely manager Office365 | asksven

  • Hi,

    A massive thanks for this page. It helped me a lot but….

    I’m getting an error for the roledefinationcollection which is “new-object : Cannot find an overload for “RoleDefinitionBindingCollection” and the argument count: “1”.
    At C:\scripts\SharepointScripts\authPerm.ps1:115 char:12
    + $collRdb = new-object Microsoft.SharePoint.Client.RoleDefinitionBindingCollectio …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [New-Object], MethodException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand”

    Can you tell me what happening?

    Thanks
    Mark

  • Mark Playford says:

    Hi. I was wondering if you could help me. I’ve been trying to use you and adapt it for my need. I simply want to add a group to the library and web but I get an error, I think with the principal part. I’ve posted over at stack overflow and wondered if you could have a look.

    http://stackoverflow.com/questions/21767514/powershell-csom-sharepoint-online-list-permission

    thanks
    Mark

  • Andrew says:

    Hello,

    i followed your instructions, but it doesn’t seem to work.
    First of all, i get the “False” Message from the DLL:
    GAC Version Location
    — ——- ——–
    False v2.0.50727 c:\ClaimsAuth.dll
    Doesn’t matter if i try running Powershell as admin, use powershell -STA or using Powershell for SharePoint online.

    I am running from a 64 bit environment, the COM Redistributable are installed too.

    The connection with SharePoint Online works, but if i want to add properties – like your Add Property to a bag sample:
    $web.AllProperties.FieldValues.Add(“propA”,”Property A”)
    $spoContext.ExecuteQuery()

    $web.AllProperties.Item(“propA”)

    there is no saving of the changes. Powershell shows the added properties in the first run (it is in the cache i assume) but when i open a new shell and want to log in again, the properties are no longer found.

    Do you have maybe any hint, what the problem could be?
    Possibly the “False” with the DLL would be my guess, but how can i change this?

    Thanks in advance for your help,
    Andrew



Leave a Reply to Jaap Cancel reply

%d bloggers like this: