PowerShell and TFS: The Basics and Beyond

Summary: Learn about Windows PowerShell and Team Foundation Server.

Microsoft Scripting Guy, Ed Wilson, is here. Today I am happy to introduce a new guest blogger, Susan Ferrell. Susan has spent most of her professional life working with computers, from basement tech startups to high-end datacenters. She’s a senior technical writer at Microsoft.

Hi all! Susan here.

Unlike most of you reading this, administering Team Foundation Server (TFS) is my primary focus, not Windows PowerShell. In fact, I was pretty much a Windows PowerShell novice until a month ago. However, less than a half hour into my first Windows PowerShell scripting session, I found myself excited by the possibilities for TFS and Windows PowerShell together.

Things that are difficult or downright impossible to do with the built-in tools in TFS are very possible by using Windows PowerShell. Or so I believed. And luckily enough for me, Jason Stangroome not only confirmed my suspicions, he volunteered to help me put this post together. He's an old pro with both Windows PowerShell and TFS. So whether you're relatively new to Windows PowerShell (like me), relatively new to TFS, or an old hand at both, there should be at least something of interest to you here. In fact, I'm labelling the sections, so you can skip to what's interesting to you.

The absolute basics

Where does a newbie to Windows PowerShell start—particularly in regards to TFS? There are a few obvious places. I'm hardly the first person to trip across the natural peanut-butter-and-chocolate nature of TFS and Windows PowerShell together. In fact, the TFS Power Tools contain a set of cmdlets for version control and a few other functions.

There is one issue when downloading them, however. The "typical" installation of the Power Tools leaves out the Windows PowerShell cmdlets! So make sure you choose "custom" and select those Windows PowerShell cmdlets manually.

After they're installed, you also might need to manually add them to Windows PowerShell before you can start using them. If you try Get-Help for one of the cmdlets and see nothing but an error message, you know you'll need to do so (and not simply use Update-Help, as the error message implies).

Fortunately, that's simple. Using the following command will fix the issue:

add-pssnapin Microsoft.TeamFoundation.PowerShell

See the before and after:

Image of command output

A better way to review what's in the Power Tools and to get the full list of cmdlets installed by the TFS Power Tools is to use:

Get-Command -module Microsoft.TeamFoundation.PowerShell

This method doesn't depend on the developers including "TFS" in all the cmdlet names. But as it happens, they did follow the Cmdlet Development Guidelines, so both commands return the same results.

Something else I realized when working with the TFS PowerShell cmdlets: for administrative tasks, like those I'm most interested in, you'll want to launch Windows PowerShell as an administrator. And as long-time Windows PowerShell users already know, if you want to enable the execution of remote scripts, make sure that you set your script execution policy to RemoteSigned. For more information, see How Can I Write and Run a Windows PowerShell Script?.

Of all the cmdlets provided with the TFS Power Tools, one of my personal favorites is Get-TfsServer, which lets me get the instance ID of my server, among other useful things.  My least favorite thing about the cmdlets in the Power Tools? There is little to no useful information for TFS cmdlets in Get-Help. Awkward! (There's a community bug about this if you want to add your comments or vote on it.)

Jason has a different favorite: Get-TFSItemHistory. His following example not only demonstrates the power of the cmdlets, but also some of their limitations:

Get-TfsItemHistory -HistoryItem . -Recurse -Stopafter 5 |

    ForEach-Object { Get-TfsChangeset -ChangesetNumber $_.ChangesetId } |

    Select-Object -ExpandProperty Changes |

    Select-Object -ExpandProperty Item

This snippet gets the last five changesets in or under the current directory, and then it gets the list of files that were changed in those changesets. Sadly, this example also highlights one of the shortcomings of the Power Tools cmdlets: Get-TfsItemHistory cannot be directly piped to Get-TfsChangeset because the former outputs objects with ChangesetId properties, and the latter expects a ChangesetNumber parameter.

One of the nice things is that raw TFS API objects are being returned, and the snap-ins define custom Windows PowerShell formatting rules for these objects. In the previous example, the objects are instances of VersionControl.Client.Item, but the formatting approximates that seen with Get-ChildItem.

So the cmdlets included in the TFS Power Tools are a good place to start if you're just getting started with TFS and Windows PowerShell, but they're somewhat limited in scope. Most of them are simply piping results of the tf.exe commands that are already available in TFS. You'll probably find yourself wanting to do more than just work with these.

Beyond the basics

Fortunately, the APIs for TFS client, SQL Server, and SharePoint are all accessible in Windows PowerShell. This is where the fun begins, and where you really start getting into the scripting. This is also where I, as a newbie to Windows PowerShell, start running into my limitations. Fortunately, there are a lot of great resources out there, from people posting their first TFS-related Windows PowerShell scripts to projects available on CodePlex. (I've started a Curah to share useful links. If you have additional suggestions, please add them!)

For my first project, I decided I wanted to write a simple script to tell me if my account is an authenticated account in TFS (I work with a lot of different TFS boxes in test environments). To do this, I had to learn how to add the TFS public assemblies to my Windows PowerShell scripts. Check out Add-Type –AssemblyName! It made my day when I realized I could simply do this to get the full power of the TFS 2012 client assemblies at my Windows PowerShell fingertips:

Add-Type –AssemblyName "Microsoft.TeamFoundation.Client, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

By adding the public TFS assemblies, I can call the APIs from Windows PowerShell simply by referencing them. For example, here’s the simple script I wrote as a training exercise:

And hurrah, it works! I just input the URI (for example, http://fabrikamfiber:8080/tfs), and the script tells me if the account I’m logged in as authenticates against that particular server.

You’ll notice that instead of only using Add-Type –AssemblyName, I defined a function. This is a handy shortcut, particularly if you want to load more than one assembly, as I did. Because this is a function, I’m will likely want to use over and over again, so I’m going to add it to my profile.

Jason came up with an even more elegant answer to add assemblies, which is a lot easier to read and is also easier to add multiples to, if they are the same version and use the same key:

'Microsoft.TeamFoundation', 'Microsoft.TeamFoundation.Client', 'Microsoft.TeamFoundation.Common' |
    ForEach-Object {
        Add-Type -AssemblyName "$_, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

Next, I thought about using Windows PowerShell to create a team project. I ought to be able to create a Windows PowerShell cmdlet that lets me specify the name of the server and collection, the name for the project, the template to use, and the source control option, and then go. Right?

Yes, I know, there's a power tool that does this already: tfpt createteamproject. It's not Windows PowerShell, though, and it requires installing the power tools and having Visual Studio or Team Explorer on the box. And as it happens, I couldn’t figure out a better way to do this in Windows PowerShell. Jason did, though. He’s created a Windows PowerShell script that wraps the tfpt createteamproject command, and he made it publicly available: New-TFSTeamProject.ps1.

I then wanted to create a cmdlet to help me know what users belong to what teams in TFS. Getting information about what users belong to which projects in a collection is a relatively straightforward task (you can pipe the results of TFSSecurity commands into much nicer formats by using Windows PowerShell). But there’s no quick way to show what teams exist in a collection, or the user membership of each of those teams. There's the Team Members utility in the TFS 2012 Power Tools, but even that isn't ideal. Fortunately, I know that the TeamFoundationTeam Class is part of Microsoft.TeamFoundation.Client. So can I leverage that by using Windows PowerShell?

Probably. Here's where I've run into my limitations as a newbie Windows PowerShell user. I had some ideas about how to do this, but I'm still trying to figure it out and get it to work. But I was sure it was possible, and here's where Jason comes to the rescue once again (and provides the real value for those of you well past my basic level).

Not only has he already thought about this, he did in PSTFSTeams—at least for those of you using TFS 2012. PSTFSTeams not only lets you see exactly the information that I was trying to figure out how to extract, but it lets you add members to teams and create new teams, too.

So what next? There are all kinds of administrative tweaks that seem suited to Windows PowerShell, from managing build workflows to mass-managing work items. I hardly know where to start. What are your favorite TFS-related Windows PowerShell tips and tricks? And what would you like to see next?

I can't wait to find out.


Thank you, Susan and Jason, for a really powerful and interesting post.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Comments (17)

  1. Anonymous says:

    Thanks Stefan, that’s a great post!
    Sina, I’m glad you enjoyed the blog post and found it helpful. I hope to have more useful information to share in the future. 🙂

  2. sina2 says:

    Thanks for the great tips on TFS scripting; just getting into grips with power shell to for automating TFS tasks and the gap in my understanding was how to enable the use of TFS cmdlets; you very nicely plugged this gap! Looking forward to your next blogs.

  3. james says:

    Thanks Stefan, that’s a great post!
    I’m glad you enjoyed the blog post and found it helpful. I hope to have more useful information to share in the future. 🙂


  4. alex says:

    Thanks Stefan, that’s a great post!
    I’m glad you enjoyed the blog post and found it helpful. I hope to have more useful information to share in the future. 🙂


  5. Andrea says:

    Wow, a great article about PowerShell and TFS, very detailed and informative, thanks..

    Andrea from http://www.ukgrocertificatesonline.co.uk/

  6. kate4 says:

    Very well written, detailed, informative and well structured article , thank you.


  7. Krushna says:


    i want to add all 3 versions so that who ever uses the script need not edit
    but i am getting few error’s

    ‘Microsoft.TeamFoundation’, ‘Microsoft.TeamFoundation.Client’, ‘Microsoft.TeamFoundation.Common’ |
    ForEach-Object {
    Add-Type -AssemblyName "$_, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
    Add-Type -AssemblyName "$_, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
    Add-Type -AssemblyName "$_, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

    I should not get any error even if i use Try catch block..
    Can some one help me ? i tried with -silently also but didnot work properly

  8. pregunton says:

    Now, in may 2015 , how use TFS and Powershell ?

  9. Matt W. says:

    Hi there,

    I’ve seen some articles indicating to simply use tf.exe within PowerShell scripts instead of installing Tools and using the cmdlets. What are the advantages of using cmdlets over tf.exe?

  10. Matt W says:

    Currently I’m looking to check-out the AssemblyInfo files to update their versions then check them back in. I did find a script that will update the actual binary versions (http://www.colinsalmcorner.com/post/matching-binary-version-to-build-number-version-in-tfs-2013-builds)
    , but the information, based on the definitions Build Number format attribute or property is not maintained in the AssemblyInfo files.

    Here is the latest hack attempt I’ve tried…

    if ($buildNumber -match $pattern -ne $true) {
    Write-Host "Could not extract a version from [$buildNumber] using pattern [$pattern]"
    exit 1
    } else {
    $extractedBuildNumber = $Matches[0]
    Write-Host "Using version $extractedBuildNumber"

    gci -Path $pathToSearch -Filter $searchFilter -Recurse | %{
    Write-Host " -> Changing $($_.FullName)"

    # remove the read-only bit on the file
    #if the following is uncommented, the binaries are versioned correctly or as intended…
    #sp $_.FullName IsReadOnly $false

    # PowerTools on TFS Server?…
    #checkout-file $_.FullName
    checkout-file "C:SrcCiTestProductOneComponentWithUnitTestPropertiesAssemblyInfo.cs"

    # run the regex replace
    (gc $_.FullName) | % { $_ -replace $pattern, $extractedBuildNumber } | sc $_.FullName

    Write-Host "Done!"
    catch {
    Write-Host $_
    exit 1

    function checkout-file([string] $file)
    #function checkout-file
    #"Checking out $file"
    if ((Get-PSSnapin -Name Microsoft.TeamFoundation.PowerShell -ErrorAction SilentlyContinue) -eq $null )
    Add-PsSnapin Microsoft.TeamFoundation.PowerShell -ErrorAction SilentlyContinue

    if ((Get-PSSnapin -Name Microsoft.TeamFoundation.PowerShell -ErrorAction SilentlyContinue) -eq $null )
    #try to check out the code using command line tf.exe
    &"C:Program Files (x86)Microsoft Visual Studio 12.0Common7IDETF.exe" checkout AssemblyInfo* /recursive | Out-Null
    #checkout the file using snapin
    Add-TfsPendingChange -Edit -item $file -lock none -recurse | Out-Null
    #Add-TfsPendingChange -Edit -item $/CITEST/ProductOne/Properties/assemblyinfo.cs
    #checkout the file using snapin
    Add-TfsPendingChange -Edit -item $file -lock none -recurse | Out-Null
    #Add-TfsPendingChange -Edit -item $/CITEST/AssemblyInfo* -lock none -recurse

    I’m getting to the point of wondering if I would be better off trying to customize the TFS build template in Visual Studio instead. My thinking is that if I can script as much as possible it would be easier to maintain and I wouldn’t have to worry anything
    getting whacked by a VS/TFS upgrade, etc.

  11. Wessun007 says:

    is there a simple way to connect to TFS via Powershell as i’m finding a simple solution

  12. Soniya says:

    Hi Susan/Jason,

    I tried to customise the Script in New-TFSTeamProject.ps1 to use CSV file instead of xml. But Get an error, please help..

    Module: CssStructureUploader | Thread: 18 | TF30155: Error occurred while trying to create project : “MyforthProject”
    —begin Exception entry—
    Time: 2016-03-09T23:39:12
    Module: Engine
    Event Description: TF30162: Task “UploadStructure” from Group “Classification” failed
    Exception Type: Microsoft.TeamFoundation.Client.PcwException
    Exception Message: The Project Creation Wizard encountered an error while creating the project structure on hmsutar.visualstudio.com\DefaultCollection.
    Exception Details:
    The Project Creation Wizard encountered a problem while creating the project structure on hmsutar.visualstudio.com\DefaultCollection.
    The reason for the failure cannot be determined at this time.
    Because the operation failed, the wizard was not able to finish
    creating the Team Project.

    The customised script is below:

    param (
    [parameter(position=0, Mandatory=$true)]

    [parameter(position=1, Mandatory=$true)]

    Write-Host “Please provide the login credentials for VSTS” -ForegroundColor Yellow

    $UserID = Read-Host -Prompt ” User Name”
    $SecurePassword = Read-Host -Prompt Password -AsSecureString
    $creds = New-Object System.Management.Automation.PSCredential($UserID, $SecurePassword)

    #Authenticate to the VSTS website

    Invoke-WebRequest -Uri $collectionUri -Credential $creds

    #System check for TFS Power tools
    if (-not $env:TFSPowerToolDir) {
    throw “Enviroment Variable ‘TFSPowerToolDir is not set. You may need to restart the computer after installing the TFS power tools”

    $tfptexe = Join-Path -Path $env:TFSPowerToolDir -ChildPath tfpt.exe
    if (-not (Test-Path -Path $tfptexe -PathType Leaf)) {
    Throw ” TFS power tools must be installed”

    if (Get-Process | Where-Object { $_.Name -eq ‘devenv’}) {
    Write-Warning ‘For best results, close running instances of Visual Studio before proceeding. waiting 10 seconds….’
    Start-Sleep -Seconds 10

    #Import CSV data and create team project

    $TProjectdata = Import-Csv -Delimiter ‘,’ -Path $PathtoCSV

    Foreach ($Tproject in $TProjectdata) {
    $ProjectName = $Tproject.Projectname
    $ProcessTemplate = $Tproject.ProcessTemplate
    & $tfptexe createteamproject /collection:$uri /teamproject:$Projectname /processtemplate:$template /noportal

  13. A D R I A N says:

    When I try and use your Add-Type command, it fails under Windows 10. Why would that be? Here is my command and it’s output:

    PS C:\projects\dump> Add-Type –AssemblyName “Microsoft.TeamFoundation.Client, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”
    Add-Type : Cannot add type. The assembly ‘Microsoft.TeamFoundation.Client, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ could not be found.
    At line:1 char:1
    + Add-Type –AssemblyName “Microsoft.TeamFoundation.Client, Version=11.0 …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : ObjectNotFound: (Microsoft.TeamF…03f5f7f11d50a3a:String) [Add-Type], Exception
    + FullyQualifiedErrorId : ASSEMBLY_NOT_FOUND,Microsoft.PowerShell.Commands.AddTypeCommand

    Add-Type : Cannot add type. One or more required assemblies are missing.
    At line:1 char:1
    + Add-Type –AssemblyName “Microsoft.TeamFoundation.Client, Version=11.0 …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidData: (:) [Add-Type], InvalidOperationException
    + FullyQualifiedErrorId : ASSEMBLY_LOAD_ERRORS,Microsoft.PowerShell.Commands.AddTypeCommand

    1. A D R I A N says:

      It would seem that the problem was that the version was incorrect. You were using VS2012, which has assemblies marked as 11.0… This number is different for every succeeding VS version. 11 = 2012, 12 = 2013, 14 = 2015.

      Curah has been removed and replaced by docs. It would be great to update your links as it would be great to be able to have some examples to use credentials because of the scriptlets lack of credential usage. Even then, I feel that I shouldn’t have to write such low level stuff when most of the code is already available in the current scriptlets :(, unless I’m missing something.

  14. Sameer_Kumar says:

    Nice post. Inspired me to curate the scriptlets and produce a repo: https://github.com/sameer-kumar/adhoc-posh

Skip to main content