PowerShell ISE + TFS

For a while now I have been using a combination of ISE and Visual Studio to maintain my powershell scripts. Why? because I wanted to be able to have proper version control for my team and also have the features supplied by ISE.

Enter Visual Studio Team Foundation Server 2013 Update 2 Power Tools. They come with a PSSnapin to maintain files on TFS. The Scripting Guys have a great detailed blog on TFS and the PowerShell SnapIn here.

Please note that 2013 is now available and the process is very much the same. In brief, you can signup for a free TFS service via Visual Studio Online. Then Install the TFS 2013 Client. Configure the client. Install Power Tools for TFS 2013.

Now you have got TFS, ISE and a bunch of cmdlets, you might be wondering, is this really going to be easier than managing it via the VS gui? Well with a few customizations to my ISE profile and it will be.

After installing the TFS Power Tools, add the following to your ISE profile. (Just modify the $Global:WorkspacePath Variable to suit your environment).

This will make sure you have the latest files from TFS each time you load ISE, and will add a menu under add-ons where you can check-out the current file, and check-in all pending files.

Stay posted as I will publish a standalone module for easier installation soon.

$Global:WorkspacePath = 'Path/to/VS/Workspace'
Add-Type -AssemblyName System.Windows.Forms
Add-PSSnapin -Name Microsoft.TeamFoundation.PowerShell

function Checkin-Files
    Add-TfsPendingChange -Add -Item $Global:WorkspacePath -Recurse
    $pendingFiles = Get-TfsPendingChange -Item $Global:WorkspacePath -Recurse
    if ($pendingFiles -ne $null)
        $TFSCheck = [System.Windows.Forms.MessageBox]::Show(($pendingFiles | Select-Object -ExpandProperty localitem),'Do you want to check in these files?', 'TFS Check in','YesNo')
        if ($TFSCheck -eq 'Yes') 
            $TFSResult = New-TfsChangeset -Item $Global:WorkspacePath -Recurse 
            if ($TFSResult -ne $null){
            $null = [System.Windows.Forms.MessageBox]::Show(('Change Set ID: ' + $TFSResult.ChangesetID + "`r`nOwner: " + $TFSResult.Owner + "`r`nCreation Date: " + $TFSResult.CreationDate), 'TFS Check in')
            {$null = [System.Windows.Forms.MessageBox]::Show('No changes to check in!', 'TFS Check in')}
        $null = [System.Windows.Forms.MessageBox]::Show('No files to be checked in!', 'TFS Check in')

Write-Host -Object 'Updating TFS Workspace' -ForegroundColor Green
Update-TfsWorkspace -Item 'C:\Users\Peter\Documents\Visual Studio 2013\Workspace - Techpath\TechPath Base Class Library\TPBCL' -Recurse
New-Alias -Name 'cf' -Value Checkin-Files
$tfsRoot = $psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add('Team Foundation Server',$null,$null) 
$tfsRoot.Submenus.Add('Check out current file',  
        $TFSResult = Add-TfsPendingChange -Edit -Item $psISE.CurrentFile.FullPath | Select-Object -Property Version, ChangeType, ServerItem
        if ($result -ne $null)
            $null = [System.Windows.Forms.MessageBox]::Show(('Version: ' + $TFSResult.Version + "`r`nChangeType: " + $TFSResult.ChangeType + "`r`nFile: " + $TFSResult.ServerItem), 'TFS File Checkout')
            $null = [System.Windows.Forms.MessageBox]::Show('File could not be checked out', 'TFS File Checkout')
, 'Ctrl+Alt+E') | Out-Null
$tfsRoot.SubMenus.Add('Check in Pending Changes',  
, 'Ctrl+Alt+S') | Out-Null

PowerShell: Customising the pipeline inputs

Sometime when using cmdlets there might be cases where you would assume two cmdlets would work together naturally and they just don’t. A good example of this is anything that takes -ComputerName as an input parameter (E.g test-connection) and the Get-ADComputer command.

In this example if you run Get-ADComputer -filter * you get a list of all computers in the current domain, say you now want to run Test-Connection to check machines are online, you might think you could just go:
Get-ADComputer -filter * | test-connection
Unfortunately you will be greeted with a wall of red text, this is because Get-ADComputer does not output an object with a ComputerName property, instead it has either Name or DNSHostName. You might now decide to output it all to a text file and ping them manually but your using PowerShell and there are easy ways do do this on the fly.

Method 1: Create a custom property on the ADComputer object with the name “ComputerName” so the next command receives what it expects:

Get-ADComputer -Filter * | 
   select @{n="ComputerName"; e={$_.DNSHostName}} | 

Method 2: Specify the name of the ADComputer object’s property in the next cmdlets input:

Get-ADComputer -Filter * | Test-Connection -ComputerName {$_.DNSHostName}

Using either of the above methods works with any cmdlet that accepts ComputerName as a pipeline input parameter.

Holy Trinity of PowerShell






PS C:\> $a = "Hello World"

PS C:\> $a | Get-Member
TypeName: System.String ..

PS C:\> $b | Get-Member
TypeName: System.String ...

Name        BaseType 
String      System.Object

Name        BaseType 
Object[]    System.Array