Powershell to Create TFS Backlog Items

I work with Team foundation server – keeping track of projects, etc… and now that it’s January i had 100+ tasks to create. I thought to myself… why do this manually?


####################################################################
###
###  Script to create TFS entries from CSV file
###  by Jeremy Castleberry 
###  created 1-16-2019
###  last update 1-17-2019
###  Version 1
###
####################################################################

#region Log Of Changes
####################################################################
###
###  
###  
###
###
###
###
####################################################################
#endregion Log of Changes
####################################################################


#region FUNCTIONS

#############################################################################
##
## Function from Hey Scripting Guys!
##  https://blogs.technet.microsoft.com/heyscriptingguy/2009/09/01/hey-scripting-guy-can-i-open-a-file-dialog-box-with-windows-powershell/
##
#############################################################################

Function Get-FileName($initialDirectory)
	{   
 	[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") |
 	Out-Null
	
 	$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
 	$OpenFileDialog.initialDirectory = $initialDirectory
 	$OpenFileDialog.filter = "All files (*.*)| *.*"
 	$OpenFileDialog.ShowDialog() | Out-Null
 	$OpenFileDialog.filename
	} 
#end function Get-FileName


#endregion FUNCTIONS


#region Get Input File
#We need to prompt for the file. 

[string]$VARfilepath  = Get-FileName

# or set it hardcoded
#$VARfilepath = "c:\temp\tasks.csv"

#EndRegion Get Input File
####################################################################

#region Modules
# Import whichever modules we need
Import-Module TfsCmdlets -Force

#endregion Modules
####################################################################

#region Variables
[string]$VARUrl = "http://YOURSERVER:####/tfs/Projects/"
[string]$VARTFSproject = "TFSPROJECT"
[array]$ARRTFSItems = @()

#endregion Variables
####################################################################

#region Connect to TFS components

#First to the server
Connect-TfsTeamProjectCollection $VARUrl

# and now our project 
Connect-TfsTeamProject -Project $VARTFSproject
$OBJtms = Get-TfsTeamProject -Project $VARTFSproject

# If you need an Array of projects:
#$ARRprojects = Get-TfsTeamProject

# If you need an array of sprints:
#$ARRIterations = Get-TfsIteration -Project TFSPROJECT

#endregion Connect to TFS components
####################################################################

#Region BODY
####################################################################
###
###  Body
###
####################################################################

#############################
## Get the Array of Tasks


$ARRTFSItems = Import-Csv -Path $VARfilepath

# List of columns expected in the CSV:
# areaPath
# iterationPath
# assignee
# title
# type    #'Task' or 'Product Backlog Item'
# description
# parentItem
# $effort
# tags

## Start working through each of our TFS items and Create them.

Foreach ($tfsitem in $ARRTFSItems)
	{
	####################################################################
	# Let's blank out the variables.  
	# if a property is "empty" in the array,  when looping the variable isn't replaced by setting the variable
	# to the property and so the previous use of that property for the pevious item in the loop is kept.  
	####################################################################
	
	$areaPath = $null
	$iterationPath = $null
	$assignee = $null
	$title = $null
	$type = $null     #'Task' or 'Product Backlog Item'
	$description = $null
	$parentItem = $null
	$effort = $null
	$tags = $null
	
	## now we set the variables to the data in our TFS item in the Array
	
	$areaPath = $tfsitem.areaPath
	$iterationPath = $tfsitem.iterationPath
	$assignee = $tfsitem.assignee
	$title = $tfsitem.title
	$type = $tfsitem.type
	$description = $tfsitem.description
	$effort = $tfsitem.effort
	$tags = $tfsitem.Tags
	$parentID = $null
	
	####################################################################
	# for the parent we need to do some custom work.
	# if the item Type is a Backlog Item, then the parent is a #number of a feature. 
	# if the item type is a task - we need to get the backlog Item and it's number
	####################################################################
	
	If ($type -eq "Task")
		{
		#ok. so it's a task.  We need to get the product backlog item from the specified title
		$parenttitle = $tfsitem.parentItem
				
		#we are going to look for the Title in TFS work items
		[array]$ARRparentworkitem = Get-TfsWorkItem -Text $parenttitle
				
		#we most likely will get back multiple so lets loop through and find just the backlog item
		
		Foreach ($parentworkItem in $ARRparentworkitem)
			{
			$parentworkitemType = $parentworkitem.type.Name
			
			If ($parentworkitemType -eq "Product Backlog Item")
				{
				
				$parentworkitemTitle = $parentworkitem.title
				# and lets see if the backlog item is an exact title match
					If ($parentworkitemTitle -eq $parenttitle)
						{
						#ok we have our match - so lets set the Parent
						[int]$parentID = $parentworkitem.Id
					
						#close the IF
						}
				
				#Close the IF
				}
				
			#close the ForEach
			}
		
		
		#close the IF
		}
	
	If ($type -eq "Product Backlog Item")
		{
		#ok. so it's a Product Backlog Item - meaning we should have the Parent ID 
	
		[int]$parentID = $tfsitem.parentItem
		}
			
		
	


	####################################################################
	#
	# Ok. Lets create the object now
	#
	####################################################################
	
	# we create an essentially blank new item

	$newTFSItem = New-TfsWorkItem -Title $title -Type $type -Project $VARTFSproject
		
	# so we need the ID of the newTFSItem

	[int]$Newid = $newTFSItem.Id

	####################################################################
	#
	# Lets build a hash table of the properties
	#
	####################################################################
	
	# we need on set for Backlog Items and another for Tasks

	If ($type -eq "Task")
		{
		
		$HASHUpdateFields = @{
			'Description'=$description
			'Assigned To'=$assignee
			'Iteration Path'=$IterationPath
			'Area Path'=$areaPath
			'Remaining Work'=$effort
			'Tags'=$tags
			}	
		
		#close IF
		}
		
	If ($type -eq "Product Backlog Item")
		{	
		$HASHUpdateFields = @{
			'Description'=$description
			'Assigned To'=$assignee
			'Iteration Path'=$IterationPath
			'Area Path'=$areaPath
			'Effort'=$effort
			'Tags'=$tags
			}
		
		#close IF
		}

	####################################################################
	#
	# Using the SET command we are going to set the hash table to the NewID
	#
	####################################################################
	
	Set-TfsWorkItem -WorkItem $Newid -Fields $HASHUpdateFields


	####################################################################
	#
	# And now we need to connect it to it's parent.
	# TARGET is the PARENT 
	# SOURCE is the CHILD
	#
	####################################################################
	
	# lets make sure we have a parent before we link
	
	If ($parentID -ne $null)
		{
		Add-TfsWorkItemLink -SourceWorkItem $Newid -TargetWorkItem $parentID -EndLinkType Parent	
		
		#Close IF
		}
	
	#close the ForEach
	}	

#endregion Body

A list of columns for the CSV Follows:

  • areaPath
  • iterationPath
  • assignee
  • title
  • description
  • effort
  • tags
  • type
    • Task
    • Product Backlog Item
  • parentItem
    • for Product Backlog Items it should be the # for the Parent Feature
    • for task put the name of the Product Backlog items – which hopefully are unique…. go make it unique


Cool tool for the Cireson Portal

The Cireson portal has it’s own web based pages for Knowledge articles – but what does one do when they have 4000 knowledge articles in Rich Text Format (RTF) stored in the SCSM knowledge base?

A guy named John Doyle was awesome and wrote a script and DLL to migrate the date! Go John! you are the hero of the day.


The other day, someone asked about migrating KB articles from the SCSM database to the Cireson Portal. 

I built a small PowerShell script to attempt this. The code is fairly simple. You need to place the MarkupConverter.dll file somewhere on the file system and then reference this path in the script. You also need to set the URL to your Portal Server, and your credentials to authenticate to the portal.

It uses SMLets to get the list of KB articles from the SCSM Server and then uploads the articles to the Cireson Portal using the API – AddOrUpdateHTMLKnowledgeApi.

The code converts the RTF content in the End User and Analyst content to XAML, and then converts this to HTML. I looks alright on my server, but I am not guaranteeing the accuracy of the conversion.

Please feel free to modify it and adapt it to your needs.


https://us.v-cdn.net/6026663/uploads/editor/8c/2q7dwesg4hb7.zip

So of course i wanted to know more about this “AddorUpdateHTMLKnowledgeAPI which lead me to here:


https://support.cireson.com/Help/Api/POST-api-V3-KnowledgeBase-AddOrUpdateHTMLKnowledgeApi

and of course the general Cireson help:

https://support.cireson.com/Help

Commenting your Code

There is nothing more frustrating than looking at Powershell code from someone else – who didn’t bother to comment the code. 1000 lines without a clue of what is happening, unless you understand the relevance of each command in context. Uhg.

In Powershell one can use the # symbol (Pound or Hashtag) to denote information that is “notes” and not part of the code.

For example I will denote section headers with large blocks of # signs for visibility:

############################################
#
# This section is about commenting your code
#
############################################

and smaller notes with a smaller comment:

####################
## Loop through this

You can also add a # at the end to comment out a piece – for example if I use a whatif for testing but don’t want it there once the code is “production”.

$user = Get-Aduser -name $inputname #-whatif

NOTE: Whereever the #starts, the code ends.

In additon you can use a “Regions” to denote sections of your code. Like college football conferences… but with less gerrymandering.

To do this we use the # sign and the word “region” followed by a description to start the section:

#region Our Variables

And then # and “endregion” to end the section.

#endregion

Most powershell editors, like PowerGui Script editor, will then allow you to collapse the region – makes browsing over sections of code:

I hope you find this useful.

SCSM Entity Explorer

Historically I have used the SMLets to query objects in SCSM and learn about classes and relationships. Powershell works – but it’s not always the most beautiful of interfaces.

I stumbled upon a blog article and SCSM Entity Explorer and I really can’t do it justice. There are a number of well written blog articles, so i am not going to bore you with reading mine – and I am going to replicate the info to find the articles here with the express understanding that please give credit where due.

The tool can be used to explore classes and relationships of objects in SCSM. This is invaluable for learning HOW Service Manager is structured… especially if like my company, you have done some customization on top of the standard management packs.

The tool can be located here:
https://gallery.technet.microsoft.com/SCSM-Entity-Explorer-68b86bd2

It was written by Dieter Gasser who’s blog can be found here:
https://blog.dietergasser.com/2014/05/08/scsm-entity-explorer/


And Xaptiy did an excellent job of explaining the tools use here: https://www.xapity.com/single-post/2017/05/27/How-to-use-SCSM-Entity-Explorer