Create a Communication Site from code in SharePoint Online using PowerShell

Lately Microsoft released the new  and long awaited modern site template for publishing sites in SharePoint Online (Office 365). The site templates is named “Communication Site” and is the second template released after the modern “Team Site”. This site template can be created if you have permissions from the SharePoint Home page using the “Create a site” form:

cpcomsite1

This approach is fine if you have permissions to create sites and are happy with the default setup. For my case this is almost never the case. Often the tenants I work with have restricted who can create new sites/groups, and also requires additional configuration after it has been created. This can be accomplished for the new “Team site” template, so I was curious how this can be done for the Communication site.

At the time of writing Microsoft have not released any documentation on how to do this, so this was done by re-creating the steps of the “Create a site” wizard (found by using Fiddler). I assume and hope that we sooner or later will find official documentation from Microsoft on how to do this. They might provide us with a bit more elegant way to use their API’s. But if you are familiar with using general REST API’s, this is pretty straight forward as soon you get authenticated with SharePoint.

UPDATE (30.10.2017): This documentation have now been released, and this method is now a supported approach to provision new Communication Sites.

PowerShell script for creating a Communication site

This script requires the “SharePoint Online Client Components” to be installed.

# Configuration
$TenantUrl = "https://contoso.sharepoint.com"
$Username = "danj@contoso.com"
$Password = "Password1"
$SiteTitle = "My Communication Site 1"
$SiteUrl = "https://contoso.sharepoint.com/sites/mycomsite1" # Note this URL must be available (check with "/_api/GroupSiteManager/GetValidSiteUrlFromAlias")
$SiteTemplate = "" # "Topic" => leave empty (default), "Showcase" => "6142d2a0-63a5-4ba0-aede-d9fefca2c767" and "Blank" => "f6cc5403-0d63-442e-96c0-285923709ffc"
# Communication site creation request
$RequestBody = '{"request":{"__metadata":{"type":"SP.Publishing.PublishingSiteCreationRequest"},"Title":"' + $SiteTitle + '","Url":"' + $SiteUrl + '","Description":"","Classification":"","SiteDesignId":"' + $SiteTemplate + '","AllowFileSharingForGuestUsers":false}}'
if ($SiteTemplate -eq "")
{
$RequestBody = '{"request":{"__metadata":{"type":"SP.Publishing.PublishingSiteCreationRequest"},"Title":"' + $SiteTitle + '","Url":"' + $SiteUrl + '","Description":"","Classification":"","AllowFileSharingForGuestUsers":false}}'
}
# Get a user based context for SharePoint (app credentials not supported for this approach)
$Context = New-Object Microsoft.SharePoint.Client.ClientContext($TenantUrl)
$Context.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Username, $(ConvertTo-SecureString AsPlainText $Password Force))
$Context.ExecuteQuery()
# Get url, cookie and forms digest for authentication
$RequestUrl = "$($TenantUrl)/_api/sitepages/publishingsite/create"
$AuthenticationCookie = $Context.Credentials.GetAuthenticationCookie($TenantUrl, $true)
$FormsDigest = $Context.GetFormDigestDirect()
$WebSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$WebSession.Credentials = $Context.Credentials
$WebSession.Cookies.SetCookies($TenantUrl, $AuthenticationCookie)
$Headers = @{
'X-RequestDigest' = $FormsDigest.DigestValue;
'accept' = 'application/json;odata=verbose';
'content-type' = 'application/json;odata=verbose' }
# Call REST API to create new site
$Result = Invoke-RestMethod Method Post WebSession $WebSession Headers $Headers ContentType "application/json;odata=verbose;charset=utf-8" Body $RequestBody Uri $RequestUrl UseDefaultCredentials
$Context.Dispose()
# Site has been created
Write-Output "New site created at: $($Result.d.Create.SiteUrl)"

Summary

With the modern sites Microsoft has created several new REST based API’s used in their own wizards. These API’s can be used as long as you have a user authenticated context.

This code demonstrates how you can create the new Communication Site from code. After the site have been created you are free to connect to the site and apply customizations using in example the PnP PowerShell framework.

Problem with connecting to SharePoint Online in Office 365 with PowerShell, SharePoint Designer and other 3. party tools

You are not any longer able to log into SharePoint using PowerShell, SharePoint Designer and other 3. party tools (ex. ShareGate, SharePoint Search Query Tool etc). The error message states something that you are “Unauthorized” and “…the web site does not support SharePoint Online credentials” even though you username and password is fine.

Example: Error while logging in with PnP PowerShell

Connect-PnPOnline : Cannot contact web site ‘https://TENANTID.sharepoint.com/‘ or the web site does not support SharePoint Online credentials. The response status code is ‘Unauthorized’.  The response headers are ‘X-SharePointHealthScore=0, X-MSDAVEXT_Error=917656; Access+denied.+Before+opening+files+in+this+location%2c+you+must+first+browse+to+the+web+site+and+select+the+option+to+login+automatically.,

Example: Stuck while logging in with SharePoint Designer

Reason

SharePoint Online has a setting named “LegacyAuthProtocolsEnabled” with the purpose “Prevents Office clients using non-modern authentication protocols from accessing SharePoint Online resources .”.

By default this is allowed in all tenants. But as an administrator it is possible to tighten up the security and disallow us to login with these non-modern approaches.

More details can be found here: https://technet.microsoft.com/en-in/library/fp161390.aspx

Solution

1. Start using modern authentication (recommended)

Check if your application support the use of modern authentication through either WebLogin or using application credentials (ClientId/ClientSecret) authentication. This is advice to be the recommended and a more secure approach.

Not all tools, like SharePoint Designer, supports this modern authentication and if you are required to continue to use these apps further on, you might have to re-enable the support as described in the next point.

2. Re-enable support for legacy apps (temporary fix)

Note: Your company might have performed a security hardening and disabled this by purpose. If so it would not be advisable to continue without verifying the reason for this change.

Using “SharePoint Online Management Shell” login in with “Connect-SPOService”.


Connect-SPOService -Url "https://TENANTID-admin.sharepoint.com"

Verify the value of “LegacyAuthProtocolsEnabled”.


$TenantSettings = Get-SPOTenant

$TenantSettings.LegacyAuthProtocolsEnabled

If this value is “False”, then this issue will be solved by setting this to “True”.


Set-SPOTenant -LegacyAuthProtocolsEnabled $True

Updating you SharePoint Online tenant settings does not take immediate effect. So you need to while a while, exact how long can be from from minutes to 24 hours with the different settings, before you retry.

Summary

Changing the value of “LegacyAuthProtocolsEnabled” can cause issues for some existing applications. Checking if you can start using more modern authentication options will solve the issue in many apps, but for  some you might still need to keep this support open.

Create list from custom template in SharePoint with PowerShell

This examples shows how to create new lists from custom list templates in SharePoint using PowerShell and CSOM. Custom list templates are STP-files uploaded to the List Template Gallery on the site. This should work both on-premises (2013/2016) and Online (Office 365).

#region Configuration
$Url = "https://TENANTID.sharepoint.com" # URL of the site
$ListTemplateInternalName = "CorpMenu.stp" # Change this with your own list template
$ListName = "Menu" # Change this with the name of the list to be created
#endregion
#region Create list from template
Connect-PnPOnline Url $Url
$Context = Get-PnPContext
$Web = $Context.Site.RootWeb
$ListTemplates = $Context.Site.GetCustomListTemplates($Web)
$Context.Load($Web)
$Context.Load($ListTemplates)
ExecutePnPQuery
$ListTemplate = $ListTemplates | where { $_.InternalName -eq $ListTemplateInternalName }
if ($ListTemplate -eq $null)
{
Throw [System.Exception] "Template not found"
}
$ListCreation = New-Object Microsoft.SharePoint.Client.ListCreationInformation
$ListCreation.Title = $ListName
$ListCreation.ListTemplate = $ListTemplate
$Web.Lists.Add($ListCreation)
ExecutePnPQuery
Disconnect-PnPOnline
#endregion

Note: This example uses commands from the Office Dev PnP PowerShell library, ex. “Connect-PnPContext”. I recommend using this library for working this PowerShell and SharePoint (both Online and On-Premises).

Enable ratings in SharePoint with PowerShell and CSOM

In document libraries it is possible to enable ratings, both likes and average score (1-5). In this article I will cover how to enable “Likes” on the Pages library in a publishing site.

When configuring libraries manually, this is enabled from the “Rating settings” in the library:

enableratings

But when we follow the remote provisioning pattern to create new sites by using PowerShell and CSOM, there is currently no available function in the API for this. This pattern is the recommenced approach when working with SharePoint Online or SharePoint 2013/2016 without access to server side PowerShell or code.

To understand what is required to programmatic enable ratings I had to, as always with SharePoint, inspect the code behind with a reflection tool like DotPeek.

How to enable likes ratings

This show the procedure to enable ratings with Likes on a Pages library. To enable “Star rating” (1-5 average) this would be similar, but not covered in this post.

  1. Add the required fields to the library
    FieldName Guid
    AverageRating 5a14d1ab-1513-48c7-97b3-657a5ba6c742
    RatingCount b1996002-9167-45e5-a4df-b2c41c6723c7
    RatedBy 4D64B067-08C3-43DC-A87B-8B8E01673313
    Ratings 434F51FB-FFD2-4A0E-A03B-CA3131AC67BA
    LikesCount 6E4D832B-F610-41a8-B3E0-239608EFDA41
    LikedBy 2CDCD5EB-846D-4f4d-9AAF-73E8E73C7312
  2. Add “LikesCount” to default view
  3.  Add “Ratings_VotingExperience” to RootFolder’s property bag

Use PowerShell to enable ratings

Note: This function requires the Office Dev PnP PowerShell library to be installed and loaded in the current PowerShell session. I recommend using this library for working with PowerShell and SharePoint (both Online and On-Premises).

function Enable-CustomLikesRatingsOnLibrary {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[Microsoft.SharePoint.Client.Web]$Web,
[Parameter(Mandatory=$true)]
[String]$ListRelativeUrl
)
# Get required fields
$AverageRatingField = $Web.Fields.GetById([guid]"5a14d1ab-1513-48c7-97b3-657a5ba6c742") # AverageRating
$RatingCountField = $Web.Fields.GetById([guid]"b1996002-9167-45e5-a4df-b2c41c6723c7") # RatingCount
$RatedByField = $Web.Fields.GetById([guid]"4D64B067-08C3-43DC-A87B-8B8E01673313") # RatedBy
$RatingsField = $Web.Fields.GetById([guid]"434F51FB-FFD2-4A0E-A03B-CA3131AC67BA") # Ratings
$LikesCountField = $Web.Fields.GetById([guid]"6E4D832B-F610-41a8-B3E0-239608EFDA41") # LikesCount
$LikedByField = $Web.Fields.GetById([guid]"2CDCD5EB-846D-4f4d-9AAF-73E8E73C7312") # LikedBy
$Context.Load($AverageRatingField)
$Context.Load($RatingCountField)
$Context.Load($RatedByField)
$Context.Load($RatingsField)
$Context.Load($LikesCountField)
$Context.Load($LikedByField)
ExecuteSPOQuery
# Get the library for the site
$ServerRelativeUrl = $Web.ServerRelativeUrl
if ($ServerRelativeUrl -ne "/")
{
$ServerRelativeUrl = $ServerRelativeUrl + "/"
}
$List = $Web.GetList($ServerRelativeUrl + $ListRelativeUrl)
$ListRootFolder = $List.RootFolder
$ListRootFolderProperties = $ListRootFolder.Properties
$Context.Load($List)
$Context.Load($ListRootFolder)
$Context.Load($ListRootFolderProperties)
ExecuteSPOQuery
# Add fields
$List.Fields.Add($AverageRatingField)
$List.Fields.Add($RatingCountField)
$List.Fields.Add($RatedByField)
$List.Fields.Add($RatingsField)
$List.Fields.Add($LikesCountField)
$List.Fields.Add($LikedByField)
$List.Update()
ExecuteSPOQuery
# Update default view
$ListDefaltView = $List.DefaultView
$Context.Load($ListDefaltView)
ExecuteSPOQuery
$ListDefaltView.ViewFields.Add("LikesCount")
$ListDefaltView.Update()
ExecuteSPOQuery
# Update property bag
$ListRootFolderProperties["Ratings_VotingExperience"] = "Likes"
$ListRootFolder.Update()
ExecuteSPOQuery
}

Example: How to use this function with a site

# Connect to SharePoint Online site collection and get context
Connect-SPOnline Url "https://TENANTID.sharepoint.com/sites/news
$Context = Get-SPOContext
$Web = $Context.Site.RootWeb
$Context.Load($Web)
Execute-SPOQuery
# Enable likes ratings
Enable-CustomLikesRatingsOnLibrary -Web $Web -ListRelativeUrl "Pages"
# Close connection
Disconnect-SPOnline

Using this with subsites within the Site Collection would require some extension to the function, as the Fields always are loaded form the RootWeb, but the library reside in the subsite.

When we have run the “Enable-CustomLikesRatingsOnLibrary” function on the desired library, we can see that Likes now are available:

ratings-library

Summary

Using this function it is now possible to provision new sites with PowerShell and CSOM and enable likes rating on libraries.

Add AD security group as Site Collection administrator with PowerShell in SharePoint

In provisioning solutions it can be a good practice to add a security group from Active Directory as an additional site collection owner. Unfortunately a security group is not accepted as either primary or secondary owner. So to set a group as site collection administrator, the easiest approach I could find was to set the “IsSiteAdmin” property on the User object.

# Configuration
$WebUrl = "http://teams.contoso.com/sites/team1"
$SiteAdminGroup = "CONTOSO\ContosoSiteAdmins"
# Set the AD group as site collection administrator
$Web = Get-SPWeb $WebUrl
$SiteAdminUser = $Web.EnsureUser($SiteAdminGroup)
$SiteAdminUser.IsSiteAdmin = $true
$SiteAdminUser.Update()

Enabling scheduling on Publishing Pages in SharePoint Online (Office 365) using CSOM and PowerShell

Scheduling is easily enabled through the web interface when configuring the “Pages” library, but when deploying solutions using PowerShell, this must be automated as part of the configuration. Unfortunately, as many other sources also state, this is not directly supported in the Client Side API (CSOM).

The solution is to manually set up the Pages library the same way Microsoft does by adding two event receivers, changing some columns from hidden to visible and adding them to the default view.

Note: This example uses commands from the Office Dev PnP PowerShell library, ex. “Get-SPOContext”. I recommend using this library for working this PowerShell and SharePoint (both Online and On-Premises).

Function to enable Scheduling on a library on a given web site:

function Enable-CustomItemScheduling {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[Microsoft.SharePoint.Client.Web]$Web,
[Parameter(Mandatory=$true)]
[String]$PagesLibraryName
)
$List = $Web.Lists.GetByTitle($PagesLibraryName)
# Get function from: https://gist.github.com/aflyen/4a098b69b9faa43fd1a3
$ListParameter = Get-CustomLoadParameter Object $List PropertyName "EventReceivers"
$Web.Context.Load($List, $ListParameter)
ExecuteSPOQuery
# Prerequisites for using scheduling
$List.EnableModeration = $true
$List.EnableMinorVersions = $true
$List.Update()
$Assembly = "Microsoft.SharePoint.Publishing, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
$FullName = "Microsoft.SharePoint.Publishing.Internal.ScheduledItemEventReceiver"
$EventReceiverItemAddedExists = $false
$EventReceiverItemAddedType = [Microsoft.SharePoint.Client.EventReceiverType]::ItemAdded
$EventReceiverItemAddedName = "Item Added Event Handler For Scheduling"
$EventReceiverItemUpdatingExists = $false
$EventReceiverItemUpdatingType = [Microsoft.SharePoint.Client.EventReceiverType]::ItemUpdating
$EventRecieverItemUpdatingName = "Item Updating Event Handler For Scheduling"
# Check if the event receivers already exists
foreach ($EventReceiver in $List.EventReceivers)
{
if ($EventReceiver.ReceiverName -eq $EventReceiverItemAddedName)
{
$EventReceiverItemAddedExists = $true
}
elseif ($EventReceiver.ReceiverName -eq $EventRecieverItemUpdatingName)
{
$EventReceiverItemUpdatingExists = $true
}
}
# Add event receiver for ItemAdded
if ($EventReceiverItemAddedExists -ne $true)
{
$EventReceiverItemAdded = New-Object Microsoft.SharePoint.Client.EventReceiverDefinitionCreationInformation
$EventReceiverItemAdded.EventType = $EventReceiverItemAddedType
$EventReceiverItemAdded.ReceiverName = $EventReceiverItemAddedName
$EventReceiverItemAdded.ReceiverClass = $FullName
$EventReceiverItemAdded.ReceiverAssembly = $Assembly
$List.EventReceivers.Add($EventReceiverItemAdded) | Out-Null
}
# Add event receiver for ItemUpdating
if ($EventReceiverItemUpdatingExists -ne $true)
{
$EventReceiverItemUpdating = New-Object Microsoft.SharePoint.Client.EventReceiverDefinitionCreationInformation
$EventReceiverItemUpdating.EventType = $EventReceiverItemUpdatingType
$EventReceiverItemUpdating.ReceiverName = $EventRecieverItemUpdatingName
$EventReceiverItemUpdating.ReceiverClass = $FullName
$EventReceiverItemUpdating.ReceiverAssembly = $Assembly
$List.EventReceivers.Add($EventReceiverItemUpdating)| Out-Null
}
# Make fields for start and end date visible and add them to the default view
if ($EventReceiverItemAddedExists -ne $true -or $EventReceiverItemUpdatingExists -ne $true)
{
$FieldPublishingStartDateName = "PublishingStartDate"
$FieldPublishingExpirationDateName = "PublishingExpirationDate"
$FieldPublishingStartDate = $List.Fields.GetByInternalNameOrTitle($FieldPublishingStartDateName)
$FieldPublishingStartDate.Hidden = $false
$FieldPublishingStartDate.Update()
$FieldPublishingExpirationDate = $List.Fields.GetByInternalNameOrTitle($FieldPublishingExpirationDateName)
$FieldPublishingExpirationDate.Hidden = $false
$FieldPublishingExpirationDate.Update()
$ListDefaultView = $List.DefaultView
$ListDefaultView.ViewFields.Add($FieldPublishingStartDateName)
$ListDefaultView.ViewFields.Add($FieldPublishingExpirationDateName)
$ListDefaultView.Update()
$List.Update()
}
ExecuteSPOQuery
}

On line 11 an additional function is needed to create the parameters for the “Load” method:

function Get-CustomLoadParameter {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[Microsoft.SharePoint.Client.ClientObject]$Object,
[Parameter(Mandatory=$true)]
[string]$PropertyName
)
# Reference: http://sharepoint.stackexchange.com/questions/126221/spo-retrieve-hasuniqueroleassignements-property-using-powershell
$Context = $Object.Context
$Load = [Microsoft.SharePoint.Client.ClientContext].GetMethod("Load")
$Type = $Object.GetType()
$ClientLoad = $Load.MakeGenericMethod($Type)
$Parameter = [System.Linq.Expressions.Expression]::Parameter(($Type), $Type.Name)
$Expression = [System.Linq.Expressions.Expression]::Lambda(
[System.Linq.Expressions.Expression]::Convert(
[System.Linq.Expressions.Expression]::PropertyOrField($Parameter,$PropertyName),
[System.Object]
),
$($Parameter)
)
$ExpressionArray = [System.Array]::CreateInstance($Expression.GetType(), 1)
$ExpressionArray.SetValue($Expression, 0)
return $ExpressionArray
}

view raw
GetLoadParameter.ps1
hosted with ❤ by GitHub

How to enable scheduling:

$Context = Get-SPOContext
$Web = $Context.Site.RootWeb
$Context.Load($Web)
ExecuteSPOQuery
Enable-CustomItemScheduling Web $Web PagesLibraryName "Pages"

Exporting XsltListViewWebPart in SharePoint 2013

By default the XsltListViewWebPart does not support export from the web user interface. I often build solutions by configuring the web parts in the browers, exporting and adjusting the defintions and then deploying them with PowerShell. With a few lines of PowerShell this capability can also be enabled for the Xslt List View Web Part.

Before enabling the Export Mode on the webpart:

XsltListViewWebPart-before

After running the PowerShell script and enabling the Export mode:

XsltListViewWebPart-after

PowerShell script

# Configuration
$WebUrl = "http://intranet.contoso.com"
$PageUrl = "/Pages/default.aspx"
$WebPartName = "Documents"
# Enables export mode
$Web = Get-SPWeb $WebUrl
$File = $Web.GetFile($Web.ServerRelativeUrl + $PageUrl)
$File.CheckOut()
$Wpm = $File.GetLimitedWebPartManager([System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared)
$WebParts = $Wpm.WebParts
$WebPart = $WebParts | where { $_.Title -eq $WebPartName }
$WebPart.ExportMode = [System.Web.UI.WebControls.WebParts.WebPartExportMode]::All
$Wpm.SaveChanges($WebPart)
$File.CheckIn("", [Microsoft.SharePoint.SPCheckInType]::MajorCheckIn)

Using BCS from PowerShell with SharePoint 2013

Running data migrations with SharePoint 2013 often involves using BCS and PowerShell if you don’t have and third party tools at hand. This can be a bit tricky to set up, but once you are familiar the the BCS configuration, the PowerShell scripts are mostly straight forward.

From my experience there are a few differences when running against BCS lists vs. regular lists. I have now been able to find a good guide for this, so from my own trial and error I wanted to share a few tips.

Get the context straight: Accessing the list gives a weird error

Running this:


$Web = Get-SPWeb "http://mybcsweb/"

$Web.GetList("/Lists/MyBcsDataList")

Gives this error:

format-default : The shim execution failed unexpectedly – Proxy creation failed. Default context not found..    + CategoryInfo          : NotSpecified: (:) [format-default], BdcException   + FullyQualifiedErrorId : Microsoft.BusinessData.Infrastructure.BdcException,Microsoft.PowerShell.Commands.FormatDefaultComman

bcs-powershell-sp2013-1

The fix:


$Site = Get-SPSite "http://mybcsweb"

$ServiceContext = Get-SPServiceContext -Site $Site # Required when working with BCS
$ServiceScope = New-Object Microsoft.SharePoint.SPServiceContextScope $ServiceContext # Required when working with BCS

$Web = $Site.RootWeb
$Web.GetList("/Lists/MyBcsDataList")

A bit more wiring is required to get the context straight for BCS to work.

Make sure you are running with administrator privileges

I use PowerShell ISE when creating and running these scripts. From time to time I accidently forget to start this with Administrator privileges. This can fool you into weird errors like this one. Short minded one can try to start adding the missing assembly references, but the real reason is the missing privileges.

Running this:

$Web = Get-SPWeb "http://mybcsweb/"
$Web.GetList("/Lists/MyBcsDataList")

Gives this error:

format-default : The shim execution failed unexpectedly – Could not load file or assembly ‘System.Data.OracleClient, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089’ or one of its dependencies. Either a required impersonation level was not provided, or the provided impersonation level is invalid. (Exception from HRESULT: 0x80070542).    + CategoryInfo          : NotSpecified: (:) [format-default], BdcException  + FullyQualifiedErrorId : Microsoft.BusinessData.Infrastructure.BdcException,Microsoft.PowerShell.Commands.FormatDefaultCommand

bcs-powershell-sp2013-2

The fix:

Search for “PowerShell ISE”, right-click it and choose “Run as Administrator”.

bcs-powershell-sp2013-3

Include ViewFields in you CAML queries

Always remember to include the fields you are trying to query on in the “<Where>” statement within “<ViewFields>”. This isn’t required on regular lists, but to get any results in return, I experienced I was required to include them querying BCS lists.

Example of CAML query in PowerShell:


$Query= New-Object Microsoft.SharePoint.SPQuery 

$Query.ViewXml =
 "<View>
 <Method Name='Read List'/>
 <Query>
 <Where>
 <And>
 <Eq>
 <FieldRef Name='ProjectID' />
 <Value Type='Text'>100</Value>
 </Eq>
 <Eq>
 <FieldRef Name='Role' />
 <Value Type='Text'>Project Manager</Value>
 </Eq>
 </And>
 </Where>
 </Query>
 <ViewFields>
 <FieldRef Name='ProjectID'/>
 <FieldRef Name='Role'/>
 </ViewFields>
 </View>" 

 $Items = $List.GetItems($Query)

Summary

BCS has been around for a while and isn’t something very existing. But as mention in the introduction, using it for data migrations is very powerful, specially when you don’t have any 3. parts tools around. Doing this from PowerShell is pretty straight forward, but these three issues I experienced can be a headache if you get stuck with them. Hopefully this post can help others get quickly passed them!

Use PowerShell to create a demosite for all the common used Web Templates in SharePoint 2013

In my previous article I provided a list of all the common used web templates in SharePoint 2013. For demo purposes it can be handy to spin up a copy of all these templates and later easy remove them. Using PowerShell we can accomplish this with the snippets provided below. Change the parameters under “Configuration” to match your own environment.

Create new site collections

This snippet will create a new site collection for each of the web templates.


Add-PSSnapin Microsoft.SharePoint.PowerShell

# Configuration
$webApp = "http://intranet.contoso.com"
$managedPath = "sites"
$siteOwner = "contoso\sp_admin"
$languageId = 1033

# Create site collections for demo
Write "Create demo sites"
Start-SPAssignment -Global
New-SPSite "$webApp/$managedPath/demo-team-site" -Name "Team Site" -Template "STS#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-blog" -Name "Blog" -Template "BLOG#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-dev-site" -Name "Developer Site" -Template "DEV#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-project-iste" -Name "Project Site" -Template "PROJECTSITE#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-community-site" -Name "Community Site" -Template "COMMUNITY#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-document-center" -Name "Document Center" -Template "BDR#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-ediscovery-center" -Name "eDiscovery Center" -Template "EDISC#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-records-center" -Name "Records Center" -Template "OFFILE#1" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-bi-center" -Name "Business Intelligence Center" -Template "BICenterSite#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-ent-search-center" -Name "Enterprise Search Center" -Template "SRCHCEN#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-mysite-host" -Name "My Site Host" -Template "SPSMSITEHOST#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-community-portal" -Name "Community Portal" -Template "COMMUNITYPORTAL#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-basic-search-center" -Name "Basic Search Center" -Template "SRCHCENTERLITE#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-visio-process-rep" -Name "Visio Process Repository" -Template "visprus#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-pub-portal" -Name "Publishing Portal" -Template "BLANKINTERNETCONTAINER#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-ent-wiki" -Name "Enterprise Wiki" -Template "ENTERWIKI#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-prod-catalog" -Name "Product Catalog" -Template "PRODUCTCATALOG#0" -OwnerAlias $siteOwner -Language $languageId
Stop-SPAssignment -Global

Clean up and remove all the sites

This snippet will delete all the site collections created with the snippet above.


# Configuration
$webApp = "http://intranet.contoso.com"
$managedPath = "sites"
$siteOwner = "contoso\sp_admin"
$languageId = 1033

# Remove site collections
Remove-SPSite "$webApp/$managedPath/demo-team-site" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-blog" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-dev-site" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-project-site" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-community-site" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-document-center" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-ediscovery-center" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-records-center" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-bi-center" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-ent-search-center" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-mysite-host" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-community-portal" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-basic-search-center" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-visio-process-rep" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-pub-portal" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-ent-wiki" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-prod-catalog" -Confirm:$false

Summary

This article demonstrated how easy we can create a copy of all the common used web templates in SharePoint for demo purposes, and later clean it up.

Remove orphaned tasks from the aggregated task list on MySite in SharePoint 2013

For some reason a deleted task was still visible in the aggregated task list for a end-user. The task itself had been deleted from the source site, so when you clicked the task you got an error message telling you it did not exist anymore. The problem was that SharePoint was unable to remove the task from the aggregated view, so now we had to deal with a ghost task!

Solution

The new task aggregation is performed with the help of the Service Application “Work Management Service”, and the users MySite. When the service has aggregated your tasks, it stores the data itself in a list called “WmaAggregatedList_User”. Since it is a traditional list,  you might think: “This is easy! Just go to the list and delete the task!”. Sorry, but no. This list is only intended as a system list, and nothing we ever should care about, so it’s actually has no available views.

Step-by-step to remove the task

  1. Start “SharePoint Manager 2013” on one server in the farm (download from CodePlex)
  2. Navigate to the correct web application, and locate the users MySite site collection under “/personal” or your preferred managed path.
  3. Expand the structure and locate the list “WmaAggregatorList_User”, and choose to browse the “GridView”. If the user doesn’t have to many tasks, you should now be able to use this to visualy inspect the data.
  4. Locate the column named “TxEditUrl” and verify that it matched the URL of the ghost task. In my case I ignored the “&source=” end of the URL. Make a note of the list item ID for the task (the first column)
  5. Fire up good old “SharePoint 2013 Management Shell” to do some PowerShell magic.
  6. Example how to locate the task and remove it:

# Open the users personal site colletion and retrieve the list
$web = Get-SPWeb "http://mysite/personal/adamb"
$list = $web.Lists["WmaAggregatorList_User"] 

# Get the task with the ID located with SharePoint Manager
$item = $list.GetItemById(1) # NOTE: Use the correct ID here

# Now delete the task
$list.Items.DeleteItemById(1)

Summary

In some very rare cases, you can end up with users having orphaned tasks in their aggregated task list on MySite. I never got a understanding why this happened, but it was possible to remove this and get thing back to normal.