Manipulating the Terms Store through Powershell
I really like Powershell, for its repeatability, the fact that it means I know that that once I’ve worked out how to do something I can save it in my stash of modules for later, and the way that I can spin up lots of different scenarios very quickly.
For my session on “Build a website in 60 minutes” at SP Evo, I spent a fair amount of time pulling together scripts for manipulating term store data quickly. I could, I guess, have just used the out of the box “import CSV file” option from the UI. But that won’t cut it when going between multiple environments in the real world. I need to be able to do it from my build scripts, and I couldn’t find any way via the API to import from a CSV – it seems that code is locked into the web UI, not baked into the core APIs.
There are other posts out there for how to set up the term store in the first place, both manually and via Powershell, so I’ll not repeat them here. But assuming you have a term store available, here is some very straightforward Powershell to start populating it:
Assuming you have an XML file in this format:
<?xml version="1.0"?> <termstore name="Managed Metadata Service Application Proxy"> <group name="SPEvo"> <termset name="Services"> <term name="Customer Engagement"> <term name="Analytics"> </term> <term name="Search Engine Optimisation"> </term> <term name="Engagement Strategies"> </term> </term> <term name="Technology"> <term name="Technical Build and Delivery"> </term> <term name="Technical Consultancy"> </term> <term name="Technical Design"> </term> </term> <term name="User Experience"> <term name="Creative Design"> </term> <term name="Information Architecture"> </term> </term> </termset> </group> </termstore>
You should be able to manipulate that like this:
$TermStoreData = [xml] (Get-Content ($xmlpath))
$site = Get-SPSite $url
$session = new-object Microsoft.SharePoint.Taxonomy.TaxonomySession($site)
$termstore = $session.TermStores[$TermStoreData.termstore.name]
$TermStoreData.termstore.group |
ForEach-Object {
## create the group
if ($termstore.Groups[$_.name] -eq $null)
{
$group = $termstore.CreateGroup($_.name);
Write-Host -ForegroundColor Cyan "Added group $_.name"
$_.termset |
ForEach-Object {
## create the termset
$termset = $group.CreateTermSet($_.name)
Write-Host -ForegroundColor Cyan "Added termset $_.name"
SetTermsRecursive -termsetitem $termset -parentnode $_
}
}
}
$termstore.CommitAll()
Some of the “power” of “Powershell” is in its ability to load up standard .NET objects; in this case there are no out of the box cmdlets to add term store data, so we have to make use of “new-object” to load up the TaxonomySession object. From that we are into native SharePoint code – walking down the XML and creating Term Store Groups, Term Sets and then ultimately Terms. The terms in this example are deployed in a recursive function:
function SetTermsRecursive ([Microsoft.SharePoint.Taxonomy.TermSetItem] $termsetitem, $parentnode)
{
$parentnode.term |
ForEach-Object {
## create the term
$newterm = $termsetitem.CreateTerm($_.name, 1033)
Write-Host -ForegroundColor Cyan "Added term $_.name"
SetTermsRecursive($termsetitem, $_)
}
}
Pretty neat? Some core Powershell abilities are showcased here:
- Walking through XML DOM structures via the “dot” shorthand notation. For example, $TermStoreData.termstore.group is equivalent to $TermStoreData.SelectNodes(“/termstore/group”) – the dot notation means less typing and more readable code.
- Pipelines – for example, the ForEach-object and pipeline associated $_ syntax.
These can be difficult to read in examples, but I strongly advise anyone starting out in Powershell to get to grips with these, particularly the piping model.
Anyway, the end result is that in a few lines of fairly simple Powershell you can take an XML file and build up a term store with data as part of your deployment model. I’ll post later about why that is important if you ever want to use those term sets in, say a site column declaration…

Your code seems to fail on the following line SetTermsRecursive($termsetitem,$_) within the function with the following error message
SetTermsRecursive : Cannot process argument transformation on parameter ‘termsetitem’. Cannot convert the “System.Object[]” value of type “System.Object[]” to type “Microsoft.SharePoint.Taxonomy.TermSetItem”.
Joris
June 24, 2010 at 7:18 am
Hi Joris / James – I wrote this on RC rather than RTM, so there may be a change to the API. I’ll have a look and post an update.
Ben Robb
July 19, 2010 at 10:04 am
Thx for the great tip, Ben!
Joris, I think a small tweak in the SetTermsRecursive will fix things right up. The method(var1, var2) is c# syntax; calling a powershell method that way makes it look like an array of objects since () only represents order of precedence groupings. Added a null check, otherwise the foreach of the recursive call will try to act on a $null value (see comment / link below). Finally, changed the recursive call from $termsetitem to $newterm.
if ($_.term -ne $null)
{
#powershell treats $null as explicit placeholder; see http://technet.microsoft.com/en-us/library/dd347608.aspx
SetTermsRecursive $newterm $_
}
Best,
Jason
Jason
August 20, 2010 at 7:42 am
Hi Ben,
You can use Get-SPTaxonomySession to grab the term store and Import-Csv to create a CSV format of your own. The format provided with SharePoint 2010 doesn’t support synonyms so I have used this method to populate the IPSV terms (11,000+), which are provided as a CSV download to begin with.
Phil
Phil Childs
June 29, 2010 at 3:10 pm
This looks like exactly what i need however I’m getting the following error when i run this:
Cannot index into a null array.
At C:\_dev\Scripts\metadata.ps1:39 char:24
+ if ($termstore.Groups[ <<<< $_.name] -eq $null)
+ CategoryInfo : InvalidOperation: (MyGroup:String) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
Thoughts?
James
July 8, 2010 at 10:18 pm
James, I had the same problem and figured out this was to do with PowerGui. I ran the script in native PowerShell and all worked fine! very strange.
Paul Grimley
November 12, 2010 at 2:50 pm
Great post, Ben! I love practical, elegant solutions!
David Remillard
July 17, 2010 at 10:13 pm
[...] – I could use XML and PowerShell as Ben Robb did. [...]
Programmatically: Create many term sets from one csv file « Norpoint's Blog
November 8, 2010 at 1:48 pm
has anyone been able to successfully put TermStore.CommitAll() anywhere in C# code on a sharepoint site without it breaking? it seems to be difficult to find examples of it, I need a little chat to get the appropriate permissions to use it there
brian
November 11, 2010 at 6:26 pm
Updated Code which resolves the error:Cannot process argument transformation on parameter
Add-PsSnapin Microsoft.SharePoint.PowerShell
function SetTermsRecursive ([Microsoft.SharePoint.Taxonomy.TermSetItem] $termsetitem, $parentnode)
{
$parentnode.term |
ForEach-Object {
## create the term
if($_ -ne $null)
{
$newterm = $termsetitem.CreateTerm($_.name, 1033)
Write-Host -ForegroundColor Cyan “Added term $_.name”
SetTermsRecursive $newterm $_
}
}
}
#Do not modify anything in the script from here onwards
function Get-ScriptDirectory
{
$Invocation = (Get-Variable MyInvocation -Scope 1).Value
Split-Path $Invocation.MyCommand.Path
}
#Solutions to Deploy
$XMLName = “ManagedMetaDataTermSets.xml”
$XMLPath = Join-Path (Get-ScriptDirectory) $XMLName
echo “Extracting information from the $XMLPath”
#Site Collection URL – Give your site collection url in quotation marks
$TaxonomySiteUrl = “http://localhost”
#Access the TermStore data
[xml]$TermStoreData = Get-Content ($XMLPath)
$site = Get-SPSite $TaxonomySiteUrl
$session = new-object Microsoft.SharePoint.Taxonomy.TaxonomySession($site)
$termstore = $session.TermStores[$TermStoreData.termstore.name]
$TermStoreData.termstore.group |
ForEach-Object {
## create the group
if ($termstore.Groups[$_.name] -eq $null)
{
$group = $termstore.CreateGroup($_.name);
Write-Host -ForegroundColor Cyan “Added group $_.name”
$_.termset |
ForEach-Object {
## create the termset
$termset = $group.CreateTermSet($_.name)
Write-Host -ForegroundColor Cyan “Added termset $_.name”
#SetTermsRecursive -termsetitem $termset -parentnode $_
SetTermsRecursive -termsetitem $termset -parentnode $_
}
}
}
$termstore.CommitAll()
Mehul Bhuva
January 20, 2011 at 5:20 pm
# Some fixes for you
function SetTermsRecursive ([Microsoft.SharePoint.Taxonomy.TermSetItem] $termsetitem, $parentnode, $tab)
{
if($parentnode.ChildNodes.Count -gt 0) {
$parentnode.term | ForEach-Object {
## create the term
$newterm = $termsetitem.CreateTerm($_.name, 1033)
Write-Host -ForegroundColor Cyan “$tab`t” $_.name
if($_.ChildNodes.Count -gt 0) {
SetTermsRecursive $newterm $_ “$tab`t”
}
}
}
}
$TermStoreData = [xml] (Get-Content ($ConfigFile))
$session = Get-SPTaxonomySession -Site $CentralAdminUrl
Write-host “Count of termstores is ” $session.TermStores.Count
$termstore = $session.TermStores[$TermStoreData.termstore.name]
$TermStoreData.termstore.group | ForEach-Object {
## create the group
write-host “Processing group:” $_.name
if ($termstore.Groups -eq $null -or $termstore.Groups[$_.name] -eq $null)
{
Write-Host “Creating group:” $_.name
$group = $termstore.CreateGroup($_.name);
$_.termset | ForEach-Object {
## create the termset
Write-Host “Creating termset:” $_.name
$termset = $group.CreateTermSet($_.name)
$openSet=[bool]$($_.Open)
$available=[bool]$($_.AvailableForTagging)
$termset.IsOpenForTermCreation = $openSet
$termset.IsAvailableForTagging = $available
SetTermsRecursive $termset $_ “”
}
}
}
$termstore.CommitAll()
Brian T
February 8, 2011 at 1:36 pm