get-tfs.ps1 - example of adding properties to make interactive life easier

One of the things I noticed while working inside PowerShell was wanting to make a quick TFS object model (OM) call (for instance, to VersionControlServer) and slice/data/inspect the results.  The OM calls to get an instance of TeamFoundationServer aren't bad (first load the assembly, then just call the ctor or use the factory class), and getting the VersionControlServer from there isn't bad (load the assembly, then GetService the type), but they weren't "very" scripting-friendly, so here's a simple wrapper for them.

Currently it just adds 4 properties to the TeamFoundationServer instance, for a little easier access to the particular interfaces I find myself using the most.  Of course, you can add/remove entries from these (or just not add them at all, of course).  For each of them, it generates the script block to execute for that property, then uses add-member to attach it.  Of course, we're not really changing the TeamFoundationServer type on the fly, but the PsObject mechanics make it look like that, which is really slick and useful :)

With this script, I can fetch an instance with " $tfs = get-tfs https://tkbgitvstfat01:8080" (substitute your server name or URL, of course) and then make OM calls easily.  Since we're in PowerShell, I can take the output of these calls and do lots of fun filtering and inspection.  Here are a few simple examples - hopefully it's clear that this is far easier than having to call tf.exe and do the cut/awk/sed/grep/findstr kinds of work you normally would deal with in a cmd/bash/whatever world.

Note that the actual useful functionality (Version Control, Work Item Tracking, etc.) was always there, of course, the only real thing this script does it make it a little easier to get at it.  Also, there are far more useful services offered by TeamFoundationServer than just these 4, but I wanted to keep the example simple, but still useful.

Use VersionControlServer (VCS) to find the top-level folder with the longest name

C:\> $tfs.vcs.GetItems('$/*').Items | sort -desc { $_.serveritem.length } | select -first 1 | fl serveritem,checkindate,changesetid

ServerItem : $/Developer Division Product Scope
CheckinDate : 5/30/2006 1:08:36 PM
ChangesetId : 74756

Use CommonStructureService (CSS) to show all the team projects that have 'VSTS' in the name

C:\> $tfs.css.listallprojects() | ?{ $_.Name -match 'VSTS' } | fl

Uri : vstfs:///Classification/TeamProject/180e1e5d-bbe8-418a-b9a4-152026964fb4
Name : VSTS V2 Plans
Status : WellFormed

Uri : vstfs:///Classification/TeamProject/365f4f53-9e05-4ded-9c21-52e9d9de978a
Name : VSTS Architecture Group
Status : Deleting

Uri : vstfs:///Classification/TeamProject/7c0d0a50-e31a-4bed-8e48-a99bc95def95
Name : VSTS Dogfood
Status : WellFormed

Uri : vstfs:///Classification/TeamProject/76a10abd-b3be-4803-9efc-d2a9d219885a
Name : VSTS Community
Status : WellFormed

Use WIT to check out the links from a work item and who created it when

C:\> $tfs.wit.getworkitem(99000) | fl links,CreatedBy,CreatedDate

Links : {100701, 99473, 95514}
CreatedBy : Doug Mortensen
CreatedDate : 5/22/2006 7:26:09 PM

Use GSS to look up my display name and email address

C:\> $tfs.gss.ReadIdentity('accountname', 'NORTHAMERICA\jmanning', 'none') | fl displayname,mailaddress

DisplayName : James Manning
MailAddress : james.manning@microsoft.com

Use GSS to count the number of direct members in a security group

C:\> $tfs.gss.ReadIdentity('accountname', 'REDMOND\burtonall', 'direct').members.count
6

 

 param(
    [string] $serverName = $(throw 'serverName is required')
)

begin
{
    # 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'),
        ('CSS', 'Microsoft.TeamFoundation', 'Microsoft.TeamFoundation.Server.ICommonStructureService'),
        ('GSS', 'Microsoft.TeamFoundation', 'Microsoft.TeamFoundation.Server.IGroupSecurityService')
    )
}

process
{
    # 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
}

get-tfs.ps1