Using the 'tf.exe' command to clean up old TFVC workspaces

Today, a colleague contacted me with an issue that he encountered during one of his builds: Workspace error

The problem lies with something legacy that we try not to use anymore wherever possible, but in this case, there seems to be an exception. In Azure DevOps, the default for version control was once (actually not that long ago) "Team Foundation Version Control" (TFVC) and to be able to get your code locally, you needed to set up a so called "workspace". That workspace took care of the mapping of the files in version control and the files on the local computer. You could do all kinds of cool stuff with it (and sometimes they went out out of control). These workspaces were also needed in the context of the build. at the start of the build, a workspace was created and the required mapping was put in place in order for the build to be able to work. After the build was done, the files (and the workspace) were removed. This worked fine, except when builds failed: In this case, the cleanup (did not always) happen and the result was that sometimes the build server tried to "create" a workspace with a name that was already in use. (This is an issue as duplicates workspace names are not allowed in TFVC). This means that from time to time, some kind of cleanup needed to be done and typically we used tools like TFS Sidekicks for this. Because the support of this thing stopped with Visual studio 2015, I tried to do it differently:

tf.exe

every installation of visual studio comes with an executable that is called tf.exe. It allows you to automate/script stuff in TFS/AzureDevOps and has been around for a while. There are better alternatives now (such as the Azure CLI and the Azure DevOps SDK), but in this case, and since we are working with some legacy, this approach seemed applicable...

One of the things that you can use TF for, is to manipulate workspaces. (list, search, etc.) This can be seem below:

1tf vc workspaces /server:<collectionUrl> /computer:<servername> /owner:"<ownername>"

note: the servername can also be *, but then I really suggest to also work with the (optional) /owner flag as you will otherwise list all workspaces of all users on all machines. In essence there is no issue in doing so when listing the workspaces, but it is a whole different matter when starting the cleanup.

Then you would think that cleaning all these workspaces at once, can then be done by adding the '/remove' flag to the previous command. Unfortunately, it's not that simple. It depends on the current config of your pc; If you have the workspaces on your machine, then you can work try to do it with the workspaces subcommand. Otherwise, you have to work with the workspace command, which is described a bit lower!

Working with the TF workspaces command

note, I had to clean up form a machine where the workspaces were not available, so this is based on one or 2 assumptions)

1tf vc workspaces /server:<collectionUrl> /computer:<servername> /owner:"<ownername>" /remove:*

note: this doesn't work on a machine where the workspaces are not actually present. For that, you need to work with the workspace command.

unfortunately, this is not the case as you can see in the output that /owner and /computer flags cannot be used in combination with the /remove flag:

This means that you will have to get smart with what you add to the /remove flag as filter. adding a * would remove all the workspaces that are found and in our context, that would mean that all workspaces on al computers of all users would be deleted and that is perhaps not what you want. In my context there are 2 options of which I assume that they should work:

  1. the workspace name has a similarity in the name: the one that gave me the build problem, starts with TeamCity, so I can try add that to my filter: /remove:TeamCity*
  2. the workspace name is also appended with my name in the TFVC DB, this means that I can also try do this with the remove flag: /remove:"*;Tim Schaeps"

note: In my case, this did not work, so I cannot show the output... (nor can I confirm that this actually works 😃 )

More info on this "workspaces" command: https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/54dkh0y3(v=vs.100) Note: this page is not actively supported anymore! (and might be removed in the future)

working with the TF workspace command

So, as the workspaces command did not work for me, I had to resort to another approach. The alternative is the tf workspace /delete command, which accepts a /delete flag. This allows you to specify one (1) workspace that you want to delete. It is not ideal, but this was the road that I went down and that I wanted to complete. So I wrote some (very basic) PowerShell to allow me to extract the workspace name from the table output from the tf workspaces command that was used earlier.

 1$currentdir = pwd
 2cd "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7
 3\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer"
 4$workspaces = .\tf.exe vc workspaces /server:https://<account>.visualstudio.com /computer:* /owner:"Tim Schaeps"
 5# next line isneeded to remove the reference to the azure devops collection as seen in one of the first screenshots
 6$workspaces = $workspaces | Select-Object -Last ($workspaces.Length -1) 
 7# each column is indicated by dashes, I used this to determine the width of the first column (which I needed here)
 8$lines = $workspaces | Select-Object -Index 1
 9#here I get the width of the first column
10$segments = $lines.Split(' ')
11$firstcolumn = ($segments | Select-Object -Index 0).length
12#here I loop over every line in the table (after skipping the header lines)
13ForEach ($workspace in $workspaces |Select-Object -Last ($workspaces.Length -2))
14{
15    #get the workspace name
16    $workspacetoremove = ($workspace.Substring(0,$firstcolumn))
17    Write-Host $workspacetoremove
18    #use the workspace name (after trimming) to delete the specific workspace
19    .\tf.exe vc workspace /server:https://<account>.visualstudio.com /delete $workspacetoremove.Trim()
20}
21cd $currentdir

Running this script, will loop over all the workspaces and delete them one by one. The "only" issue that I could not overcome in this case, is that the /delete subcommand does not seem to accept something to confirm up front or even to force, which means that the deletion of each workspace needs to be deleted. In this case, I could live with that as this is most likely something that I will not have to be doing again anytime soon...

The result, after running a validation to see of all workspaces are indeed gone, can be seen below:

More info on this "workspace" command: https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/y901w7se(v=vs.100) Note: this page is not actively supported anymore! (and might be removed in the future)

Conclusion

I showed you how you can clean up old TFVC workspaces with relative ease and now you should be able to do so too. Be aware that Microsoft prefers you to use git and that TFVC is not actively supported anymore. If you want to migrate away from TFVC to git (within Azure DevOps), then you can automate this. I have done it and will write about it soon!

I hope it helps!

T

comments powered by Disqus