Uploading custom Build Task to TFS 2015

In a previous article I wrote on how to write a custom Task For Visual Studio Team Services, but a usual question is: can I use the same technique to write a task to TFS 2015 on-premise?

The answer is yes, and it is really simple, thanks to this fantastic article by Jesse, that explain how to use Fiddler to being able to authenticate to on-premise TFS without the hassle of enabling basic authentication. Thanks to that article and Fiddler, you can simply login from tfx-cli to your TFS 2015 without any problem.


Figure 1: Login against your local TFS Service

Once I’m logged in, I can simply use the very same command used for VSTS to upload the directory where I defined the Build Task on my local TFS on-premise Server.


Figure 2: Task was uploaded to the server

As you can see the task was uploaded to the server, exactly in the very same way I uploaded to my VSTS account. The task is now available to be used in my TFS.


Figure 3: Custom task in action in a TFS 2015 on-premise build

Thanks to the extensibility with PowerShell I did not need to care about versions of VSTS or TFS, because the script does not have reference on any dll or package and the same task can be used both in VSTS or in TFS 2015 without changing a single line.

Thanks to the new Buidl System, extending a build for both VSTS and TFS is now a simple and easy task.

Gian Maria.

“Unsupported filter” using ContainsAny in Mongo 2.x driver

Porting code from Legacy driver to new driver syntax is quite annoying for .NET MongoDb driver. In the new Drivers almost everything is changed, and unless you want to still use old legacy syntax creating a mess of new and old syntax, you should convert all the code to the new syntax.

One of the annoying problem is ContainsAny in LINQ compatibility driver. In old drivers, if you have an object that contains an array of strings, and you want to filter for objects that have at least one of the value contained in a list of allowed values you had to resort to this syntax.

  return Containers.AllUnsorted
                .Any(c => c.PathId.Contains(containerIdString) &&

In this situation Aces properties is an HashSet<String> and aceList is a simple String[], the last part of the query uses the ContainsAny extension method from Legacy MongoDb driver. That extension was needed in the past because the old driver has no full support for LINQ Any syntax.

The problem that arise with the new driver is, after migrating code, the above code still compiles because it references the Legacy Drivers, but it throws an “Unsupported Filter” during execution. The solution is really simple, the new driver now support the whole LINQ Any syntax, so you should write:

return Containers.AllUnsorted
	 .Any(c =&gt; c.PathId.Contains(containerIdString) &amp;&amp;
			c.Aces.Any(a =&gt; aceList.Contains(a)));

As you can see, you can now write the Query with standard LINQ syntax without the need to resort to ContainsAny.

While I really appreciate that in the new Drivers LINQ support is improved, it is quite annoying that the old code still compiles but it throws at run-time.

Gian Maria.

Execute powershell code during a build in TFS 2015 Update 2

In TFS 2015 build system, running a PowerShell script stored in source code is simple because there is a dedicated action to accomplish this task, but if you want to run a PowerShell script that is not in source control, you have no option out of the Box.

Thanks to Update 2, you have now the ability to use the extensions you can find on Visual Studio Marketplace directly in your on-premise TFS installation. In the Upper Right corner of Web Ui you can find an icon that link to

This image shows the link in TFS UI to open the control panel to manage extension.

Figure 1: Link to manage extensions

This opens an administration page that allows you to manage extensions of your TFS installation. The first time you open this page, you will see no extension installed, so it is time to browse Visual Studio MarketPlace to find an extension. Actually I want to run inline PowerShell and I can immediately find that there is an extension that can solve my need.

As you can see from Figure 2, this extension have a couple of button, the first one, Install, is used to install this extension on one of your VSTS account, but if the extension is compatible with on-premise TFS you have also the option to Download the extension.

This image shows that extensions can be downloaded to local pc to be installed to TFS

Figure 2: Once you find an extension compatible with on-premise TFS you can download it.

The extension is a simple .vsix file, and from extension administration page you can upload to your TFS.

This image shows how you can upload downloaded extension to your TFS instance.

Figure 3: Once downloaded you can upload the extension to your TFS.

The process of uploading is really simple:

This image shows how you can choose the file to upload

Figure 4: Browse for downloaded extension

Tfs shows info for file to upload

Figure 5: Upload UI shows all the detail of the file

Now you can press Upload button, and the extension is uploaded to TFS and ready to be activated.

TFS shows the list of uploaded and installed extensions

Figure 6: Uploaded extension is now listed in the list of available extension

For each extension TFS ask you to install in specific project collection

  1. Figure 7: Install the extension on one or more collection

The concept behind “install then activate” is that, once uploaded the extension is available for the TFS Server instance, but then you should install in each Project Collection where you want to use that extension.

TFS asks you to choose the collection where you want to use the extension.

Figure 8: Choose the collection you want to install the extension into

Once you choose the Project Collection you need to confirm the operation

Figure 9: Confirm the selection

Once the Extension is installed in one Project Collection it can be used. In this example the extension contains a new build Task that allows you to execute an arbitrary PowerShell script during the build. To verify that everything is ok you can create a build and verify that the new Task is available and ready to use.

the extension is used in a build, where I was able to add the new task that executes arbitrary powershell code

Figure 10: The extension is ready and can be used

Thanks to TFS 2015 Update 2 you can Download and install extension for VSTS Marketplace to your on-premise TFS installation with few clicks.

Gian Maria.

Change how MongoDb C# Driver serialize Guid in new driver version

We have code based on the old legacy mongo driver that uses MemberSerializationOptionsConvention class to serialize all Guid as plain strings. This option is really useful because saving Guid in Bson format is often source of confusion (CSUUID, … ) for .NET users. On the contrary, having saving Guid as plain string solves some pain and made your Bson really more readable.

Here is the old code that runs with the old legacy driver.

var conventions = new ConventionPack
        new MemberSerializationOptionsConvention(
            typeof (Guid),
            new RepresentationSerializationOptions(BsonType.String)
ConventionRegistry.Register("guidstring", conventions, t =&gt; true);

In the new Driver, these classes are removed, so we need to find a different way to serialize Guid as strings. Here is a possible solution.

public static void RegisterMongoConversions(params String[] protectedAssemblies)
    var guidConversion = new ConventionPack();
    guidConversion.Add(new GuidAsStringRepresentationConvention(protectedAssemblies.ToList()));
    ConventionRegistry.Register("guidstring", guidConversion, t =&gt; true);

This class uses a custom class called GuidAsStringRepresentationConvention, used to instruct Mongo serializer to serialize Guid as plain String. This class accepts a list of string that represents a list of protected assemblies. The reason behind it is that we do not want this convention to be applied to every type loaded into memory, and we want to avoid to change how Guid are serialized for types stored in assembly we do not have control to.

Here is the code of the class.

/// <summary>
/// A convention that allows you to set the serialization representation of guid to a simple string
/// </summary>
public class GuidAsStringRepresentationConvention : ConventionBase, IMemberMapConvention
    private List protectedAssemblies;

    // constructors
    /// <summary>
    /// Initializes a new instance of the  class.
    /// </summary>  
    public GuidAsStringRepresentationConvention(List protectedAssemblies)
        this.protectedAssemblies = protectedAssemblies;

    /// <summary>
    /// Applies a modification to the member map.
    /// </summary>
    /// The member map.
    public void Apply(BsonMemberMap memberMap)
        var memberTypeInfo = memberMap.MemberType.GetTypeInfo();
        if (memberTypeInfo == typeof(Guid))
            var declaringTypeAssembly = memberMap.ClassMap.ClassType.Assembly;
            var asmName = declaringTypeAssembly.GetName().Name;
            if (protectedAssemblies.Any(a =&gt; a.Equals(asmName, StringComparison.OrdinalIgnoreCase)))

            var serializer = memberMap.GetSerializer();
            var representationConfigurableSerializer = serializer as IRepresentationConfigurable;
            if (representationConfigurableSerializer != null)
                BsonType _representation = BsonType.String;
                var reconfiguredSerializer = representationConfigurableSerializer.WithRepresentation(_representation);

The code is really simple, it scan each memberMap to verify if the memberMap is of type GUID, but if the type is declared in a protected assembly, simply return, because we want to leave serialization as-is. If the type is not in a protected assembly and is of type GUID, we simply change the representation of the serialization to be a BsonType.String.

The result is that now, each property of type GUID, will be serialized as plain string into MongoDb.

Gian Maria.

Nuget default configuration

If you use MyGet as package source or some other Nuget Feed and your code fail to build in a buildserver because it cannot retrieve some package, probably you have some problem in NuGet configuration.

If you issue a nuget.exe source list  in command line, you should be able to see all package source configured. Actually I had a problem in a TeamCity build server, after we added a new package source to a solution and pushed code to the server the build was broken. The error is due to the inability to download packages from the new source. Running nuget.exe source list command confirmed me that the new package source was not listed, so it is clear why the package was missing.

If you have this kind of a problem, just know that nuget has a default configuration file that can be used to set all sources for all users in the machine. In our build server this file is missing, so I simply created it listing all the package source we need and the build started working like a charm.

Gian Maria.