Go to content Go to navigation Go to search

Brokenwire.NET::Programming

Using the Team Foundation Object Model with PowerShell
· 2008-02-26 12:45 by Thijs Kroesbergen for Brokenwire.NET

After my first steps in PowerShell I wanted to do something more. I wanted to manipulate the TFS build store from within PowerShell (because I installed TFSDeployer which responds to "Build Quality Changed events" from TFS). So after some Google-ing I found the "get-tfs.ps1" script written by James Manning. His script wraps the Team Foundation Server SDK (which is .NET object model to access TFS) into one nice PowerShell script.

The only thing his script could not do, was manipulate the build store. But with the addition of one line I made this possible. His version also was a "script-as-function" while I'd rather have just a separate function that I could put in my script library. The script now look like this:

function get-tfs ( [string] $serverName = $(Throw 'serverName is required') ) { # load the required dll [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Client") $propertiesToAdd = ( ('VCS', 'Microsoft.TeamFoundation.VersionControl.Client', 'Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer'), ('WIT', 'Microsoft.TeamFoundation.WorkItemTracking.Client', 'Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore'), ('BS', 'Microsoft.TeamFoundation.Build.Common', 'Microsoft.TeamFoundation.Build.Proxy.BuildStore'), ('CSS', 'Microsoft.TeamFoundation', 'Microsoft.TeamFoundation.Server.ICommonStructureService'), ('GSS', 'Microsoft.TeamFoundation', 'Microsoft.TeamFoundation.Server.IGroupSecurityService') ) # fetch the TFS instance, but add some useful properties to make life easier # Make sure to "promote" it to a psobject now to make later modification easier [psobject] $tfs = [Microsoft.TeamFoundation.Client.TeamFoundationServerFactory]::GetServer($serverName) foreach ($entry in $propertiesToAdd) { $scriptBlock = ' [System.Reflection.Assembly]::LoadWithPartialName("{0}") > $null $this.GetService([{1}]) ' -f $entry[1],$entry[2] $tfs | add-member scriptproperty $entry[0] $ExecutionContext.InvokeCommand.NewScriptBlock($scriptBlock) } return $tfs }

The way this works is that the "propertiesToAdd" array is looped through, and the $tfs powershell object is extended with a new object with the names of the first array element. The actual objects are retrieved from the third array element and they are retrieved from the assemblies in the second array element.
This function is used to create a $tfs object, like this:

$tfs = get-tfs http://YourTfsServer:8080

Next, you can use one of the properties of the $tfs object to do cool stuff.

VCS gives you access to the version control server. For example, you can download a single file, without an active workspace mapping:

$tfs.VCS.DownloadFile("$/Test Project/ClassLibrary1/ClassLibrary1.sln", "C:\ClassLibrary1.sln")

Or you can map a temporary workspace to retrieve all files in a specific folder:

$workspace = $tfs.VCS.CreateWorkspace([System.Guid]::NewGuid().ToString(), $tfs.AuthenticatedUserName) $workspace.Map("$/Test Project/Dev/Branch/SomeFolder", "C:\SomeFolder") $workspace.Get() $workspace.Delete()

WIT lets you use the work item store. For example, you can retrieve the uri, title and state of a workitem:

$tfs.WIT.GetWorkItem(1) | Format-List uri, title, state

BS is the wrapper for the build store. Let's do something more complex: retrieve the last successfully completed build and once this build is found, update the Build Quality.

$build = $tfs.BS.GetListOfBuilds("Test Project", "TestBuild") | where {$_.Buildstatus -eq "Successfully Completed"} | sort -property FinishTime -desc | select -first 1 $tfs.BS.UpdateBuildQuality($build.BuildUri, "Ready for Initial Test")

CSS contains the ICommonStructureService interface. This interface gives you access to the more common properties of you TFS server. For example, retrieve all projects on your server (which is just a bit boring...):

$tfs.CSS.ListAllProjects()

The last one, GSS, gives access to the group security service interface. The next two lines of script will give you a list of all the groups (both windows and application groups) a specific account is a member of:

$groups = $tfs.gss.ReadIdentity('accountName', 'YourNTAccountName', 'direct').memberof $groups | foreach-object -process { $tfs.GSS.ReadIdentity('sid', $_, 'none') } | fl type, displayname

I know I like working with TFS & PowerShell a lot, and I hope you do too. Enjoy!

Permalink -

  1. I am trying to recursively-get all of the files that are in the $/MyProj/TeamBuildTypes/IncludeFiles into a local filesystem, without checking anything out into a workspace or anything else (my actual solution is in $/MyProj/QA). I was trying to understand what you did and am coming up short. Can you provide a little more detail?

    Thanks.


    Thor    2010-04-19 18:28    #
  2. @Thor:
    You can’t get files without a workspace. The VCS example above creates a temp-workspace (with a Guid as name), then it gets the files and removes the workspace.


    thijs    2010-04-20 16:15    #
  3. I ended up addressing the API using C# using some stuff from the TFS SDK. If it can be done with C#, I’m sure it could be done with PowerShell (but I utterly lack PS skills)…

    this.TfsCurrent =
    new TeamFoundationServer(this.TfsUri, System.Net.CredentialCache.DefaultCredentials);
    this.TfsCurrent.Authenticate();
    this.VcsCurrent = this.TfsCurrent.GetService(typeof(VersionControlServer)) as VersionControlServer;
    this.ItemsCurrent = this.VcsCurrent.GetItems(this.TfsSourcepath , RecursionType.Full);
    this.txtInfo.Text += Environment.NewLine + “QueryPath “ + this.ItemsCurrent.QueryPath + Environment.NewLine;
    foreach (Item currentItem in this.ItemsCurrent.Items)
    { if ( currentItem.DeletionId == 0 && currentItem.ServerItem.ToLower().EndsWith(”.xml” ) ) { this.txtInfo.Text += “ “ + currentItem.ServerItem + Environment.NewLine; }
    }
    this.txtInfo.Text += Environment.NewLine + Environment.NewLine;
    this.txtInfo.Text += Environment.NewLine + “Saving files locally.” + Environment.NewLine;
    foreach (Item currentItem in this.ItemsCurrent.Items)
    { if (currentItem.DeletionId == 0 && currentItem.ServerItem.ToLower().EndsWith(”.xml”)) { string localFile = this.LocalFsDestPath + “\\” + currentItem.ServerItem.Split(’/’)[currentItem.ServerItem.Split(’/’).Length – 1]; currentItem.DownloadFile( localFile ); this.txtInfo.Text += “ Saved “ + localFile + Environment.NewLine; }

    }

    Thanks!


    Thor    2010-04-22 16:37    #
  4. Thank you for this helpful script.

    I’ve tried using it and managed to connect to my TFS, but the following line:
    $builds = $tfs.BS.GetListOfBuilds(“MyProject”, “MyBuild”)
    raised this exception:
    You cannot call a method on a null-valued expression.
    At C:\deploy.ps1:45 char:34
    + $builds = $tfs.BS.GetListOfBuilds <<<< (“MyProject”, “MyBuild”) + CategoryInfo : InvalidOperation: (GetListOfBuilds:String) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : InvokeMethodOnNull

    Might you know how to fix this?

    Thanks in advance,
    Uri Goldstein


    Uri Goldstein    2010-08-11 20:39    #
  5. An important detail regarding my previous comment (no. 4):

    I have VS2010 installed and so I believe the Microsoft.TeamFoundation.Build.Common namespace no longer contains a Microsoft.TeamFoundation.Build.Proxy.BuildStore class any more – it’s been deprecated in .net 4.0 (?)


    Uri Goldstein    2010-08-12 11:02    #
  6. Hi,
    Have you found what class corresponds to build store in .net 4?


    Nickolay Medin    2010-08-27 11:52    #
  7. Is there a way to get the list of collections on a server?


    — Larry Singer    2011-04-21 17:09    #
  8. @Larry
    Yes there is an API call for that, take a look here:
    http://blogs.msdn.com/b/taylaf/archive/2010/01/26/retrieve-the-list-of-team-project-collections-from-tfs-2010-client-apis.aspx


    thijs    2011-04-21 17:11    #