Automating Task Creation in Team Foundation Server with PowerShell

Administrating Team Foundation Server often involves repeating the same tasks over and over with only slight variation in the details. This is especially true if your team adheres to an Agile software development methodology. Every few weeks a new Iteration begins, which means inputting new Change Requests into Team Foundation Server along with their associated Tasks*.

Repetition equals Automation equals PowerShell. If you have to repeat the same task in Windows more than a few times, consider automating it with PowerShell. Microsoft has done an outstanding job equipping PowerShell to access a majority of the functionary of their primary application; Team Foundation Server 2010 (TFS) is no exception.

Microsoft’s latest release of Team Foundation Server Power Tools December 2011 includes Windows PowerShell Cmdlets for Visual Studio Team System Team Foundation Server. According to Microsoft, Power Tools are a set of enhancements, tools and command-line utilities that increase productivity of Team Foundation Server scenarios. Power Tool’s TFS PowerShell Cmdlets give you control of common version control commands in TFS.

One gotcha with TFS Power Tools, it doesn’t install PowerShell extras by default. Yes I agree, it makes no sense. If you already have Power Tools installed, you must rerun the installer, select the Modify Install option and add the PowerShell features. If you are installing Power Tools for the first time, make sure to select the Custom install option and add the PowerShell features.

*Tasks are a type of TFS Work Item. Work Item types can also include Bugs, Defects, Test Cases, Risks, QoS Requirements, or whatever your teams decides to define as Work Items. There is a comprehensive explanation of Work Items in chapter 12 of Microsoft’s Patterns & Practices, available to review on Codeplex.

Automating Task Creation

Working with different teams during my career that practice SCRUM, a variation of Agile, we usually start a new Sprint (Iteration) ever four to six weeks with an average Sprint Backlog of 15-25 items. Each item in the backlog translates into individual CRs in TFS. Each CR has several boilerplate Tasks associated with them. Many Tasks are common to all Change Requests (CR). Common Tasks often include analysis, design, coding, unit testing, and administration. Nothing is more mind-numbing as a Manager than having to input a hundred or more Tasks into TFS every few weeks, with each Task requiring an average of ten or more fields of data. In addition to the time requirement, there is the opportunity for human error.

The following PowerShell script creates a series of five different Tasks for a specific CR, which has been previously created in TFS. Once the Tasks are created, I use a separate method to link the Tasks to the CR. Every team’s development methodologies are different; ever team’s use of TFS is different. Don’t get hung up on exactly which fields I’ve chosen to populate. Your processes will undoubtedly require different fields.

There are many fields in a Work Item template that can be populated with data, using PowerShell. Understanding each field’s definition – name, data type, and rules for use (range of input values, required field, etc.) is essential. To review the field definitions, in Visual Studio 2010, select the Tools tab -> Process Editor -> Work Item Types -> Open WIT from Server. Select your Work Item Template (WIT) from the list of available templates. The template you chose will be the same template defined in the PowerShell script, with the variable $workItemType. To change the fields, you will need the necessary TFS privileges.

Task WIT Data Fields

Task WIT Data Fields

Avoiding Errors

When developing the script for this article, I was stuck for a number of hours with a generic error (shown below) on some of the Tasks the script tried to create – “…Work Item is not ready to save” I tried repeatedly debugging and altering the script to resolve the error without luck. An end up the error was not in the script, but in my lack of understanding of the Task Work Item Template (WIT) and its field definitions.

Incorrect Task Input Error

Incorrect Task Input Error

By trial and error, I discovered this error usually means that either the data being input into a field is invalid based on the field’s definition, or that a required field failed to have data input for it. Both were true in my case at different points in the development of the script. First, I failed to include the Completed Time field, which was a required field in our Task template. Secondly, I tried to set the Priority of the Tasks to a number between 1 and 5. Unbeknownst to me, the existing Task template only allowed values between 1 and 3. The best way to solve these types of errors is to create a new Task in TFS, and try inputting the same data as you tried to inject with the script. The cause of the error should quickly become clear.

The Script

For simplicity sake I have presented a simple PowerShell script. The script could easily be optimized by wrapping the logic into a function with input parameters, further automating the process. I’ve placed a lot of comments in the script to explain what each part does, and help make customization easier.The script explicitly declares all variables and adheres to PowerShell’s Strict Mode (Set-StrictMode -Version 2.0). I feel this makes the script easier to understand and reduces the number of runtime errors.

#############################################################
#
# Description: Automatically creates (5) standard Task-type
#              Work Items in TFS for a given Change Request.
#
# Author:      Gary A. Stafford
# Created:     04/12/2012
# Modified:    04/14/2012
#
#############################################################

# Clear Output Pane
clear

# Loads Windows PowerShell snap-in if not already loaded
if ( (Get-PSSnapin -Name Microsoft.TeamFoundation.PowerShell -ErrorAction SilentlyContinue) -eq $null )
{
    Add-PSSnapin Microsoft.TeamFoundation.PowerShell
}

# Set Strict Mode - optional
Set-StrictMode -Version 2.0

# Usually changes for each Sprint - both specific to your environment
[string] $areaPath = "Development\PowerShell"
[string] $iterationPath = "PowerShell\TFS2010"

# Usually changes for each CR
[string] $changeRequestName = "Create Task Automation PowerShell Script"
[string] $assignee = "Stafford, Gary"

# Values represent units of work, often 'man-hours'
[decimal[]] $taskEstimateArray = @(2,3,10,3,.5)
# Remaining Time is usually set to Estimated time at start (optional use of this array)
[decimal[]] $taskRemainingArray = @(2,3,10,3,.5)
# Completed Time is usually set to zero at start (optional use of this array)
[decimal[]] $tasktaskCompletedArray = @(0,0,0,0,0,0)

# Usually remains constant
# TFS Server address - specific to your environment
[string] $tfsServerString = "http://[YourServerNameGoesHere]/[PathToCollection]"

# Work Item Type - specific to your environment
[string] $workItemType = "Development\Task"

[string[]] $taskNameArray = @("Analysis", "Design", "Coding", "Unit Testing", "Resolve Tasks")
[string[]] $taskDisciplineArray = @("Analysis", "Development", "Development", "Test", $null)

# Loop and create of eack of the (5) Tasks in prioritized order
[int] $i = 0

Write-Host `n`r**** Script started...`n`r

while ($i -le 4) {
    # Concatenate name of task with CR name for Title and Description fields
    $taskTitle = $taskNameArray[$i] + ": " + $changeRequestName

    # Build string of field parameters (key/value pairs)
    [string] $fields = "Title=$($taskTitle);Description=$($taskTitle);Assigned To=$($assignee);"
    $fields += "Area Path=$($areaPath);Iteration Path=$($iterationPath);Discipline=$($taskDisciplineArray[$i]);Priority=$($i+1);"
    $fields += "Estimate=$($taskEstimateArray[$i]);Remaining Work=$($taskRemainingArray[$i]);Completed Work=$($tasktaskCompletedArray[$i])"

    #For debugging - optional console output
    Write-Host $fields

    # Create the Task (Work Item)
    tfpt workitem /new $workItemType /collection:$tfsServerString /fields:$fields

    $i++
 }

 Write-Host `n`r**** Script completed...

The script begins by setting up a series of variables. Some variables will not change once they are set, such as the path to the TFS server, unless you work with multiple TFS instances. Some variables will only change at the beginning of each iteration (Sprint), such as the Iteration Path. Other variables will change for each CR or for each Task. These include the CR title and Estimated, Completed, and Remaining Time. Again, your process will dictate different fields with different variables.Once you have set up the script to your requirements and run it successfully, you should see output similar to the following:

Successful Creation of Tasks

Successful Creation of all Five Tasks

In TFS, the resulting Tasks, produced by the script look like the Task, below:

New Task Created by PowerShell

New Task Created by PowerShell

Deleting Work Items after Developing and Testing the Script

TFS Administrators know there is no Work Item delete button in TFS. So, how do you delete the Tasks you may have created during developing and testing this script? The quickest way is from the command line or from PowerShell. You can also delete Work Items programmatically in .NET. I usually use the command line, as follows:

  1. Open the Visual Studio 2010 Command Prompt.
  2. Change the directory to the location of witadmin.exe. My default location is:
    C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE.
  3. Run the following command, substituting the Task Id for the Task Id or Task Ids, comma delimited without spaces, of the Tasks you want to delete:
    witadmin destroywi /collection:[Your TFS Collection Path Here] /id:12930 /noprompt
Deleting a Task from the Command Line

Deleting a Task from the Command Line

Almost the same command can be run in PowerShell by including the path to witadmin.exe in the script. I found this method at Goshoom.NET Dev Blog. You can read more, there.

Be warned, there is no undoing the delete command. The noprompt is optional; using it speeds up the deletion of Tasks. However, leaving out noprompt means you are given a chance to confirm the Task’s deletion. Not a bad idea when you’re busy doing a dozen other things.

Further PowerShell Automation

Creating Tasks with PowerShell, I save at least two hours of time each Sprint cycle, and greatly reduce my chance for errors. Beyond Tasks, there are many more mundane TFS-related chores that can be automated using PowerShell. These chores include bulk import of CRs and Tasks from Excel or other Project Management programs, creating and distributing Agile reports, and turnover and release management automation, to name but a few. I’ll explore some of these topics in future blog.

, , , , , , , , ,

  1. #1 by http://fundupra.com on May 19, 2013 - 2:18 am

    If your camera doesn’t have this option, try going outside where the light is usually better. It includes the feature of red eye reduction and the various white balancing features include Auto, Daylight, Cloudy, Fluorescent H, Fluorescent L, Tungsten and Custom. As you will see later other considerations like lenses, flash and battery are no less important.

  2. #2 by frtic on July 19, 2013 - 8:20 am

    Hi,
    My power tools installation – in custom option – did not have option to install powershell extras.
    There were just 2 options:
    1. Best practices analyzer
    2. Tfs backup plan

    but no powershell extras
    can yoou please help with this?

    thank you
    frtic

  3. #3 by ADP on April 4, 2014 - 4:31 am

    I am having 25 issues with priority as 1
    Now I want to downgrade all the priorities to 2
    To achieve this we need to provide value in the “Reason” field and then issue will get saved with “Priority” 2
    Problem
    When “Priority” is 1 the “Reason” field is readonly, it becomes enable when we change Priority from 1 to 2.
    Manually – We change the priority to 2 ,then reason field becomes enable now enter the value and save it.
    How to we get it by power shell code?
    I tried with
    __________________________________________________
    $P =2
    $R=”ABC”
    $fields = “Priority:=$($P);Reason:=$($R)”
    tfpt workitem /collection:$tfsSvrpath /update 1571 /fields:$fields
    ___________________________________________________
    It executes successfully
    Work item 1571 updated.
    but when I open TFS the Priority still remains as 1 no value is displayed in Reason

  4. #4 by Marcel on August 6, 2014 - 5:46 am

    Great script, thanks! A few problems I discovered:

    –TFS 2013: some fields have been removed or changed: Estimate, Remaining, Discipline–>Activity
    –Script is not automatically triggered when creating a PBI. Perhaps you found a solution already? I’m gonna work on it now. I’ll also try to add a boolean that triggers the script through the process editor of power tools.
    –Note that only the URL uses %20

    Thanks!

  5. #5 by Herny on October 30, 2014 - 12:50 am

    Marcel, I am also looking for the auto-trigger. have your found a solution ?

  6. #6 by ZeroInfinity on March 23, 2015 - 9:39 pm

    Can you tell me how I can get pending changes without excluded files?
    Thanks, in advance.

  7. #7 by Shhram on September 13, 2016 - 7:54 pm

    My contribution to the planet.
    #############################################################
    #
    # Description: Automatically creates standard Tasks for a new Fact table User Story
    # This version links to a parent
    #
    # Prerequisites: – PowerShell 3.0 https://www.microsoft.com/en-us/download/details.aspx?id=34595
    # – Issue this PowerShell command to see the current version that is installed: $PSVersionTable.PSVersion
    # – Visual Studio 2015 needs to be installed as well. Note the paths in the add-type commands below need to be updated after each installation because
    # VS creates a random folder name to put them in ?!
    # – If PS complains about policy, run PowerShell as administrator and execute this command Set-ExecutionPolicy RemoteSigned
    #
    #
    # leveraged from https://programmaticponderings.wordpress.com/2012/04/15/automating-task-creation-in-team-foundation-server-with-powershell/
    # http://mypowershell.webnode.sk/news/access-tfs-by-powershell/
    #
    #############################################################

    #############################################################
    #Instructions:
    # 1. Save the script locally so you do not overwrite the base script.
    # 2. Set the first 3 variables
    # a. Parent user story : the script assumes that a user story has been created and these tasks are to be linked to it
    # b. Table name: Task titles are customized using this
    # c. Assigned to: This can be left blank.
    # 3. Set the Area Path variable to your environment
    # 4. Save the script—requirement before it can be executed
    # 5. Execute by pushing the green arrow in the tool bar or hitting F5
    #############################################################

    # set variables
    $ParentUserStory= 72960
    $TableName= ‘Fact.TEST’ # or Dim.NewTableName. Note DIM tables require an additional step to Seed the table that is not yet included in this script
    $AssignedTo= ‘Your Mom’

    ########################################################################
    # SET AREA PATH AS APPROPRIATE. ITERATION PATH CAN ALSO BE SET HERE #
    ########################################################################
    [string] $tfsServerString = “http://youserver:8080/tfs”
    [string] $AreaPath = “\”
    [string] $IterationPath = “\”

    # set $BaseTableName which will be used in titles below
    If ($TableName.ToLower().StartsWith(“fact.”)) {$BaseTableName=$TableName.Substring(5)}
    elseIf ($TableName.ToLower().StartsWith(“dim.”)) {$BaseTableName=$TableName.Substring(4)}

    # Clear Output Pane
    clear

    # Set Strict Mode – optional
    Set-StrictMode -Version 2.0

    #load assemblies (ARE ALL OF THESE NECESSARY?)
    add-type -Path ‘C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\Extensions\hol2iu4t.wna\Microsoft.TeamFoundation.Build.Workflow.dll’
    add-type -Path ‘C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\Extensions\hol2iu4t.wna\Microsoft.TeamFoundation.Client.dll’

    [void][System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.TeamFoundation.WorkItemTracking.Client”)
    [void][System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.TeamFoundation.Client.TeamFoundationServerFactory”)

    #load assembly to support message boxes
    [System.Reflection.Assembly]::LoadWithPartialName(“System.Windows.Forms”) | Out-Null

    ######################
    # Control variables #
    ######################
    # Loop and create of eack of the Tasks in array order
    [int] $i = 0
    [string] $MsgBoxTxt = “”
    [string] $MsgBoxResult = “”

    #####################################
    # Default estimates for each task #
    #####################################

    # Original Estimate
    [decimal[]] $taskEstimate = @(5, 2, .5, .5, .5, 1, 1, 2, 1, 1, .5, 5, .5, 1, .25) #the “5” hours is not a typo
    # Remaining Time
    [decimal[]] $taskRemaining = @(5, 2, .5, .5, .5, 1, 1, 2, 1, 1, .5, 5, .5, 1, .25)
    # Completed Time
    [decimal[]] $taskCompleted = @(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

    # Work Item Type – specific to your environment
    [string] $workItemType = “Task”

    # Titles for each task
    [string[]] $Title = @(“Define requirements for $TableName and develop user story”,
    “Create EBM for $TableName”,
    “Create $TableName in DW”,
    “Create Error.$BaseTableName in DW_Stage”,
    “Create Updates.$BaseTableName in DW_Stage”,
    “Create view v$BaseTableName in DW_Stage”,
    “Create staging ETL package(s) for v$BaseTableName”,
    “Create ETL package for $TableName”,
    “Create deployment script for $TableName”,
    “Validate stage deployment for $TableName”,
    “Create change request (CR) for $TableName”,
    “Data and change detection validation for $TableName”,
    “Update master package for $TableName”,
    “Deploy $TableName to production”,
    “Clean up artifacts for $TableName”)

    # Activity
    [string[]] $Activity = @(“Analysis”,
    “Design”,
    “Development”,
    “Development”,
    “Development”,
    “Development”,
    “Development”,
    “Development”,
    “Development”,
    “Development”,
    “Planning/Admin”,
    “Testing”,
    “Deployment”,
    “Deployment”,
    “Deployment”)

    ########################################################
    # Create objects that we will need to create new tasks #
    ########################################################

    #get TFS structure, project, task WIT
    [psobject] $tfs = [Microsoft.TeamFoundation.Client.TeamFoundationServerFactory]::GetServer(“http://tfs:8080/tfs/ChemPoint_TFS”)
    $WorkItemStore=$tfs.TfsTeamProjectCollection.GetService([Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore])
    $project=$WorkItemStore.Projects[“Business Intelligence 2016 Agile”] # area
    $type=$project.WorkItemTypes[“Task”] #Type of work item to create
    $ParentWIT=$WorkItemStore.GetWorkItem($ParentUserStory)
    $ParentWITTitle=$ParentWIT.title
    $ParentWITID=$ParentWIT.Id
    #Build dialog box text
    $MsgBoxTxt=”Parent: “+ $ParentWITID + “: ” + $ParentWITTitle + “`r`n”
    $MsgBoxTxt+=”Area Path: $AreaPath`n`rIteration Path: $IterationPath`n`r”
    $MsgBoxTxt+=$Activity.Count#
    $MsgBoxTxt+=” ” + $workItemType + “s will be created for table ” + $TableName + “:`r`n`r`n”
    $MsgBoxTxt+=”[TITLE, ACTIVITY, (ORIGINAL/REMAINING/COMPLETED) `n`r”

    while ($i -le $Activity.Count-1)
    {
    $MsgBoxTxt+= $Title[$i] + ‘, ‘ + $Activity[$i] + ‘ (‘ + $taskEstimate[$i] + ‘/’+ $taskRemaining[$i] +’/’ + $taskCompleted[$i] + “)`n`r”
    $i++
    }

    #Show message box to confirm creation
    write-host $msgboxtxt
    $MsgBoxResult=[System.Windows.Forms.MessageBox]::Show($MsgBoxTxt, “Continue With Task Creation?”, [Windows.Forms.MessageBoxButtons]::YesNo, [Windows.Forms.MessageBoxIcon]::Question)

    ##########################################
    # Start the main loop that creates tasks #
    ##########################################
    $i=0 #Reset counter because we used it above
    if($MsgBoxResult -eq “Yes”) {
    while ($i -le $Activity.Count-1) {

    #create parent-child relationship
    $linkType = $WorkItemStore.WorkItemLinkTypes[[Microsoft.TeamFoundation.WorkItemTracking.Client.CoreLinkTypeReferenceNames]::Hierarchy]

    #create Task
    $item = new-object Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem $type

    # Create and add the link to the $item object
    $link = new-object Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemLink($linkType.ReverseEnd, $ParentUserStory)
    $item.WorkItemLinks.Add($link)

    #set properties
    #properties that are different for each item
    $item.Title = $Title[$i]
    $item.Fields[“Activity”].Value =$Activity[$i]
    $item.Fields[“Original Estimate”].Value = $taskEstimate[$i]
    $item.Fields[“Remaining Work”].Value = $taskRemaining[$i]
    $item.Fields[“Completed Work”].Value = $taskCompleted[$i]
    $item.Fields[“Assigned To”].Value = $AssignedTo

    #properties common for all items – could come from input parameters
    $item.AreaPath = $areaPath
    $item.IterationPath = $iterationPath

    #save task
    $item.Save() # You can comment this line out to run the script without creating any items
    Write-host $item.Title $item.ID

    #Increment the counter
    $i++

    } #while
    } #if

    Write-Host `n`r**** Script completed…

  1. Automating Work Item Creation in TFS 2010 with PowerShell, Continued « Programmatic Ponderings
  2. Using PowerShell to Generate a TFS Change Manifest for Artifact Delivery « ProgrammaticPonderings
  3. Using PowerShell to Generate TFS Change Manifest for Build Artifact Delivery | Jisku.com - Developer Network
  4. Experimenting with powershell and tfs | Developer Rants
  5. Some notes on tfpt.exe from Microsoft Visual Studio Team Foundation Server (2010/2013/2015) Power Tools extension « The Wiert Corner – irregular stream of stuff

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.