Categories
Configuring SharePoint Talk

Scripting SharePoint Online with PowerShell using Client Object Model

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).

[code gutter=”false” lang=”powershell”]powershell -STA[/code]

Import the DLL.

[code gutter=”false” lang=”powershell”][Reflection.Assembly]::LoadFile("D:\ClaimsAuth.dll")[/code]

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

[code gutter=”false” lang=”powershell”]$spoContext = new-object SPOContext("https://jeffreypaarhuis.sharepoint.com")[/code]

Now, let’s test it:

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

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

[code lang=”powershell” gutter=”false”]

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

[/code]

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.

40 replies on “Scripting SharePoint Online with PowerShell using Client Object Model”

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?

I get the same error. Did you change the version of the DLL? The Version number is coming up the same

PS C:\> .\get-sharepointstats.ps1

GAC Version Location
— ——- ——–
False v2.0.50727 c:\ClaimsAuth.dll
New-Object : Cannot find type [SPOContext]: make sure the assembly containing this type is loaded.

Hi,

i get the same error, tested on 32 bit machine with the 32 bit dll, and the 64 bit dll on a 64 bit machine.
The DLL claims to load ok..
I would be SO great if i can access our sharepoint through PS….

PS C:\Users\Administrator> $spoContext = new-object SPOContext(“https://******.sharepoint.com”)
New-Object : Cannot find type [SPOContext]: make sure the assembly containing this type is loaded.

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…

I also encountered this issue! But unblocking it did the trick! Thanks, Jeffrey!

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

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.

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

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.

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.

I’m sorry, the link I posted doesn’t work for sharepoint online. But I need this feature too for my script, working on it.. if you already solved the problem don’t hesitate to post it 😉

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

thanks for this though – great stuff

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

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

This is great stuff..do you know i can get all the items in a library

when i try to do
$list = $SPOcontext.Web.Lists.GetByTitle(“Documents”)
$query = [Microsoft.SharePoint.Client.CamlQuery]::CreateAllItemsQuery(10000, ‘UniqueId’,’ID’,’Created’,’Modified’,’FileLeafRef’,’Title’)

i get an error

Cannot convert argument “query_”, with value: “Microsoft.SharePoint.Client.CamlQuery”, for “GetItems” to type “Microsoft.SharePoint.Client.CamlQuery”: “Cannot convert the
“Microsoft.SharePoint.Client.CamlQuery” value of type “Microsoft.SharePoint.Client.CamlQuery” to type “Microsoft.SharePoint.Client.CamlQuery”.”

any ideas will be much appreciated

Leave a Reply to Andrew Cancel reply