Office 365 - Moving and Merging Managed Metadata Terms using CSOM with PowerShell

I've previously posted about using CSOM to automate the addition and management of Managed Metadata in Office 365:

In this post I'll discuss how CSOM can be used in SharePoint Online to Move or Merge MMS Terms.

Moving a Term

In the example below I have a Group named Products, a Term Set named Entry Level and two categories of Products, Widget x and Widget y. Imagine that one of the products within this Term Set needs to move between the Widget lines of products, for example we may need to move Widget x1000 to the Widget y product line as part of a product re-structure within our company. MMS supports the ability to move a Term between Term Sets, which can be really useful. In my very contrived example I will use CSOM via PowerShell to move Widget x1000 to the Widget y product line - in a real world scenario it would be quicker to do this using the UI however if you have lots of changes to make (as one om my colleagues customers did) it makes sense to automate this!

The script below takes a source term $SourceTermName (Widget x1000) and moves this to a destination $DestTermSetName (Widget y). All you need to do is update the highlighted variables and execute! In the real world you would probably use an import file (CSV perhaps) that contains a list of terms to move and wrap this into a loop to automate the mind-numbing process of performing the move - this is purely a quick sample to get you started.

#Specify tenant admin and URL
$User = "admin@tenant.onmicrosoft.com"
$TenantURL = "https://tenant-admin.sharepoint.com"
$Site = "https://tenant.sharepoint.com"
$GroupName = "Products"
$TermSetName = "Entry Level"
$SourceTermSetName = "Widget x"
$SourceTermName = "Widget x1000"
$DestTermSetName = "Widget y"

#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\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"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Taxonomy.dll"
$Password = Read-Host -Prompt "Please enter your password" -AsSecureString

#Bind to MMS
$Context = New-Object Microsoft.SharePoint.Client.ClientContext($Site)
$Creds = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($User,$Password)
$Context.Credentials = $Creds
$MMS = [Microsoft.SharePoint.Client.Taxonomy.TaxonomySession]::GetTaxonomySession($Context)
$Context.Load($MMS)
$Context.ExecuteQuery()

#Retrieve Term Stores
$TermStores = $MMS.TermStores
$Context.Load($TermStores)
$Context.ExecuteQuery()

#Bind to Term Store
$TermStore = $TermStores[0]
$Context.Load($TermStore)
$Context.ExecuteQuery()

#Retrieve Groups
$Groups = $TermStore.Groups
$Context.Load($Groups)
$Context.ExecuteQuery()

Foreach ($Group in $Groups)
{
Write-Host $Group.Name
}

$Group = $Groups | Where {$_.Name -eq $GroupName}
$Context.Load($Group)
$Context.ExecuteQuery()

$TermSet = $Group.TermSets
$Context.Load($TermSet)
$Context.ExecuteQuery()

$TermSet = $Group.TermSets | Where {$_.Name -eq $TermSetName}
$Context.Load($TermSet)
$Context.ExecuteQuery()

$Terms = $TermSet.Terms
$Context.Load($Terms)
$Context.ExecuteQuery()

#Bind to Term Sets
$Source = $Terms | Where {$_.Name -eq $SourceTermSetName}
$Context.Load($Source)
$Context.ExecuteQuery()

$Destination = $Terms | Where {$_.Name -eq $DestTermSetName}
$Context.Load($Destination)
$Context.ExecuteQuery()

#Move
$ChildTerms = $Source.Terms
$Context.Load($ChildTerms)
$Context.ExecuteQuery()

$SourceTerm = $Source.Terms | Where {$_.Name -eq $SourceTermName}
$Context.Load($SourceTerm)
$Context.ExecuteQuery()

$Move = $SourceTerm.Move($Destination)
$Context.ExecuteQuery()

Once the script has completed, it should look like this....

As you can see Widget x1000 is now within Widget y.

Merging a Term

Let's imagine that Widget x1000 is being discontinued and it's functionality will be incorporated into Widget y1000. In this scenario it may be desirable to merge the terms, which involves removing Widget x1000 as a separate entity and adding it as an additional label to Widget y1000.

The script below automates this operation, making the assumption that you have run the script above (they should be run in sequence as moving is a pre-requisite to merging). You simply need to update the highlighted variable with the name of the term you wish to merge to and Widget x1000 will be added as a label to Widget y1000.

$DestTermName = "Widget y1000"
$Destination = $Terms | Where {$_.Name -eq $DestTermSetName}
$Context.Load($Destination)
$Context.ExecuteQuery()

$ChildTerms = $Destination.Terms
$Context.Load($ChildTerms)
$Context.ExecuteQuery()

$DestTerm = $Destination.Terms | Where {$_.Name -eq $DestTermName}
$Context.Load($DestTerm)
$Context.ExecuteQuery()

$SourceTerm = $Destination.Terms | Where {$_.Name -eq $SourceTermName}
$Context.Load($SourceTerm)
$Context.ExecuteQuery()

$Merge = $SourceTerm.Merge($DestTerm)
$Context.Load($Merge)
$Context.ExecuteQuery()

All being well it should look something like this!

Brendan Griffin - @brendankarl