Query TFS build using Powershell

TFS functions detailled on this article are compiled in Module-TFS.psm1 hosted on GITHUB

Connect to TFS

Load TFS assemblies

First step working with TFS and powershell, is to load TFS assemblies in your powershell session.

On a computer running Visual Studio 2015, default path hosting TFS API is :

tfsPath

You can load them from default path, otherwise I advice to export them in a separate folder so that you can generate a stand alone script who can be run from any client, regardless if TFS is missing or version mismatched from server (use server assemblies on client).

You’ll need to add following types to your powershell script in order to query TFS and your build server :

tfsAssembliesShort

Because of dependencies, the dlls list involved is larger, if you choose to export dlls list, you have to get following members :

tfsAssembliesLong

Needed Assemblies to query TFS 2015 or 2013 can be found on GITHUB.

Code

Assemblies can be loaded with Add-Type cmdlet, which lets you define a .NET Framework class in your Windows PowerShell session.

	# inquire your assemblies path
	$assembliesPath = "F:\GitHub\Powershell\tfsKit\Assemblies"

	# inquire TFS assemblies list
	$tfsAssemblies = ("Microsoft.TeamFoundation.Client.dll","Microsoft.TeamFoundation.Build.Client.dll","Microsoft.TeamFoundation.TestManagement.Client.dll","Microsoft.TeamFoundation.WorkItemTracking.Client.dll","Microsoft.VisualStudio.Services.Client.dll","Microsoft.TeamFoundation.WorkItemTracking.Client.DataStoreLoader.dll","Microsoft.TeamFoundation.Build.Common.dll")

	# import assemblies
	$tfsAssemblies | % { Add-Type -Path ([IO.Path]::Combine($assembliesPath, $_)) }
	

This simple way to import assemblies does not bring you error on failed import, to do so we have to get LoaderExceptions property on the exception raised.

As all assemblies name start with “Microsoft” and ends with “.dll”, we can also shorten the call.

	try
	{
		$assembliesShortName = @("TeamFoundation.Client","TeamFoundation.Build.Client","TeamFoundation.TestManagement.Client","TeamFoundation.WorkItemTracking.Client","VisualStudio.Services.Client","TeamFoundation.WorkItemTracking.Client.DataStoreLoader","TeamFoundation.Build.Common")
		$assembliesShortName | % { Add-Type -Path ([IO.Path]::Combine($global:AssembliesFolder, "Microsoft.$_.dll")) }
	}
	catch [System.Reflection.ReflectionTypeLoadException]
	{
		Write-Error $($_.Exception | Select-Object LoaderExceptions | Out-String)
	}
	

In Module-TFS.psm1, we use the Add-Assemblies wrapper from Module-IO.psm1, the module is also used for logging functionnality.

	# import TFS assemblies
	Add-Assemblies @("TeamFoundation.Client","TeamFoundation.Build.Client","TeamFoundation.TestManagement.Client","TeamFoundation.WorkItemTracking.Client","VisualStudio.Services.Client","TeamFoundation.WorkItemTracking.Client.DataStoreLoader","TeamFoundation.Build.Common")
	

Connect to TFS projects collection

To perform queries through a TFS projects collection, we need to instantiate a TfsTeamProjectCollection object from TFS collection URI and TFS Credentials, to do so we use the GetTeamProjectCollection static method from TfsTeamProjectCollectionFactory class in Microsoft.TeamFoundation.Client namespace.

Then, to ensure we are authenticated to a valid team projects collection we use the EnsureAuthenticated method from TfsTeamProjectCollection, this method set the HasAuthenticated property (Boolean) on the source object, and check the projects collection is valid. (no error is returned when project collection object is instanced from a wrong URI, if your URI or credentials are wrong, you will get an error on the EnsureAuthenticated method)

The GetTeamProjectCollection method contains a few overload, notice that Microsoft mark with obsolete attribute overload using TFS credentials.

Name

Description

GetTeamProjectCollection(Uri)

Gets the TfsTeamProjectCollection instance that is associated with the server at the specified URI.

GetTeamProjectCollection(RegisteredProjectCollection)

Gets the TfsTeamProjectCollection instance that is associated with the specified RegisteredProjectCollection instance.

GetTeamProjectCollection(Uri, ICredentialsProvider)

Obsolete. Gets the TfsTeamProjectCollection instance that is associated with the server at the specified URI and a fallback credentials provider.

GetTeamProjectCollection(RegisteredProjectCollection, ICredentialsProvider)

Obsolete. Gets the TfsTeamProjectCollection instance that is associated with the specified RegisteredProjectCollection instance and fallback credentials provider.

GetTeamProjectCollection(String, Boolean, Boolean)

Retrieves the TfsTeamProjectCollection object that is used for a given server.

As Microsoft mark with obsolete attribute overload using tfs credentials, now the supported ways to deals with credentials are :

tfsCredentials

  • Launch powershell command from an autorized active directory account
  • Launch the script from a computer who has TFS installed and your tfs server added to servers collection
  • Let powershell store a TFS cookies when you launch the command for the first time and are prompted for credentials

Code

As credentials are stored in cookies, TFS collection URI is the only mandatory parameter you need to call the GetTeamProjectCollection method.

	$tfsCollectionUri = [System.Uri]"https://tfs.myprojectname.com/DefaultCollection"

	$projectsCollection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($tfsCollectionUri) #this method never throw exceptions

	$projectsCollection.EnsureAuthenticated() #this method throw an exception on bad URI or wrong credentials

	if($projectsCollection.HasAuthenticated)
	{
		# actions list
	}
	else
	{
		Write-Warning "you are not authenticated to $tfsCollectionUri" #this case should never append as EnsureAuthenticated() throw and exception on bad credentials
	}
	

Get TFS projects collection services

A few services are usefull from a TFS projects collection :

  • builds server : servers collection running Team Foundation Build, they are needed to query builds data.
  • work item store : work item tracking client connection to a server that is running Team Foundation Server, the service is usefull to query TFS Team project.
  • test management service : main object for the test management client API, this service is needed to query Unit Tests and Code Coverage results on a build. (depends on team project)

To get these services we use the the method GetService from the TfsTeamProjectCollection object, this method return an instance of the service that is requested if it could be found, or a null reference if it could not be found.

Code

	$buildServer = $projectsCollection.GetService([Microsoft.TeamFoundation.Build.Client.IBuildServer])

	$workItemStore = $projectsCollection.GetService([Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore])

	$testManagementService = $projectsCollection.GetService([Microsoft.TeamFoundation.TestManagement.Client.ITestManagementService])
	

Function : connect to TFS and get projects collection services

Logging functions are provided from Module-IO.psm1.

	<#
	.Synopsis
	Get TFS projects collection and services from Project Uri

	.Description
	Use GetTeamProjectCollection method from TfsTeamProjectCollectionFactory class to get Project Collection from Project Uri, check if TFS is authenticated, if so get builds server, work item and test management services. 

	.Parameter TfsCollectionUri
	TFS team projects collection Uri

	.Example
	Get-TfsConnection "https://tfs.myProjectsColUri.com/DefaultCollection"

	.Outputs
	Object containing TFS projects collection and services on success / null object on fail
	#>
	Function Get-TfsConnection
	{
		[CmdletBinding()]
		Param (	[Parameter(Mandatory=$true,Position=0)][System.Uri]$TfsCollectionUri )

		Write-LogDebug "Start Get-TfsConnection"

		try
		{
			Write-LogHost "getting TFS projects collection from $TfsCollectionUri"

			# initializing return object
			$tfsConnection = New-Object -TypeName PSObject

			$projectsCollection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($TfsCollectionUri)

			Write-LogVerbose "checking authentication to Team projects collection"
			$projectsCollection.EnsureAuthenticated()

			$tfsConnection | Add-Member -NotePropertyName "HasAuthenticated" -NotePropertyValue ($projectsCollection.HasAuthenticated)

			if($projectsCollection.HasAuthenticated)
			{
				Write-LogVerbose "getting TFS builds server from $projectsCollection"
				$buildServer = $projectsCollection.GetService([Microsoft.TeamFoundation.Build.Client.IBuildServer])
				$tfsConnection | Add-Member -NotePropertyName "BuildServer" -NotePropertyValue $buildServer

				Write-LogVerbose "getting TFS work item store cache $projectsCollection"
				$workItemStore = $projectsCollection.GetService([Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore])
				$tfsConnection | Add-Member -NotePropertyName "WorkItemStore" -NotePropertyValue $workItemStore

				Write-LogVerbose "getting test management services from $projectsCollection"
				$testManagementService = $projectsCollection.GetService([Microsoft.TeamFoundation.TestManagement.Client.ITestManagementService])
				$tfsConnection | Add-Member -NotePropertyName "TestManagementService" -NotePropertyValue $testManagementService
			}
			else
			{
				Write-LogWarning "you are not authenticated to $TfsCollectionUri" #this case should never append as EnsureAuthenticated() throw and exception on bad credentials
			}

			# trace Success
			Write-LogDebug "Success Get-TfsConnection"
		}
		catch
		{
			$tfsConnection = $null

			# log Error
			Write-LogError $($_.Exception) $($_.InvocationInfo)
		}

		return $tfsConnection
	}
	

Get TFS build data

As seen in previous section, when querying a TFS Projects collection, its usefull to get the build server service, which can query TFS Builds.

Following examples start from predicate you already instanciate a buildServer, use previous example or
Get-TfsConnection function from Module-TFS.psm1.

Get TFS build

The IBuildServer Interface in Microsoft.TeamFoundation.Build.Client namespace own the QueryBuilds method, here are its overload :

Name

Description

QueryBuilds(String)

Gets all builds for a team project.

QueryBuilds(IBuildDefinition)

Gets all builds for a build definition.

QueryBuilds(IBuildDefinitionSpec)

Gets all builds for a build definition specification.

QueryBuilds(IBuildDetailSpec)

Gets a single build query result for the specified build specification.

QueryBuilds(IBuildDetailSpec[])

Gets the build query results for the specified list of build specifications.

QueryBuilds(String, String)

Gets all builds for a team project and definition.

The most simple overload to get builds data from a build name, use project name and build name as parameters it query for all available informations (QueryOptions = “All”, InformationTypes = “*”), this is a heavy process (up to 1 go memory used) and very long, around 16 minuts. (examples were timed on a 6 month old integration build)

Code

process time : 983757 ms

	$tfsBuilds = $buildServer.QueryBuilds("yourProjectName", "yourBuildName")
	

The most versatile overload, use a IBuildDetailSpec, object as parameters, it bring us full control over the query process from the following properties :

Name

Description

BuildNumber

Gets or sets the number of the desired builds. Wildcard characters are supported.

DefinitionSpec

Gets the build definition specification of the desired builds.

DefinitionUris

Gets the build definition uniform resource identifiers (URIs) of the desired builds.

InformationTypes

Gets or sets the information types that will be returned from the query or queries.

MaxBuildsPerDefinition

Gets or sets the maximum number of builds to return per definition.

MaxFinishTime

Gets or sets the end of the finish time range of the specified builds.

MinChangedTime

Gets or sets the earliest revision date and time of the desired builds.

MinFinishTime

Gets or sets the start value of the finish time range of the specified builds.

Quality

Gets or sets the quality of the desired builds.

QueryDeletedOption

Gets or sets options to query deleted builds.

QueryOptions

Gets or sets the additional data that will be returned from the queries.

QueryOrder

Gets or sets the ordering scheme to use when the user sets a maximum number of builds.

Reason

Gets or sets the reason for the desired builds.

RequestedFor

Gets or sets the user for whom the build was requested.

Status

Gets or sets the statuses of the desired builds.

If you still wan’t to query for all build name occurency, you can choose to return the minimun amount of builds informations (QueryOptions = “None”, InformationTypes = $null), the process is really faster, around 0.3 seconds.

Code

process time : 319 ms

	$buildSpecification = $buildServer.CreateBuildDetailSpec("yourProjectName", "yourBuildName")

	$buildSpecification.QueryOptions = "None"
	$buildSpecification.InformationTypes = $null

	$tfsBuilds = $buildServer.QueryBuilds($buildSpecification)
	

If you query for a build number, you query only one build occurency, the process keep quite fast even if you ask for all build informations, around 1.7 seconds.

process time : 1726 ms

	$buildSpecification = $buildServer.CreateBuildDetailSpec("yourProjectName", "yourBuildName")

	$buildSpecification.BuildNumber = "yourBuildNumber"

	$tfsBuild = $buildServer.QueryBuilds($buildSpecification)
	

Our goal is often to query for the last build occurency for a given build name, as most of the time you can’t infer the build number, combining the two previous example looks like the faster way to get all build informations from the last build occurency of a given build name (data brought by QueryOptions property are not needed), whole process was timed around 2.1 seconds.

process time : 2131 ms

	$buildSpecification = $buildServer.CreateBuildDetailSpec("yourProjectName", "yourBuildName")

	$buildSpecification.QueryOptions = "None"
	$buildSpecification.InformationTypes = $null

	$tfsBuilds = $buildServer.QueryBuilds($buildSpecification)

	# by default, QueryOrder property on build specification is StartTimeAscending
	$tfsLastBuildNumber = ($tfsBuilds.Builds | ? { $_.Status -ne "InProgress" } | Select-Object -Last 1).BuildNumber

	$buildSpecification.InformationTypes = "*"
	$buildSpecification.BuildNumber = $tfsLastBuildNumber

	$tfsBuild = $buildServer.QueryBuilds($buildSpecification)
	

Function : Get TFS builds

Logging functions are provided from Module-IO.psm1, Get-TfsConnection is taken from previous chapter of current article, ressources are on GITHUB.

	<#
	.Synopsis
	Return TFS builds informations

	.Description
	Use build details specification properties to filter type of data returned, see https://msdn.microsoft.com/en-us/library/microsoft.teamfoundation.build.server.builddetailspec_properties(v=vs.120).aspx

	.Parameter TfsCollectionUri
	TFS team projects collection Uri

	.Parameter TfsProjectName
	TFS project name as string

	.Parameter TfsBuildName
	TFS build name

	.Parameter TfsBuildNumber
	build number filter, wildcards are allowed, default = "*", buildnumber format : buildName_buildDate_iteration

	.Parameter InformationTypes
	information types to return, * indicates all informations, default = $null (get only elementary build infos)

	.Parameter MinFinishTime
	minimum finish time filter, default = 0 (01/01/0001 00:00:00)

	.Parameter MaxFinishTime
	maximum finish time filter, default = 0 (01/01/0001 00:00:00)

	.Parameter QueryOptions
	options that are used to control the amount of data returned : None, Definitions, Agents, Workspaces, Controllers, Process, BatchedRequests, HistoricalBuilds, All, default = All

	.Parameter QueryOrder
	desired order of the builds returned : StartTimeAscending, StartTimeDescending, FinishTimeAscending, FinishTimeDescending, default = StartTimeAscending

	.Parameter Status
	build status filter : None, InProgress, Succeeded, PartiallySucceeded, Failed, Stopped, NotStarted, All, default = All

	.Example
	Get-TfsBuilds "https://tfs.myProjectsColUri.com/DefaultCollection" "myProj" "CI-myProject-Main"

	.Outputs
	PSObject containing TFS builds on success / null object on fail
	#>
	Function Get-TfsBuilds
	{
		[CmdletBinding()]
		Param (	[Parameter(Mandatory=$true,Position=0)][string]$TfsCollectionUri,
				[Parameter(Mandatory=$true,Position=1)][string]$TfsProjectName,
				[Parameter(Mandatory=$true,Position=2)][string]$TfsBuildName,
				[Parameter(Mandatory=$false,Position=3)][string]$BuildNumber="*",
				[Parameter(Mandatory=$false,Position=4)][string]$InformationTypes=$null,
				[Parameter(Mandatory=$false,Position=5)][DateTime]$MinFinishTime=0,
				[Parameter(Mandatory=$false,Position=6)][DateTime]$MaxFinishTime=0,
				[Parameter(Mandatory=$false,Position=7)][ValidateSet("None","Definitions","Agents","Workspaces","Controllers","Process","BatchedRequests","HistoricalBuilds","All")][string]$QueryOptions="None",
				[Parameter(Mandatory=$false,Position=8)][ValidateSet("StartTimeAscending","StartTimeDescending","FinishTimeAscending","FinishTimeDescending")][string]$QueryOrder="StartTimeAscending",
				[Parameter(Mandatory=$false,Position=9)][ValidateSet("None","InProgress","Succeeded","PartiallySucceeded","Failed","Stopped","NotStarted","All")][string]$Status="All" )

		Write-LogDebug "Start Get-TfsBuilds"

		try
		{
			$tfs = Get-TfsConnection $TfsCollectionUri

			if ($tfs.HasAuthenticated)
			{
				# create build specification
				$buildSpecification = $tfs.BuildServer.CreateBuildDetailSpec($TfsProjectName, $TfsBuildName)
				$buildSpecification.BuildNumber = $BuildNumber
				$buildSpecification.InformationTypes = $InformationTypes
				$buildSpecification.MinFinishTime = $MinFinishTime
				$buildSpecification.MaxFinishTime = $MaxFinishTime
				$buildSpecification.QueryOptions = $QueryOptions
				$buildSpecification.QueryOrder = $QueryOrder
				$buildSpecification.Status = $Status

				Write-LogObject $buildSpecification "getting build with following specification" -LineAfterTitle

				Write-LogVerbose "getting TFS build $TfsBuildName"

				$tfsBuilds = $tfs.BuildServer.QueryBuilds($buildSpecification)

				if ($tfsBuilds)
				{
					$tfsBuilds | Add-Member -NotePropertyName Tfs -NotePropertyValue $tfs
				}
				else
				{
					Throw "no build data for build name $TfsBuildName & build number : $BuildNumber"
				}
			}
			else
			{
				Throw "you must be authenticated to TFS projects collection to get builds data"
			}

			# trace Success
			Write-LogDebug "Success Get-TfsBuilds"
		}
		catch
		{
			$tfsBuilds = $null

			# log Error
			Write-LogError $($_.Exception) $($_.InvocationInfo)
		}

		return $tfsBuilds
	}
	

Compilation, code analysis, unit tests and code coverage data

Following examples start from predicate you already instanciate a tfsBuild, use previous example or Get-TfsBuilds function from Module-TFS.psm1.

Get build warnings and errors

Compilation and code analysis results are nested into build errors and build warnings.

To get build errrors, we use the GetBuildErrors method from InformationNodeConverters class in Microsoft.TeamFoundation.Build.Client namespace.

To get build warnings we use the GetBuildEWarnings method from InformationNodeConverters class in Microsoft.TeamFoundation.Build.Client namespace.

Both methods take a IBuildDetails interface as parameters (Get-TfsBuilds function return build as BuildDetails object) and return a BuildWarning or BuildError List.

Then, to separate compilation and code analysis results, we can sort them with the help of ErrorType or WarningType properties. (WarningType property is missing from MSDN !?)

Code

	$buildErrors = [Microsoft.TeamFoundation.Build.Client.InformationNodeConverters]::GetBuildErrors($tfsBuild)
	$buildWarnings = [Microsoft.TeamFoundation.Build.Client.InformationNodeConverters]::GetBuildWarnings($tfsBuild)

	$compilationErrors = $buildErrors | ? { $_.ErrorType -eq "Compilation" }
	$compilationWarnings = $buildWarnings | ? { $_.WarningType -eq "Compilation" }
	$codeAnalysisErrors = $buildErrors | ? { $_.ErrorType -eq "StaticAnalysis" }
	$codeAnalysisWarnings = $buildWarnings | ? { $_.WarningType -eq "StaticAnalysis" }
	

Get unit tests and code coverage

The TestManagementService service is needed to query Unit Test and code coverage, we previously have seen how to get it.

From this service, we can get a ITestManagementTeamProject interface from the method GetTeamProject using project name as parameters.

The ITestManagementTeamProject interface own the TestRuns and CoverageAnalysisManager helper objects.

To get unit tests results we use the method TestRuns.ByBuild, to get code coverage results we use the method CoverageAnalysisManager.QueryBuildCoverage. Both method take build uri as parameter (uri is part of a BuildDetails object), QueryBuilCoverage also use a CoverageQueryFlags enumeration as flag to set the amount of data returned.

Code

	$managementService =  $tfsBuilds.Tfs.TestManagementService.GetTeamProject("yourProjectName")

	$unitTests = $managementService.TestRuns.ByBuild($tfsBuild.Uri)

	$codeCoverage = $managementService.CoverageAnalysisManager.QueryBuildCoverage($tfsBuild.Uri,[Microsoft.TeamFoundation.TestManagement.Client.CoverageQueryFlags]::BlockData -bor [Microsoft.TeamFoundation.TestManagement.Client.CoverageQueryFlags]::Functions -bor [Microsoft.TeamFoundation.TestManagement.Client.CoverageQueryFlags]::Modules)
	

Function : get build details

Previous functions were really fine for generic use, that one is more personnal you should write your own, for example i chose to nest compilation errors and warnings in the same object, same for code analysis errors and warnings, adding ErrorLevel property to differentiate them, it is a questionable choice that suits my use, I also set a few property I find usefull but you may not.

Logging functions are provided from Module-IO.psm1, Get-TfsBuilds is taken from previous chapter of current article, ressources are on GITHUB.

	<#
	.Synopsis
	get TFS build informations

	.Description
	get build details (build errors, build warnings, UnitTest and code coverage results) from projets collection URI, project name and build number

	.Parameter TfsCollectionUri
	TFS team projects collection Uri

	.Parameter TfsProjectName
	TFS project name as string

	.Parameter TfsBuildNumber
	TFS build number as string, format : buildName_buildDate_iteration

	.Example
	Get-TfsBuildDetails "https://tfs.myProjectsColUri.com/DefaultCollection" "CI-myProject-Main" "CI-myProject-Main_20160306.1" -FullDetails

	.Outputs
	PSObject containing TFS build details on success / null object on fail
	#>
	Function Get-TfsBuildDetails
	{
		[CmdletBinding()]
		Param (	[Parameter(Mandatory=$true,Position=0)][string]$TfsCollectionUri,
				[Parameter(Mandatory=$true,Position=1)][string]$TfsProjectName,
				[Parameter(Mandatory=$true,Position=2)][string]$TfsBuildNumber )

		Write-LogDebug "Start Get-TfsBuildDetails"

		try
		{
			Write-LogHost "getting build details for $tfsBuildNumber" -LineBefore

			$tfsBuildName = $tfsBuildNumber -replace '^(.*)_(\d{8}?)\.(\d*)$', '$1'

			$tfsBuilds = Get-TfsBuilds $TfsCollectionUri $TfsProjectName $tfsBuildName -BuildNumber $tfsBuildNumber -InformationTypes "*"

			if ($tfsBuilds)
			{
				$tfsBuild = $tfsBuilds.Builds | Select-Object -First 1

				$tfsBuild | Add-Member -NotePropertyName Tfs -NotePropertyValue $($tfsBuilds.Tfs)
				$tfsBuild | Add-Member -NotePropertyName BuildDuration -NotePropertyValue $($tfsBuild.FinishTime - $tfsBuild.StartTime)

				# get compilation and code analysis errors and warnings
				Write-LogVerbose "getting build errors and warning"
				$buildErrors = [Microsoft.TeamFoundation.Build.Client.InformationNodeConverters]::GetBuildErrors($tfsBuild)
				$buildWarnings = [Microsoft.TeamFoundation.Build.Client.InformationNodeConverters]::GetBuildWarnings($tfsBuild)

				$compilationErrors = $buildErrors | ? { $_.ErrorType -eq "Compilation" }
				$compilationWarnings = $buildWarnings | ? { $_.WarningType -eq "Compilation" }
				$codeAnalysisErrors = $buildErrors | ? { $_.ErrorType -eq "StaticAnalysis" }
				$codeAnalysisWarnings = $buildWarnings | ? { $_.WarningType -eq "StaticAnalysis" } 

				# get compilation info
				Write-LogVerbose "getting build compilation results"

				$compilation = @()
				foreach ($compilationMessage in $compilationErrors)
				{
					$compilationMessage | Add-Member -NotePropertyName ErrorLevel -NotePropertyValue "Error"
					$compilation += $compilationMessage
				}
				foreach ($compilationMessage in $compilationWarnings)
				{
					$compilationMessage | Add-Member -NotePropertyName ErrorLevel -NotePropertyValue "Warning"
					$compilation += $compilationMessage
				}

				$compilationIsSuccessFull = $(($compilation | ? { $_.ErrorLevel -eq "Error" }).Count -le 0)

				$tfsBuild | Add-Member -NotePropertyName CompilationSucceed -NotePropertyValue $compilationIsSuccessFull
				$tfsBuild | Add-Member -NotePropertyName Compilation -NotePropertyValue $compilation

				# get code analysis info
				Write-LogVerbose "getting build code analysis results"

				$codeAnalysis = @()
				foreach ($codeAnalysisMessage in $codeAnalysisErrors)
				{
					$codeAnalysisMessage | Add-Member -NotePropertyName ErrorLevel -NotePropertyValue "Error"
					$codeAnalysis += $codeAnalysisMessage
				}
				foreach ($codeAnalysisMessage in $codeAnalysisWarnings)
				{
					$codeAnalysisMessage | Add-Member -NotePropertyName ErrorLevel -NotePropertyValue "Warning"
					$codeAnalysis += $codeAnalysisMessage
				}    

				$codeAnalysisIsSuccessFull = $(($codeAnalysis | ? { $_.ErrorLevel -eq "Error" }).Count -le 0)

				$tfsBuild | Add-Member -NotePropertyName CodeAnalysisSucceed -NotePropertyValue $codeAnalysisIsSuccessFull
				$tfsBuild | Add-Member -NotePropertyName CodeAnalysis -NotePropertyValue $codeAnalysis

				# get unit tests info
				Write-LogVerbose "getting build unit tests results"

				$managementService =  $tfsBuild.Tfs.TestManagementService.GetTeamProject($TfsProjectName)
				$unitTests = $managementService.TestRuns.ByBuild($tfsBuild.Uri)

				$testAreSuccessFull = $true
				foreach ($testsSet in $tfsBuild.UnitTest)
				{
					if ([int]($testsSet.Statistics.FailedTests) -gt 0) { $testAreSuccessFull = $false }
				}

				$tfsBuild | Add-Member -NotePropertyName UnitTestsSucceed -NotePropertyValue $testAreSuccessFull
				$tfsBuild | Add-Member -NotePropertyName UnitTests -NotePropertyValue $unitTests

				# get code coverage info
				Write-LogVerbose "getting build code coverage"

				$codeCoverage = $managementService.CoverageAnalysisManager.QueryBuildCoverage($tfsBuild.Uri,[Microsoft.TeamFoundation.TestManagement.Client.CoverageQueryFlags]::BlockData -bor [Microsoft.TeamFoundation.TestManagement.Client.CoverageQueryFlags]::Functions -bor [Microsoft.TeamFoundation.TestManagement.Client.CoverageQueryFlags]::Modules)

				$tfsBuild | Add-Member -NotePropertyName CodeCoverage -NotePropertyValue $codeCoverage
			}
			else
			{
				Throw "no build data for $tfsBuildNumber"
			}

			# trace Success
			Write-LogDebug "Success Get-TfsBuildDetails"
		}
		catch
		{
			$tfsBuild = $null

			# log Error
			Write-LogError $($_.Exception) $($_.InvocationInfo)
		}

		return $tfsBuild
	}
	

Function : get last build details

As we often need to query the last occurency of a build, this wrapper looks helpfull.

Logging functions are provided from Module-IO.psm1, Get-TfsBuilds is taken from previous chapter of current article, Get-TfsBuildDetails is the exemple above, ressources are on GITHUB.

	<#
	.Synopsis
	Return last executed TFS builds informations, from build name

	.Description
	First perfom a fast search on all builds definition to get last build number from build name, then perform a detailled search on that build number to return build errors, build warnings, UnitTest and code coverage results

	.Parameter TfsCollectionUri
	TFS team projects collection Uri

	.Parameter TfsProjectName
	TFS project name as string

	.Parameter TfsBuildName
	Name of the build to return

	.Parameter BuildStatusExclusion
	Array containing build status to exclude, from : InProgress, Succeeded, PartiallySucceeded, Failed, Stopped, NotStarted, default = @("InProgress","NotStarted","Stopped")

	.Parameter RecentBuild
	Switch, set to true if you known last build is less than 24h age, to speed up build search process

	.Example
	Get-TfsLastBuildDetails "https://tfs.myProjectsColUri.com/DefaultCollection" "myProj" "CI-myProject-Main"

	.Outputs
	PSObject containing TFS build details on success / null object on fail
	#>
	Function Get-TfsLastBuildDetails
	{
		[CmdletBinding()]
		Param (	[Parameter(Mandatory=$true,Position=0)][string]$TfsCollectionUri,
				[Parameter(Mandatory=$true,Position=1)][string]$TfsProjectName,
				[Parameter(Mandatory=$true,Position=2)][string]$TfsBuildName,
				[Parameter(Mandatory=$false,Position=3)][Validateset("InProgress","Succeeded","PartiallySucceeded","Failed","Stopped","NotStarted")][array]$BuildStatusExclusion=@("InProgress","NotStarted","Stopped"),
				[Parameter(Mandatory=$false,Position=4)][switch]$RecentBuild )

		Write-LogDebug "Start Get-TfsLastBuildDetails"

		try
		{
			if ($RecentBuild)
			{
				$yesterday = (Get-Date).AddDays(-1)
				$tfsBuilds = Get-TfsBuilds $TfsCollectionUri $TfsProjectName $TfsBuildName -QueryOrder "StartTimeDescending" -MinFinishTime $yesterday
			}
			else
			{
				$tfsBuilds = Get-TfsBuilds $TfsCollectionUri $TfsProjectName $TfsBuildName -QueryOrder "StartTimeDescending"
			}

			$tfsLastBuildNumber = ($tfsBuilds.Builds | ? { $_.Status -notin $BuildStatusExclusion } | Select-Object -First 1).BuildNumber

			$tfsBuildDetails = Get-TfsBuildDetails $TfsCollectionUri $TfsProjectName $tfsLastBuildNumber

			# trace Successs
			Write-LogDebug "Success Get-TfsLastBuildDetails"
		}
		catch
		{
			$tfsBuildDetails = $null

			# log Error
			Write-LogError $($_.Exception) $($_.InvocationInfo)
		}

		return $tfsBuildDetails
	}
	
Advertisements

2 thoughts on “Query TFS build using Powershell

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s