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…