Nutzt man das Backlog des TFS auch für die Verwaltung von administrativen Aufgaben kommt es häufig vor, dass man ein Product Backlog Item (PBI) erstellt, unter dem dann jedes Team Mitglied einen Task hinterlegen muss. Typisch hierfür ist z. B. die Vervollständigung der Zeitabrechnung am Monatsende. Schnell wird das Anlegen der Tasks und damit die Bearbeitung vergessen. Kann man hier nichts automatisieren? Klar! Mit PowerShell lässt sich dies sehr leicht lösen.
Referenzen und TFS Dienste laden
Zu allererst ist es nötig die TFS Referenzen in die PowerShell zu laden:
#Load Reference Assemblies [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Client") [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.WorkItemTracking.Client") [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.ProjectManagement")
Weiter werden auch einige TFS-Dienste benötigt:
$TeamProjectCollection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($Url) $TeamProjectCollection.EnsureAuthenticated() # Service instances $css4 = ($TeamProjectCollection.GetService([type]"Microsoft.TeamFoundation.Server.ICommonStructureService4")) -as [Microsoft.TeamFoundation.Server.ICommonStructureService4] $teamService = ($TeamProjectCollection.GetService([type]"Microsoft.TeamFoundation.Client.TfsTeamService")) -as [Microsoft.TeamFoundation.Client.TfsTeamService] $teamConfigService = ($TeamProjectCollection.GetService([type]"Microsoft.TeamFoundation.ProcessConfiguration.Client.TeamSettingsConfigurationService")) -as [Microsoft.TeamFoundation.ProcessConfiguration.Client.TeamSettingsConfigurationService] $store = ($TeamProjectCollection.GetService([type]"Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore")) -as [Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore]
TFS-Team finden
Mit dem „ICommonStructureService4“-Dienst können wir die Project-Uri abfragen, mit deren Hilfe wir, zusammen mit dem „TfsTeamService“-Dienst Zugriff auf die Teams erhalten. Nun muss nur noch das richtige Team anhand des Namens gefunden werden:
$proj = $css4.GetProjectFromName($ProjectName) $allTeams = $teamService.QueryTeams($proj.Uri) $foundTeam = $null ForEach($t in $allTeams) { if ($t.Name -eq $Team) { $foundTeam = $t } } if (!$foundTeam) { throw "The Team $Team could not be found!" }
Standard-Area des Teams abfragen
Das Team benötigen wir einerseits natürlich für die Team-Mitglieder, aber andererseits können wir auch die Standard-Area des Teams abfragen, damit die erstellten Arbeitsaufgaben auch innerhalb des Teams gefunden werden (Wir erinnern uns: Arbeitsaufgaben werden zu Areas hinzugefügt, welche ihrerseits an Teams weitergegeben werden):
# Get default area of team $ids = [System.Linq.Enumerable]::Repeat($foundTeam.Identity.TeamFoundationId, 1) $teamConfigs = [System.Linq.Enumerable]::ToArray($teamConfigService.GetTeamConfigurations($ids)) $teamConfig = $teamConfigs[0].TeamSettings $defaultTeamArea = $teamConfig.TeamFieldValues[0].Value Write-Host "Using default area of team: $defaultTeamArea"
PBI erstellen
Nun müssen wir die “WorkItemType”-Instanzen abrufen um “WorkItems” erstellen zu können, denn Arbeitsaufgaben können immer nur mit einem bestimmten Typ erstellt werden:
$project = $store.Projects[$ProjectName] $wit = $project.WorkItemTypes[$WorkItemType] if (!$wit) { throw "The WorkItemType $WorkItemType could not be found in project $ProjectName" } $taskType = $project.WorkItemTypes["Task"] if (!$taskType) { throw "The WorkItemType Task could not be found in project $ProjectName" }
Der WorkItemType „Task“ sollte fast immer verfügbar sein, den Typ für das PBI haben wir hier Variabel, je nach Aufgabe, gewählt. Es bleibt dem interessierten Leser überlassen auch „Task“ variabel zu gestalten (Bei sehr speziellen TFS-Konfigurationen zum Beispiel). Nun können wir, dank all der Vorarbeit, auch schon das PBI erstellen:
# Create Product Backlog Item (PBI)
Write-Host "Creating Product Backlog Item"
$pbi = New-Object Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem ($wit)
$pbi.Title = $Title
$pbi.AreaPath = $defaultTeamArea
$pbi.Save()
Arbeitsaufgaben erstellen und mit PBI verlinken
Um nun für jedes Teammitglied eine Arbeitsaufgabe, quasi als Kind dieser PBI, zu erstellen, benötigen wir die ID des gerade erstellten Tasks, einen „System.LinkTypes.Hierarchy“ LinkTyp für die „Parent“-Beziehung, und natürlich die Team-Member. Das alles lässt sich aber relativ einfach abfragen:
$linkType = $store.WorkItemLinkTypes["System.LinkTypes.Hierarchy"] $pbiId = $pbi.Id # http://blog.johnsworkshop.net/tfs11-api-query-teams-and-team-members/ $members = $foundTeam.GetMembers($TeamProjectCollection, [Microsoft.TeamFoundation.Framework.Common.MembershipQuery]::Expanded)
Nun bleibt nur noch das iterieren der Mitglieder, das Erstellen der Arbeitsaufgaben (funktioniert genau wie beim PBI) und das Einfügen der Beziehung zwischen den Aufgaben:
ForEach ($member in $members) { if(!$member.IsContainer) { # Filter out groups $user = $member.DisplayName if (!$user) { throw "User $member is invalid." } Write-Host "Creating Task for user $user" $workItem = New-Object Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem ($taskType) $workItem.Title = "$Title" $workItem["Assigned To"] = $user $workItem.AreaPath = $defaultTeamArea $taskLink = New-Object Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemLink ($linkType.ReverseEnd, $pbiId) $workItem.Links.Add($taskLink) $workItem.Save() } }
Mit diesem PowerShell-Script lässt sich das Einfangs formulierte Problem automatisiert lösen. Dazu genügt beispielsweise ein Aufruf wie:
.\Create-Team-Work-Items -Url "https://ait.visualstudio.com/DefaultCollection" -ProjectName "AIT" -Team "Team.Phoenix" -Title "Abrechnung Erledigen " -WorkItemType "Requirement"
Das vollständige Script steht zum Download auf GitHub unter https://github.com/AITGmbH/AIT.Scripts.WorkItems/blob/master/Create-Backlog-Item-And-Tasks-For-Team-Members.ps1 zur Verfügung.
Referenzen / Weitergehende Blogs (u. U. auf Englisch):
– http://blog.johnsworkshop.net/tfs11-api-query-teams-and-team-members/
– http://www.nivot.org/blog/post/2009/10/09/PowerShell20AsynchronousCallbacksFromNET
– http://blogs.msdn.com/b/abaturytski/archive/2007/06/11/effective-queries-in-tfs.aspx
– http://blog.johnsworkshop.net/tfs11-api-reading-the-team-configuration-iterations-and-areas/
Danke, das hat mir wirklich geholfen.