PowerShell: Migrate field choices to a termset
By Anatoly Mironov
In my current project we are migrating a custom sharepoint solution from SharePoint 2010 to SharePoint 2013. One of the improvements is that we migrate custom field choices to Managed Metadata. Choice Fields were not intended to be so many. So now it is time to convert them to metadata terms. I have written a PowerShell script which copies all the choices from a field to a termset. The script uses Client Side Object Model (CSOM) to get the choice values and it uses Server Object Model to write data to the termset. [sourcecode language=“PowerShell”] <# .Synopsis Use MigrateChoices.ps1 to migrate choices from a field to managed metadata. The source can be a contenttype field from SharePoint 2010 or from SharePoint 2013. .Description This script uses client object model to get the list items from the old environment. Then it uses the Server Object Model to create new terms and termsets (if needed) in the new environment. This script doesn’t override anything. The reason why we use it, is that importing feature isnt easy It creates a new, instead of adding new terms to old ones. It is very hard to create an csv file for that purpose. .Example MigrateChoices.ps1 -Source http://sp2010.contoso.com -Target https://sp2013.contoso.com .Notes Name: MigrateChoices.ps1 Author: Anatoly Mironov Last Edit: 2013-09-04 Keywords: CSOM, Field, FieldChoice, Metadata, Termset .Links http://chuvash.eu .Inputs The script takes Source parameter, it is url for the root web in the Source Site Collection The script takes Target parameter, it is url for the root web in the Target Site Collection .Outputs None #Requires -Version 2.0 #> [CmdletBinding()] Param( [Parameter(Mandatory=$true)][System.String]$Source = $(Read-Host -prompt “Source site Url”), [Parameter(Mandatory=$true)][System.String]$Target = $(Read-Host -prompt “Target site Url”), [Parameter(Mandatory=$false)][System.String]$TermGroupName = “My group”, [Parameter(Mandatory=$false)][System.String]$TermGroupId, [Parameter(Mandatory=$false)][System.String]$TermSetName = “My termset”, [Parameter(Mandatory=$false)][System.String]$TermSetId, [Parameter(Mandatory=$false)][System.String]$FieldName = $(Read-Host -prompt “Field Choice Name”) ) if(-not(gsnp | ? { $_.Name -eq “Microsoft.SharePoint.PowerShell”})) { asnp Microsoft.SharePoint.PowerShell } # try to instantiate the target site, if it fails, then it does not run in the right environment $targetSite = get-spsite $Target # predefined termset guids: $guids = @{ “group” = if ($TermGroupId) {New-Object system.guid $TermGroupId } else { [system.guid]::NewGuid() } “termset” = if ($TermSetId) {New-Object system.guid $TermSetId } else { [system.guid]::NewGuid() } } function GetChoices() { $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($Source) $field = $ctx.Web.Fields.GetByInternalNameOrTitle($FieldName) $ctx.Load($field) $ctx.ExecuteQuery() return ([xml]$field.SchemaXml).Field.Choices.Choice } function New-Term { [CmdletBinding()] Param( [Parameter(Mandatory=$true)][Microsoft.SharePoint.Taxonomy.TermSetItem]$parent, [Parameter(Mandatory=$true)][System.String]$name ) if (!$parent) { throw new-object System.ArgumentException $parent } if (!$name) { throw new-object System.ArgumentException $name } $term = $parent[$name] if (!$term) { $parent.CreateTerm($name, 1033) | out-null } } function CreateTerms() { $taxSession = Get-SPTaxonomySession -Site $targetSite $termStore = $taxSession.DefaultSiteCollectionTermStore $group = $termstore.Groups[$TermGroupName] if (!$group) { $group = $termStore.CreateGroup($TermGroupName, $guids.group) } $termSet = $group.TermSets[$TermsetName] if (!$termset) { $termset = $group.CreateTermSet($SecurityClassificationTermSetName, $guids.termset, 1033) } GetChoices | % { New-Term $termSet $_ } $termStore.CommitAll() $targetSite.Dispose() write-host -ForegroundColor Green “The term sets have been created” } CreateTerms [/sourcecode]
Getting choices
When you get a Field in CSOM model you can cast it to FieldChoice in C# (CastTo) and in javascript (castTo). But in PowerShell the syntax for Casting is harder. I got this error when I just tried to cast: [sourcecode language=“PowerShell”] ([Microsoft.SharePoint.Client.FieldChoice] $field).Choices [/sourcecode]
Cannot convert the “Microsoft.SharePoint.Client.Field” value of type “Microsoft.SharePoint.Client.Field” to type “Microsoft.SharePoint.Client.FieldChoice”. At line:1 char:1 + ([Microsoft.SharePoint.Client.FieldChoice] $field).Choices + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidArgument: (:) [], RuntimeException + FullyQualifiedErrorId : ConvertToFinalInvalidCastException
But what we need is actually SchemaXml. SchemaXml contains all the choices and is an xml document. PowerShell is awesome to handle xml: [sourcecode language=“PowerShell”] ([xml]$field.SchemaXml).Field.Choices.Choice [/sourcecode]
Resources
PowerShell is as good environment for CSOM as it is with pure C# applications. But there are not so many code samples on the internet. I found these blog posts useful while writing this script:
- Client Object Model
- Csom
- Managed Metadata
- Powershell
- Sharepoint
- SharePoint 2010
- Sharepoint 2013
- Term
- Termset