├── .gitignore ├── TFS ├── TFS.psd1 ├── TFS.Project.ps1xml ├── TFS.Definition.ps1xml ├── TFS.Build.Format.ps1xml ├── TFS.GitRepository.ps1xml ├── TFS.Definition.History.ps1xml ├── Get-TFSProcesses.ps1 ├── Get-TFSQueues.ps1 ├── Remove-TFSBuild.ps1 ├── Get-TFSBuildArtifacts.ps1 ├── Add-TFSBuildTag.ps1 ├── Remove-TFSGitRepository.ps1 ├── Remove-TFSProject.ps1 ├── Remove-TFSBuildTag.ps1 ├── Get-TFSProject.ps1 ├── New-TFSGitRepository.ps1 ├── TFS.psm1 ├── Get-TFSGitRepositories.ps1 ├── _globals.ps1 ├── New-TFSBuildDefinition.ps1 ├── Get-TFSStoredCredential.ps1 ├── Remove-TFSBuildDefinition.ps1 ├── Invoke-TFSBuild.ps1 ├── New-TFSCredential.ps1 ├── Get-TFSBuildDefinitions.ps1 ├── Get-TFSProjects.ps1 ├── Get-TFSBuildDefinitionHistory.ps1 ├── Update-TFSBuildDefinition.ps1 ├── New-TFSProject.ps1 ├── Get-TFSBuildLogs.ps1 ├── Get-TFSBuildDefinition.ps1 └── Get-TFSBuilds.ps1 ├── publish.ps1 ├── license.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /vars.ps1 2 | -------------------------------------------------------------------------------- /TFS/TFS.psd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majkinetor/TFS/HEAD/TFS/TFS.psd1 -------------------------------------------------------------------------------- /TFS/TFS.Project.ps1xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majkinetor/TFS/HEAD/TFS/TFS.Project.ps1xml -------------------------------------------------------------------------------- /TFS/TFS.Definition.ps1xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majkinetor/TFS/HEAD/TFS/TFS.Definition.ps1xml -------------------------------------------------------------------------------- /TFS/TFS.Build.Format.ps1xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majkinetor/TFS/HEAD/TFS/TFS.Build.Format.ps1xml -------------------------------------------------------------------------------- /TFS/TFS.GitRepository.ps1xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majkinetor/TFS/HEAD/TFS/TFS.GitRepository.ps1xml -------------------------------------------------------------------------------- /TFS/TFS.Definition.History.ps1xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majkinetor/TFS/HEAD/TFS/TFS.Definition.History.ps1xml -------------------------------------------------------------------------------- /TFS/Get-TFSProcesses.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 26-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get the TFS processes 7 | #> 8 | function Get-TFSProcesses { 9 | [CmdletBinding()] 10 | param( 11 | ) 12 | check_credential 13 | 14 | $uri = "$collection_uri/_apis/process/processes?api-version=" + $global:tfs.api_version 15 | Write-Verbose "URI: $uri" 16 | 17 | $params = @{ Uri = $uri; Method = 'Get'} 18 | $r = invoke_rest $params 19 | $r.value 20 | } 21 | -------------------------------------------------------------------------------- /TFS/Get-TFSQueues.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 26-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get the TFS build queues 7 | #> 8 | function Get-TFSQueues { 9 | [CmdletBinding()] 10 | param () 11 | check_credential 12 | 13 | $uri = "$collection_uri/_apis/build/queues?api-version=" + $global:tfs.api_version 14 | Write-Verbose "URI: $uri" 15 | 16 | $params = @{ Uri = $uri; Method = 'Get'} 17 | $r = invoke_rest $params 18 | $r.value | select id, name 19 | } 20 | -------------------------------------------------------------------------------- /publish.ps1: -------------------------------------------------------------------------------- 1 | function test-var() { 2 | $input | % { if (!(Test-Path Env:$_)) {throw "Environment Variable $_ must be set"} } 3 | } 4 | 5 | function Publish-PSGallery() { 6 | Write-Host 'Publishing to Powershell Gallery' 7 | 8 | 'NuGet_ApiKey' | test-var 9 | $params = @{ 10 | Path = "$PSScriptRoot\TFS" 11 | NuGetApiKey = $Env:NuGet_ApiKey 12 | } 13 | Publish-Module @params 14 | } 15 | 16 | if (Test-Path $PSScriptRoot/vars.ps1) { . $PSScriptRoot/vars.ps1 } 17 | Publish-PSGallery 18 | -------------------------------------------------------------------------------- /TFS/Remove-TFSBuild.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 26-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Remove the TFS build 7 | #> 8 | function Remove-TFSBuild { 9 | [CmdletBinding()] 10 | param ( 11 | #Build ID 12 | [int] $Id 13 | ) 14 | check_credential 15 | 16 | $uri = "$proj_uri/_apis/build/builds/$($Id)?api-version=" + $global:tfs.api_version 17 | Write-Verbose "URI: $uri" 18 | 19 | $params = @{ Uri = $uri; Method = 'Delete'} 20 | invoke_rest $params | Out-Null 21 | } 22 | -------------------------------------------------------------------------------- /TFS/Get-TFSBuildArtifacts.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 14-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get the build artifacts 7 | #> 8 | function Get-TFSBuildArtifacts{ 9 | [CmdletBinding()] 10 | param( 11 | #Build id 12 | [int]$Id 13 | ) 14 | check_credential 15 | 16 | $uri = "$proj_uri/_apis/build/builds/$Id/artifacts?api-version=" + $global:tfs.api_version 17 | Write-Verbose "URI: $uri" 18 | 19 | $params = @{ Uri = $uri; Method = 'Get'} 20 | $r = invoke_rest $params 21 | $r.value 22 | } 23 | -------------------------------------------------------------------------------- /TFS/Add-TFSBuildTag.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 26-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Add tag to TFS build 7 | #> 8 | function Add-TFSBuildTag{ 9 | [CmdletBinding()] 10 | param( 11 | #Build ID 12 | [int]$Id, 13 | 14 | #Tag to add to the build 15 | [string]$Tag 16 | ) 17 | check_credential 18 | 19 | $uri = "$proj_uri/_apis/build/builds/$Id/tags/$($Tag)?api-version=" + $global:tfs.api_version 20 | Write-Verbose "URI: $uri" 21 | 22 | $params = @{ Uri = $uri; Method = 'Put'} 23 | $r = invoke_rest $params 24 | $r.Value 25 | } 26 | 27 | sal btag Add-TFSBuildTag 28 | -------------------------------------------------------------------------------- /TFS/Remove-TFSGitRepository.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 26-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get the TFS Git repositories 7 | #> 8 | function Remove-TFSGitRepository { 9 | [CmdletBinding()] 10 | param ( 11 | #Name of the repository 12 | [string] $Name 13 | ) 14 | check_credential 15 | 16 | $id = Get-TFSGitRepositories | ? name -eq $Name | % id 17 | Write-Verbose "Repository id: $id" 18 | 19 | $uri = "$proj_uri/_apis/git/repositories/$($id)?api-version=" + $tfs.api_version 20 | Write-Verbose "URI: $uri" 21 | 22 | $params = @{ Uri = $uri; Method = 'Delete' } 23 | invoke_rest $params 24 | } 25 | -------------------------------------------------------------------------------- /TFS/Remove-TFSProject.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 25-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get the TFS project 7 | #> 8 | function Remove-TFSProject { 9 | [CmdletBinding()] 10 | param ( 11 | #Id or name of the project 12 | [string]$Id 13 | ) 14 | check_credential 15 | 16 | if ($Id.Length -ne 36) { $Id = Get-TFSProject $Id | % id } 17 | if ($Id -eq $null) { throw "Can't find project with that name or id: '$Id'" } 18 | Write-Verbose "Project id: $Id" 19 | 20 | $uri = "$collection_uri/_apis/projects/$($Id)?api-version=" + $global:tfs.api_version 21 | Write-Verbose "URI: $uri" 22 | 23 | $params = @{ Uri = $uri; Method = 'Delete' } 24 | invoke_rest $params 25 | } 26 | -------------------------------------------------------------------------------- /TFS/Remove-TFSBuildTag.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 26-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Remove tag from TFS build 7 | .EXAMPLE 8 | Remove-TFSBuildTag 220 production 9 | 10 | Remove tag 'production' from the build with ID 220. 11 | #> 12 | function Remove-TFSBuildTag{ 13 | [CmdletBinding()] 14 | param( 15 | #Build ID 16 | [int]$Id, 17 | 18 | #Tag to remove from the build 19 | [string]$Tag 20 | ) 21 | check_credential 22 | 23 | $uri = "$proj_uri/_apis/build/builds/$Id/tags/$($Tag)?api-version=" + $global:tfs.api_version 24 | Write-Verbose "URI: $uri" 25 | 26 | $params = @{ Uri = $uri; Method = 'Delete'} 27 | $r = invoke_rest $params 28 | $r.Value 29 | } 30 | -------------------------------------------------------------------------------- /TFS/Get-TFSProject.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 26-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get the TFS project details 7 | .EXAMPLE 8 | Get-TFSProject ProjectXYZ 9 | 10 | Get the project 'ProjectXYZ' by its name 11 | .EXAMPLE 12 | Get-TFSProject 1 13 | 14 | Get the project by its TFS numeric id 15 | #> 16 | function Get-TFSProject { 17 | [CmdletBinding()] 18 | param( 19 | #Id or name of the project 20 | [ValidateNotNullOrEmpty()] 21 | [string]$Id 22 | ) 23 | check_credential 24 | 25 | $uri = "$collection_uri/_apis/projects/$($Id)?includeCapabilities=true&api-version=" + $global:tfs.api_version 26 | Write-Verbose "URI: $uri" 27 | 28 | $params = @{ Uri = $uri; Method = 'Get'} 29 | invoke_rest $params 30 | } 31 | -------------------------------------------------------------------------------- /TFS/New-TFSGitRepository.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 14-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get the TFS Git repositories 7 | #> 8 | function New-TFSGitRepository { 9 | [CmdletBinding()] 10 | param ( 11 | #Name of the repository 12 | [string] $Name 13 | ) 14 | check_credential 15 | 16 | $uri = "$proj_uri/_apis/git/repositories?api-version=" + $tfs.api_version 17 | Write-Verbose "URI: $uri" 18 | 19 | $pid = Get-TFSProject $global:tfs.project | % id 20 | Write-Verbose "Project id: $pid" 21 | 22 | $body = @{ name = $Name; project = @{ id = $pid } } 23 | $body = $body | ConvertTo-Json 24 | Write-Verbose $body 25 | 26 | $params = @{ Uri = $uri; Method = 'Post'; ContentType = 'application/json'; Body = $body} 27 | invoke_rest $params 28 | } 29 | -------------------------------------------------------------------------------- /TFS/TFS.psm1: -------------------------------------------------------------------------------- 1 | # Export functions that start with capital letter, others are private 2 | # Include file names that start with capital letters, ignore other 3 | 4 | $pre = ls Function:\* 5 | ls "$PSScriptRoot\*.ps1" | ? { $_.Name -cmatch '^[A-Z]+' } | % { . $_ } 6 | $post = ls Function:\* 7 | $funcs = compare $pre $post | select -Expand InputObject | select -Expand Name 8 | $funcs | ? { $_ -cmatch '^[A-Z]+'} | % { Export-ModuleMember -Function $_ } 9 | 10 | function d ( $t, $f ) { if ($t) {$t} else {$f} } 11 | $global:tfs = [ordered]@{ 12 | root_url = $global:tfs.root_url 13 | collection = d $global:tfs.collection 'DefaultCollection' 14 | project = $global:tfs.project 15 | api_version = d $global:tfs.api_version '2.0' 16 | credential = $global:tfs.credential 17 | } 18 | 19 | Export-ModuleMember -Alias * 20 | . "$PSScriptRoot\_globals.ps1" 21 | -------------------------------------------------------------------------------- /TFS/Get-TFSGitRepositories.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 26-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get the TFS Git repositories 7 | #> 8 | function Get-TFSGitRepositories { 9 | [CmdletBinding()] 10 | param () 11 | check_credential 12 | 13 | $uri = "$proj_uri/_apis/git/repositories?api-version=" + $tfs.api_version 14 | Write-Verbose "URI: $uri" 15 | 16 | $params = @{ Uri = $uri; Method = 'Get'} 17 | $r = invoke_rest $params 18 | 19 | $list = @() 20 | foreach ($r in $r.value) { 21 | $b = [pscustomobject]@{ 22 | Raw = $r 23 | Id = $r.id 24 | Name = $r.name 25 | Project = $r.project.name 26 | } 27 | $b.PSObject.TypeNames.Insert(0,'TFS.GitRepository') 28 | $list += $b 29 | } 30 | 31 | $list 32 | } 33 | -------------------------------------------------------------------------------- /TFS/_globals.ps1: -------------------------------------------------------------------------------- 1 | $collection_uri = "{0}/{1}" -f $global:tfs.root_url, $global:tfs.collection 2 | $proj_uri = "{0}/{1}" -f $collection_uri, $global:tfs.project 3 | 4 | function check_credential() { 5 | [CmdletBinding()] 6 | param() 7 | 8 | if ($global:tfs.Credential) { 9 | Write-Verbose "TFS Credential: $($global:tfs.Credential.UserName)" 10 | return 11 | } 12 | 13 | Write-Verbose 'No credentials specified, trying Windows Credential Manager' 14 | $global:tfs.Credential = Get-TFSStoredCredential 15 | } 16 | 17 | function invoke_rest($Params) { 18 | $Params.Credential = $global:tfs.credential 19 | 20 | try { 21 | Invoke-RestMethod @Params 22 | } catch { 23 | $err = $_ 24 | try { $err = $_ | ConvertFrom-Json } catch { throw $err } 25 | $err = ($err | fl * | Out-String) -join '`n' 26 | Write-Error $err 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /TFS/New-TFSBuildDefinition.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 26-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Create/import build definition 7 | .EXAMPLE 8 | New-BuildDefinition -JsonFile BuildXYZ.json 9 | 10 | Create a new build definition using the data in the JSON file 'BuildXYZ.json' 11 | #> 12 | function New-TFSBuildDefinition { 13 | [CmdletBinding()] 14 | param ( 15 | #File that contains json description of the build 16 | [string] $JsonFile 17 | ) 18 | check_credential 19 | 20 | if (!(Test-Path $JsonFile)) {throw "File doesn't exist: $JsonFile" } 21 | 22 | $uri = "$proj_uri/_apis/build/definitions?api-version=" + $global:tfs.api_version 23 | Write-Verbose "URI: $uri" 24 | 25 | $body = gc $JsonFile -Raw -ea Stop 26 | $params = @{ Uri = $uri; Method = 'Post'; Body = $body; ContentType = 'application/json' } 27 | invoke_rest $params 28 | } 29 | -------------------------------------------------------------------------------- /TFS/Get-TFSStoredCredential.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 18-May-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get saved TFS credential from the Windows Credential Manager. If none is available, create and store one. 7 | #> 8 | function Get-TFSStoredCredential { 9 | [CmdletBinding()] 10 | param() 11 | 12 | if ($global:tfs.root_url -eq $null) { throw 'You must set $global:tfs.root_url in order to get stored credentials' } 13 | if (gmo -ListAvailable CredentialManager -ea 0) { 14 | $cm = $true 15 | try { 16 | Write-Verbose "Trying to get storred credentials for '$($global:tfs.root_url)'" 17 | $cred = Get-StoredCredential -Target $global:tfs.root_url 18 | } catch { 19 | if ($_.Exception.Message -ne 'CredRead failed with the error code 1168.') { throw $_ } 20 | } 21 | } 22 | 23 | if ($cred -eq $null) { $cred = New-TFSCredential } else { Write-Verbose 'Stored credentials retrieved' } 24 | 25 | $cred 26 | } 27 | -------------------------------------------------------------------------------- /TFS/Remove-TFSBuildDefinition.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 26-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Remove the TFS build definition 7 | .EXAMPLE 8 | defs | % { rmdef $_.Name } 9 | 10 | Remove all build definitions from the project. The example is using aliases 11 | #> 12 | function Remove-TFSBuildDefinition { 13 | [CmdletBinding()] 14 | param( 15 | #Build defintion id [int] or name [string] 16 | $Id 17 | ) 18 | check_credential 19 | 20 | if ( ![String]::IsNullOrEmpty($Id) -and ($Id.GetType() -eq [string]) ) { $Id = Get-TFSBuildDefinitions -Name $Id | % id } 21 | if ( [String]::IsNullOrEmpty($Id) ) { throw "Resource with that name doesn't exist" } 22 | Write-Verbose "Build definition id: $Id" 23 | 24 | $uri = "$proj_uri/_apis/build/definitions/$($Id)?api-version=" + $global:tfs.api_version 25 | Write-Verbose "URI: $uri" 26 | 27 | $params = @{ Uri = $uri; Method = 'Delete'} 28 | invoke_rest $params 29 | } 30 | 31 | sal rmdef Remove-TFSBuildDefinition 32 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Miodrag Milić 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /TFS/Invoke-TFSBuild.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 20-Oct-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Invoke the TFS build 7 | #> 8 | function Invoke-TFSBuild { 9 | [CmdletBinding()] 10 | param( 11 | #Build defintion id [int] or name [string] 12 | $Id, 13 | #Optional source branch 14 | [string] $sourceBranch = '' 15 | ) 16 | check_credential 17 | 18 | if ( ![String]::IsNullOrEmpty($Id) -and ($Id.GetType() -eq [string]) ) { $Id = Get-TFSBuildDefinitions -Name $Id | % id } 19 | if ( [String]::IsNullOrEmpty($Id) ) { throw "Resource with that name doesn't exist" } 20 | Write-Verbose "Build definition id: '$Id'" 21 | 22 | $uri = "$proj_uri/_apis/build/builds?api-version=" + $global:tfs.api_version 23 | Write-Verbose "URI: $uri" 24 | 25 | $body = @{ definition=@{ id = $Id } } 26 | if ( $sourceBranch -ne "" ) { $body.sourceBranch = $sourceBranch } 27 | $body = $body | ConvertTo-Json 28 | $params = @{ Uri = $uri; Method = 'Post'; Body = $body; ContentType = 'application/json' } 29 | $r = invoke_rest $params 30 | } 31 | 32 | sal build Invoke-TFSBuild 33 | -------------------------------------------------------------------------------- /TFS/New-TFSCredential.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 18-May-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Create and optionaly store the TFS credentials 7 | #> 8 | function New-TFSCredential { 9 | [CmdletBinding()] 10 | param( 11 | # TFS credential 12 | [PSCredential] $Credential 13 | ) 14 | 15 | Write-Verbose "New TFS credentials for '$($global:tfs.root_url)'" 16 | if ($Credential -eq $null) { $Credential = Get-Credential } 17 | if ($Credential -eq $null) { Write-Warning 'Aborted'; return } 18 | 19 | if (!(gmo -ListAvailable CredentialManager -ea 0)) { Write-Warning 'CredentialManager module is not available'; return $Credential } 20 | 21 | if (($global:tfs.root_url -eq '') -or ($global:tfs.root_url -eq $null)) { throw 'You must set $global:tfs.root_url in order to store credentials' } 22 | 23 | Write-Verbose "Storing credential for target '$($global:tfs.root_url)'" 24 | New-StoredCredential -Target $global:tfs.root_url -UserName $Credential.UserName -Password $Credential.GetNetworkCredential().Password -Persist LocalMachine | Out-Null 25 | 26 | return $Credential 27 | } 28 | -------------------------------------------------------------------------------- /TFS/Get-TFSBuildDefinitions.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 26-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get the TFS build definitions 7 | #> 8 | function Get-TFSBuildDefinitions { 9 | [CmdletBinding()] 10 | param ( 11 | #Filters to definitions whose names start with this value. Globs supported. 12 | [string]$Name 13 | ) 14 | check_credential 15 | 16 | if ($Name) { $q_name = 'name=' + $Name + '&' } 17 | $uri = "$proj_uri/_apis/build/definitions?$($q_name)api-version=" + $global:tfs.api_version 18 | Write-Verbose "URI: $uri" 19 | 20 | $params = @{ Uri = $uri; Method = 'Get'} 21 | $r = invoke_rest $params 22 | 23 | $list = @() 24 | foreach ($r in $r.value) { 25 | $b = [pscustomobject]@{ 26 | Raw = $r 27 | Id = $r.id 28 | Name = $r.name 29 | Revision = $r.revision 30 | Author = $r.authoredBy.displayname 31 | EditUrl = "$proj_uri/_build#definitionId=" + $r.id + "&_a=simple-process" 32 | } 33 | $b.PSObject.TypeNames.Insert(0,'TFS.Definition') 34 | $list += $b 35 | } 36 | 37 | $list 38 | } 39 | 40 | sal defs Get-TFSBuildDefinitions 41 | -------------------------------------------------------------------------------- /TFS/Get-TFSProjects.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 26-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get the list of team projects from the TFS server 7 | #> 8 | function Get-TFSProjects{ 9 | [CmdletBinding()] 10 | param( 11 | #Maxium number of team projects to return, by default 100 12 | [int] $Top=100, 13 | #Number of team projects to skip, by default 0 14 | [int] $Skip=0 15 | ) 16 | check_credential 17 | 18 | $q_top = '$top=' + $Top + '&' 19 | $q_skip = '$skip=' + $Skip + '&' 20 | $query_args = $q_top + $q_skip 21 | $uri = "$collection_uri/_apis/projects?$($query_args)api-version=" + $global:tfs.api_version 22 | Write-Verbose "URI: $uri" 23 | 24 | $params = @{ Uri = $uri; Method = 'Get'} 25 | $r = invoke_rest $params 26 | 27 | $list = @() 28 | foreach ($r in $r.value) { 29 | $b = [pscustomobject]@{ 30 | Raw = $r 31 | Name = $r.name 32 | Description = $r.description 33 | Revision = $r.revision 34 | Id = $r.id 35 | } 36 | $b.PSObject.TypeNames.Insert(0,'TFS.Project') 37 | $list += $b 38 | } 39 | 40 | $list 41 | } 42 | -------------------------------------------------------------------------------- /TFS/Get-TFSBuildDefinitionHistory.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 26-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get the build definition history 7 | #> 8 | function Get-TFSBuildDefinitionHistory{ 9 | [CmdletBinding()] 10 | param( 11 | # Build definition history id [int] or name [string] 12 | $Id 13 | ) 14 | check_credential 15 | 16 | 17 | if ( ![String]::IsNullOrEmpty($Id) -and ($Id.GetType() -eq [string]) ) { $Id = Get-TFSBuildDefinitions -Name $Id | % id } 18 | if ( [String]::IsNullOrEmpty($Id) ) { throw "Build definition with that name or id doesn't exist" } 19 | Write-Verbose "Build definition history id: $Id" 20 | 21 | $uri = "$proj_uri/_apis/build/definitions/$($Id)/revisions?api-version=" + $global:tfs.api_version 22 | Write-Verbose "URI: $uri" 23 | 24 | $params = @{ Uri = $uri; Method = 'Get' } 25 | $r = invoke_rest $params 26 | 27 | $list = @() 28 | foreach ($r in $r.value) { 29 | $b = [pscustomobject]@{ 30 | Raw = $r 31 | Revision = $r.revision 32 | ChangeType = $r.changeType 33 | ChangedDate = get-date $r.changedDate 34 | ChangedBy = $r.changedBy.displayName 35 | Comment = $r.comment 36 | } 37 | $b.PSObject.TypeNames.Insert(0,'TFS.Definition.History') 38 | $list += $b 39 | } 40 | $list | sort revision -desc 41 | } 42 | -------------------------------------------------------------------------------- /TFS/Update-TFSBuildDefinition.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 26-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Update build definition 7 | .EXAMPLE 8 | Update-TFSBuildDefinition -JsonFile BuildXYZ.json 9 | 10 | Update the build definition (create a new revision) using the data in the JSON file 'BuildXYZ'. 11 | .NOTES 12 | Build definition property "revision" must point to the latest one in order for import to succeed. 13 | For this reason revision in the JSON file is ignored and remote revision is used instead. 14 | #> 15 | function Update-TFSBuildDefinition { 16 | [CmdletBinding()] 17 | param ( 18 | [string] $JsonFile 19 | ) 20 | check_credential 21 | 22 | if (!(Test-Path $JsonFile)) {throw "File doesn't exist: $JsonFile" } 23 | $json = gc $JsonFile -ea Stop | ConvertFrom-Json 24 | 25 | #Don't use id from the jsonfile, find by name on TFS 26 | $remote = Get-TFSBuildDefinition $json.name 27 | $json.id = $remote.id 28 | $json.revision = $remote.revision 29 | Write-Verbose "Using remote build definition attributes: id $($json.id), revision $($json.revision)" 30 | 31 | $uri = "$proj_uri/_apis/build/definitions/$($json.id)?revision=$($remote.revision)&api-version=" + $global:tfs.api_version 32 | Write-Verbose "URI: $uri" 33 | 34 | $body = $json | ConvertTo-Json -Depth 100 35 | $params = @{ Uri = $uri; Method = 'Put'; Body = $body; ContentType = 'application/json' } 36 | invoke_rest $params 37 | } 38 | -------------------------------------------------------------------------------- /TFS/New-TFSProject.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 26-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Create new TFS project 7 | .EXAMPLE 8 | New-TFSProject -Name Test -Description 'Test project' -ProcessTemplate Scrum 9 | 10 | Create a new TFS team project with given name and description and use Scrum process template. 11 | #> 12 | function New-TFSProject { 13 | [CmdletBinding()] 14 | param( 15 | #Name for the project 16 | [string] $Name, 17 | #Description for the project 18 | [string] $Description, 19 | #Version control type for the project 20 | [ValidateSet( 'Git', 'Tfvc')] 21 | [string] $SourceControlType='Git', 22 | #Software development schema for the project 23 | [string] $ProcessTemplate = 'Agile' 24 | ) 25 | check_credential 26 | 27 | $uri = "$collection_uri/_apis/projects/?api-version=" + $global:tfs.api_version 28 | Write-Verbose "URI: $uri" 29 | 30 | $templateId = Get-TFSProcesses | ? name -eq $ProcessTemplate | % id 31 | if (!$templateId) { throw "No template exists with name: '$ProcessTemplate'" } 32 | Write-Verbose "Template id for '$ProcessTemplate': $templateId" 33 | 34 | $body = @{ 35 | name = $Name 36 | description = $Description 37 | capabilities = @{ 38 | processTemplate = @{ templateTypeId = $templateId } 39 | versioncontrol = @{ sourceControlType = $SourceControlType } 40 | } 41 | } 42 | 43 | $body = $body | ConvertTo-Json 44 | $params = @{ Uri = $uri; Method = 'Post'; Body = $body; ContentType = 'application/json' } 45 | invoke_rest $params 46 | } 47 | -------------------------------------------------------------------------------- /TFS/Get-TFSBuildLogs.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 05-Aug-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get the unified build logs for the TFS build 7 | 8 | .EXAMPLE 9 | PS> Get-TFSBuildLogs 10 | 11 | Returns logs of the latest build 12 | 13 | .EXAMPLE 14 | PS> Get-TFSBuildLogs 250 15 | 16 | Returns logs of the build by id 17 | 18 | .EXAMPLE 19 | PS> Get-TFSBuilds -Definitions MyDefinition | select -First 1 | % BuildNumber | % { Get-TFSBuildLogs $_ } 20 | 21 | Return logs of the latest build for given build definition 22 | #> 23 | function Get-TFSBuildLogs{ 24 | [CmdletBinding()] 25 | param( 26 | #Id of the build, by default the latest build is used. 27 | [string]$Id 28 | ) 29 | check_credential 30 | 31 | if ($Id -eq '') { $Id = Get-TFSBuilds -Top 1 | %{ $_.Id } } 32 | if ($Id -eq $null) { throw "Can't find latest build or there are no builds" } 33 | Write-Verbose "Build id: $Id" 34 | 35 | $uri = "$proj_uri/_apis/build/builds/$Id/logs?api-version=" + $global:tfs.api_version 36 | Write-Verbose "Logs URI: $uri" 37 | 38 | $params = @{ Uri = $uri; Method = 'Get'} 39 | $r = invoke_rest $params 40 | 41 | $lines = @() 42 | $root_server_name = $global:tfs.root_url -split '/' | select -Index 2 43 | foreach ( $url in $r.value.url ) { 44 | #TFS might return non FQDM so its best to replace its server name with the one user specified 45 | $new_url = $url -split '/' 46 | $new_url[2] = $root_server_name 47 | $new_url = $new_url -join '/' 48 | 49 | Write-Verbose "Log URI: $new_url" 50 | $params = @{ Uri = $new_url; Method = 'Get'} 51 | $l = invoke_rest $params 52 | $lines += $l.value -replace '\..+?Z' 53 | $lines += "="*150 54 | } 55 | $lines 56 | } 57 | 58 | sal blogs Get-TFSBuildLogs 59 | -------------------------------------------------------------------------------- /TFS/Get-TFSBuildDefinition.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 27-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get the TFS build definition 7 | 8 | .EXAMPLE 9 | Get-TFSBuildDefinition Build1 10 | 11 | Get the TFS build definition by name. 12 | 13 | .EXAMPLE 14 | Get-TFSBuildDefinition Build1 -OutFile build1.json 15 | 16 | Exports the TFS build definition named Build1 to JSON file 'build1.json' in the current directory. 17 | 18 | .EXAMPLE 19 | Get-TFSBuildDefinition 5 -OutFile . 20 | 21 | Exports the build definition to json file in the current directory. '.' is a special value for the file 22 | to be automatically named as -.json in the current directory. 23 | .EXAMPLE 24 | defs | % { def $_.Id -OutFile . } 25 | 26 | Export all TFS build definitions on the project. The example is using aliases. 27 | #> 28 | function Get-TFSBuildDefinition{ 29 | [CmdletBinding()] 30 | param( 31 | #Build defintion id [int] or name [string] 32 | $Id=0, #without 0 API by default returns item with id=1... 33 | #Export the build to the specified JSON file 34 | [string]$OutFile, 35 | #Revision of the build to get 36 | [int]$Revision=0 37 | ) 38 | check_credential 39 | 40 | if ( ![String]::IsNullOrEmpty($Id) -and ($Id.GetType() -eq [string])) { $Id = Get-TFSBuildDefinitions -Name $Id | % id } 41 | if ( [String]::IsNullOrEmpty($Id) ) { throw "Resource with that name doesn't exist" } 42 | Write-Verbose "Build definition id: '$Id'" 43 | 44 | if ($Revision) { $rev = "revision=$Revision&" } 45 | $uri = "$proj_uri/_apis/build/definitions/$($Id)?$($rev)api-version=" + $global:tfs.api_version 46 | Write-Verbose "URI: $uri" 47 | 48 | $params = @{ Uri = $uri; Method = 'Get'} 49 | $r = invoke_rest $params 50 | if (!$OutFile) { return $r } 51 | 52 | if ($OutFile -eq '.') { $OutFile = "{0}-{1}.json" -f $global:tfs.project, $r.Name } 53 | $r | ConvertTo-Json -Depth 100 | Out-File $OutFile -Encoding UTF8 54 | } 55 | 56 | sal def Get-TFSBuildDefinition 57 | -------------------------------------------------------------------------------- /TFS/Get-TFSBuilds.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 26-Apr-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get the TFS build list 7 | .EXAMPLE 8 | Get-TFSBuilds 9 | 10 | Return last 10 builds 11 | .EXAMPLE 12 | builds -BuildNumber 80[0-4] 13 | 14 | Return builds 800 - 804 15 | 16 | .EXAMPLE 17 | builds -Definitions Def1, Def2 -MaxBuildsPerDefinition 2 18 | 19 | Return only 2 last builds for definition Def1 & Def 2 20 | 21 | .EXAMPLE 22 | Get-TFSBuilds -Tag production,v1 23 | 24 | Get only builds that are tagged with 'production' and 'v1' keyword 25 | #> 26 | function Get-TFSBuilds { 27 | [CmdletBinding()] 28 | param ( 29 | #Maxium number of latest builds to return, by default 10 30 | [int] $Top=10, 31 | #Return only builds with the given tags 32 | [string[]]$Tags, 33 | #Status filter 34 | [ValidateSet('inProgress', 'completed', 'cancelling', 'postponed', 'notStarted', 'all')] 35 | [string]$Status = 'all', 36 | #Results filter 37 | [ValidateSet('succeeded', 'partiallySucceeded', 'failed', 'canceled', 'all')] 38 | [string]$Result = 'all', 39 | #Definitions filter 40 | [string[]]$Definitions, 41 | #The maximum number of builds to retrieve for each definition (default 5). This is only valid when definitions is also specified. 42 | [int]$MaxBuildsPerDefinition = 5, 43 | #Builds requested by this user, SAM account name 44 | [string]$RequestedFor, 45 | #Filters to builds with build numbers that start with this value. Globs supported 46 | [string]$BuildNumber, 47 | #Builds that finished after this time 48 | [datetime]$MinFinishTime, 49 | #Builds that finished before this time 50 | [datetime]$MaxFinishTime, 51 | #A comma-delimited list of extended properties to retrieve 52 | [string]$Properties 53 | 54 | ) 55 | check_credential 56 | 57 | $q_top = '$top=' + $Top + '&' 58 | if ($Result -ne 'all') { $q_result = 'resultFilter=' + $Result + '&' } 59 | if ($Status -ne 'all') { $q_status = 'statusFilter=' + $Status + '&' } 60 | if ($Tags) { $q_tag = 'tagFilters=' + ($Tags -join ',') + '&' } 61 | if ($Definitions) { 62 | $Definitions | % { $q_definitions += ',' + (Get-TFSBuildDefinition $_).id } 63 | $q_definitions = 'definitions=' + $q_definitions.Substring(1) + '&' 64 | $q_definitions += 'maxBuildsPerDefinition=' + $MaxBuildsPerDefinition + '&' 65 | } 66 | if ($RequestedFor) { $q_requestedFor = 'requestedFor=' + $RequestedFor + '&' } 67 | if ($BuildNumber) { $q_buildNumber = 'buildNumber=' + $BuildNumber + '&' } 68 | if ($MinFinishTime) { $q_minFinishTime = 'minFinishTime=' + (Get-Date $MinFinishTime).ToUniversalTime().ToString('s') + '&' } 69 | if ($MaxFinishTime) { $q_maxFinishTime = 'maxFinishTime=' + (Get-Date $MaxFinishTime).ToUniversalTime().ToString('s') + '&' } 70 | if ($Properties) { $q_properties = 'properties=' + ($Propeties -join ',') + '&' } 71 | 72 | $query_args = $q_top + $q_tag + $q_status + $q_result + $q_definitions + 73 | $q_requestedFor + $q_buildNumber + $q_minFinishTime + $q_maxFinishTime + $q_properties 74 | $uri = "$proj_uri/_apis/build/builds?$($query_args)api-version=" + $global:tfs.api_version 75 | Write-Verbose "URI: $uri" 76 | 77 | $params = @{ Uri = $uri; Method = 'Get'} 78 | $r = invoke_rest $params 79 | 80 | $list = @() 81 | foreach ($r in $r.value) { 82 | $b = [pscustomobject]@{ 83 | Raw = $r 84 | Id = $r.id 85 | BuildNumber = $r.buildNumber 86 | Result = $r.result 87 | Definition = $r.definition.name 88 | StartTime = get-date $r.startTime 89 | FinishTime = if ($r.finishTime) { get-date $r.finishTime } else {} 90 | Duration = if ($r.finishTime) { [math]::round( ((get-date $r.finishTime) - (get-date $r.startTime)).TotalMinutes, 1) } else {} 91 | Tags = $r.tags -split ' ' 92 | 93 | } 94 | $b.PSObject.TypeNames.Insert(0,'TFS.Build') 95 | $list += $b 96 | } 97 | 98 | $list 99 | } 100 | 101 | sal builds Get-TFSBuilds 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![](https://img.shields.io/badge/PowerShell%20Gallery-TFS-blue.svg)](https://www.powershellgallery.com/packages/tfs) 2 | 3 | TFS 4 | === 5 | 6 | This is Powershell module to communicate with [Team Foundation Server](https://www.visualstudio.com/en-us/products/tfs-overview-vs.aspx) 2015 via its [REST interface](https://www.visualstudio.com/integrate/get-started/rest/basics). It provides commands which allow you to create, update, export and import build definitions, get build logs, projects, repositories etc. The goal of the module is to be able to automate TFS setup and put its configuration on the project repository. 7 | 8 | The module is tested with Team Foundation Server Update 2. 9 | 10 | Installation 11 | ============ 12 | 13 | On Windows 10 or Powershell 5+ use: `Install-Module TFS`. 14 | 15 | To install manually, copy the module to the one of the directories listed in `$Env:PSModulePath`. 16 | 17 | Configuration 18 | ============= 19 | 20 | Module uses global variable `$tfs` for its configuration: 21 | 22 | $global:tfs = @{ 23 | root_url = 'http://tfs015:8080/tfs' 24 | collection = 'DefaultCollection' 25 | project = 'ProjectXYZ' 26 | } 27 | 28 | Some attributes will take defaults if you don't specify them: 29 | 30 | $tfs.collection = 'DefaultCollection' 31 | $tfs.api_version = '2.0' 32 | 33 | If you need to work constantly on a single project put this setting in your `$PROFILE`. To manage multiple projects or collections or TFS servers you could create multiple functions for each scenario that each set `$global:tfs` in its own way, for example: 34 | 35 | function set_tfs_test() { 36 | $global:tfs = @{ ... } 37 | } 38 | 39 | Then, prior to calling any module function run `set_tfs_test`. 40 | 41 | TFS Credentials 42 | --------------- 43 | 44 | Module keeps TFS credential in the `$tfs.Credentials`. If not specified you will be prompted for the credentials when running any of the functions. If the module [CredentialManager](https://github.com/davotronic5000/PowerShell_Credential_Manager) is available (to install it run `Install-Module CredentialManager` in Powreshell 5+) credentials will be stored in the Windows Credential Manager and after first run, you will be able to use any function in any PS session using your stored credentials. 45 | 46 | To use _ad hoc_ credentials when you have your main credential stored simply use: 47 | 48 | $tfs.Credentials = Get-Credential 49 | 50 | This way stored credentials will be overridden only for the current session. To change the stored credentials for all subsequent sessions either delete them using the Control Panel (Manage Windows Credentials) and run any function again or use the following command: 51 | 52 | $tfs.Credentials = New-TFSCredential #Get credential and store it in Credential Manager. 53 | 54 | Usage 55 | ===== 56 | 57 | * To view all supported commands execute `gcm -m tfs` 58 | * To get all aliases execute `get-alias | ? source -eq 'tfs'` 59 | * Use `man` to get help for the command: `man builds -Example` 60 | 61 | All functions have a `Verbose` parameter that shows very detailed log of every step involved: 62 | 63 | PS> Get-TFSBuildLogs -Verbose 64 | 65 | VERBOSE: No credentials specified, trying Windows Credential Manager 66 | VERBOSE: Populating RepositorySourceLocation property for module CredentialManager. 67 | VERBOSE: Loading module from path 'C:\Program Files\WindowsPowerShell\Modules\CredentialManager\1.0\CredentialManager.dll'. 68 | VERBOSE: Trying to get storred credentials for 'http://tfs015:8080/tfs' 69 | VERBOSE: Retrieving requested credential from Windows Credential Manager 70 | VERBOSE: New TFS credentials for 'http://tfs015:8080/tfs' 71 | VERBOSE: TFS Credential: majkinetor 72 | VERBOSE: URI: http://tfs015:8080/tfs/DefaultCollection/ProjectXYZ/_apis/build/builds?api-version=2.0 73 | VERBOSE: received 793807-byte response of content type application/json; charset=utf-8; api-version=2.0 74 | VERBOSE: Build id: 1456 75 | VERBOSE: Logs URI: http://tfs015:8080/tfs/DefaultCollection/ProjectXYZ/_apis/build/builds/1456/logs?api-version=2.0 76 | VERBOSE: GET http://10.1.6.27:8080/tfs/DefaultCollection/ProjectXYZ/_apis/build/builds/1456/logs?api-version=2.0 with 0-byte payload 77 | VERBOSE: received 738-byte response of content type application/json; charset=utf-8; api-version=2.0 78 | VERBOSE: Log URI: http://tfs015:8080/tfs/DefaultCollection/cc756267-fb53-4148-906f-471588d87bcb/_apis/build/builds/1456/logs/1 79 | ... 80 | --------------------------------------------------------------------------------