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