Isolate your TFS Pre-Production environment for maximum security

In previous post I’ve explained how to create a clone of your TFS Production environment thanks to the new TFS “15” wizard. With this post I want to share you a simple solution I have in my Bags of Tricks to avoid your TFS Cloned environment to interfere with production environment.

The problem

In my environment I have all machines in network, my TFS has address and Primary Domain Controller is Then I have automated build and Release Management Definitions that deploy against various machines, :,,, etc.

Even if I used the wizard, or Command Line instructions to change TFS Servers id, there is always the risk that, if a build starts from cloned environment, something wrong will be deployed to machines used by production environment (, etc).

Usually the trick of changing hosts files in PreProduction TFS machines is good if you always use machine names in your build definition, but if I have a build that directly deploy to there is nothing to do. This exposes me at risk of production environment corruption and limits my freedom to freely use cloned TFS environment.

What I want is complete freedom to work with cloned TFS Environment without ANY risk of accessing production machines from any machine of Cloned Enviroments (Build controllers, test agents, etc).

Virtualization to the rescue

Instead of placing pre-production environment in my network, I use Hyper-V virtual networking capabilities to create an internal network.


Figure 1: Virtual networks configured in Hyper-V hosts

In Figure 1 I depicted what I have clicking the Virtual Switch Manager setting (1), and I have a virtual switch called “Internal Network” (2) that is configured as internal network (3). This means that this network can be used by all VM to communicate between them and with the host, but there is no possibility to communicate to the real Production Network. The Physical network card of Hyper-V host is bound to a standard “External Network”, it is called “ReteCablata” (4) and it is the network that can access machines in Production Network.

With this configuration I decided to install all machines that will be used for TFS Pre Production (server, build, etc) using only the “Internal Network”. The machine I’ll use as Pre Production TFS has the address, while the Hyper-V host will have the address. This allows for my Hyper-V hosts to communicate with the Virtual Machine through the Internal Network virtual network interface.

Now if I try to login to the machine with domain credentials I’ve got a bad error as result.


Figure 2: I’m unable to login with domain users, because domain controller is unavailable.

Accessing with local user is good, and the reason why I cannot login as domain user is because the machine is not able to reach the domain controller, since it lives in another virtual network.

Thanks to this solution I’ve created an isolated subnetwork where I can create my TFS Pre-production / Test environment without the risk of corrupting the Production Environment

Thanks to Virtual Networking it is easy to create a virtual network completely isolated from your production environment where you can safely test cloned environment

Iptables to route only what-you-want

At this point I have an isolated environment, but since it cannot access my domain controller, I have two problems:

1) PreProduction / Test Tfs cannot access the domain, and no domain users can access TFS
2) To access the PreProduction / Test TFS you can only use the Hyper-V host.

Clearly this made this approach almost impraticable, but the solution to this limitation is really really quick. Just install a Linux machine in the Hyper-V host to act as a router, in my example I have a standard Ubuntu Cloud server without UI. The important aspect is that you need to assign both Virtual Networks to the machine, so it can connect with both your isolated environment “Internal Network” and production environment “Rete Cablata”.


Figure 3: Create a Linux VM and be sure to assign both network interfaces.

In my box the physical network (ReteCablata) is eth0 while the internal network is eth1, both interface have static ip, and this is the configuration.

gianmaria@linuxtest1:~$ cat /etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
auto eth0
iface eth0 inet static

auto eth1
   # iface eth1 inet dhcp
   iface eth1 inet static

The configuration is simple, this machine has ip in my production network (eth0), and it the ip in the internal virtual network (eth1). Now I configured all Windows machines in the internal virtual network to use this machine as gateway.


Figure 4: Configuration for Pre-Production TFS Machine

The important aspect is that it is using the ip in eth1 of linux machine as gateway (, and it is using as DNS (this is the address of my primary domain controller).

Now I can configure the linux box to become a router between the two networks, you should enable forwarding as first step with the instruction

echo 1 > /proc/sys/net/ipv4/ip_forward

But this works only until you reboot the linux machine, if you want the configuration to survive a reboot you can edit /etc/sysctl.conf and change the line that says net.ipv4.ip_forward = 0 to net.ipv4.ip_forward = 1. When forwarding is enabled, you can configure iptables to route. Here is the configuration:

Disclaimer: I’m not absolutely a linux expert, this is a simple configuration I’ve done after studying a little how iptables works and thanks to articles around the web.

sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -A FORWARD -i eth0 -o eth1 -m state  --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT
sudo iptables -I FORWARD -d -j DROP
sudo iptables -I FORWARD -d -j ACCEPT

The first three instructions are standard rules to configure iptables to act as a router for ALL Traffic between eth0 and eth1 and vice-versa. With only the first three rules all the machines in network that uses (the linux box) as gateway can access internet as well as ALL machine in network (Production Network). This is not enough for me, because with this configuration machine in Cloned TFS Environment has FULL access to production machines.

The fourth rule tells to iptables to DROP all traffic from any direction to subnet This rule will completely isolates the two network, no machine can access the from and vice versa. Then the fifth and last rules tells iptables to ACCEPT all traffic from and to the address, my domain controller.

Thanks to iptables and a Linux box, it is really easy to create a router that selectively filter access from the two networks. This gives you the freedom to decide what machine of production environment can be accessed by cloned environment.

With this configuration I have created an isolated network that is capable of contacting ONLY my domain controller but otherwise is COMPLETELY isolated from my production network. This allows PreProduction / Test TFS machine to join the domain and validate users, but you can safely launch build or whatever you want in any machine of the Cloned environment because all traffic to production machine, except the domain controller, is completely dropped.

How can I access PreProduction environment from client machine

Previous configuration solves only one of my two problems, PreProduction TFS can now access only selected machine of the domain (Domain Controller usually is enough) but how can you let Developers or Manager to access PreProduction environment to test cloned instance? Suppose a developer is using the machine in production network, and he want to access PreProduction TFS at address, how can you have it access without forcing him to connect to Hyper-V host and then use the Hyper-V console?

First of all you need to tell iptables to allow traffic between that specific ip and the isolated virtual network in eth1.

sudo iptables -I FORWARD -d -j ACCEPT

This rules allow traffic with client ip so packets can flow from to every machine in network. This is necessary because we tell iptables to DROP every traffic to except, so you need this rule to allow traffic to client Developer machine. All other machine in production network are still isolated.

Now the developer in still can’t reach the machine because it is in another subnet. To allow this he simply need to add a route rule in his machine. Supposing that the machine is a standard Windows machine, here is the command line the developer need to access Cloned Environment Machines.

route ADD MASK

Thanks to this roule the developer is telling to the system that all traffic to subnet should be routed to, the address of the Ubuntu Linux machine in production environment. Now when developer try to RDP the machine (Cloned TFS Server) all traffic is routed by the linux machine.

Final Consideration

Thanks to this configuration all machines in the network, can contact and be contacted only by selected production machines, avoiding unwanted corruption of your production environment.

This gives you complete contorol on the IP addresses that can access your Cloned environment, reducing the risk of Production Environment corruption to almost zero. You can allow access to selected machine, and also you can control which client machine in your production network can access Cloned Environment.

Remember that, after a reboot, all rules in iptables will be cleared and you need to setup them again. You can configure Linux box to reload all rules upon reboot, but for this kind of environment, I prefer to have the ability to reboot Linux machine to completely reset iptables. Re-applying rules is a matter of couple of seconds.

Gian Maria.

Create a Pre-Production / Test environment for your TFS

There are a lot of legitmate reasons to create a clone of your TFS installation: verifying an upgrade, testing some customization and so on, but traditionally creating a test environment is not an easy task.

The problem is avoiding that the test installation could interfere and corrupt your production instance and since TFS is a complex product, there are a series of steps you need to do to perform this kind of operations. Thankfully with the upcoming version of TFS most of the work will be accomplished with a wizard.

Kudo to TFS Team for including a wizard experience to create a clone of your TFS Environment.

Here are the detailed steps to create a clone environment.

Step 1: Backup Database / install TFS on new Server / Restore Database

First of all you can login to your TFS server, open c:\Program Files\Microsoft Team Foundation Server 14.0\Tools and launch TfsBackup.exe to take a backup of all databases.


Figure 1: Take a backup of your Production Database

You should only specify the name of the instance of SQL Server where you have your production databases. A wizard will start asking you to select databases to backup and the location where you want to place the backup.


Figure 2: Choose databases to backup

The backup routine will perform a full backup.


Figure 3: Backup is taken automatically from the routine

Next step is creating a new Virtual Machine, install Sql Server in a compatible version with TFS “15” preview (I suggest SQL 2016) then install TFS.


Figure 4: Install TFS on the target machine

Once the installer finishes TFS “15” Configuration wizard will appear


Figure 5: Once installer is complete the Configuration Wizard will ask you to configure the server

Now you should go to c:\Program Files\Microsoft Team Foundation Server 15.0\Tools and launch TfsRestore.Exe.


Figure 6: TfsRestore will perform database restore

You should only choose the name of the SQL Server instance you want to use, in this example I’m creating a Pre-Production environment composed by only one machine called RMTEST. You should transfer backup file to target computer or place them in a network share accessible from the Target machine.


Figure 7: Restore routine will prompt you for Backup Location

Once you specify the directory with the backup the wizard will automatically list all the database to restore for you.


Figure 8: Database are restored in Sql Server

Step 2: Extra security precautions to avoid Production corruption

Now all databases are restored in the Sql Server that will be used by the Pre-Production environment and you can start TFS configuration wizard, but I’ll wait to perform extra security precautions.

You should edit the hosts file of Pre-Production machine to redirect to an inexistent IP every machine name used in Production Environment. As an example, I have Build and Release definition that will deploy software on demo machines, and I want to prevent that a build triggered on Pre-Production TFS Instance will access Production servers.

As extra security tip, I suggest you to use the hosts file trick to minimize the risk of Production Environment corruption


Figure 9: Editing hosts file will guarantee extra safety net against Production environment corruption

As an example, in Figure 9 I showed a typical hosts file, the Production instance is called TFS2013PreviewOneBox so I redirect this name to localhost in the new name. Then I redirect all machines used as deploy target or build server etc to that is an unexistent IP.

You can also create some network rules to isolate the Pre-Production machine from the Production Environment completely, such as placing it in another network segment and prevent routing entirely, but using the hosts file is a simpler approach that works well for small and medium installation.

Step 3: Perform the configuration with new TFS “15” wizard

Before TFS “15” now you should resort to command line trickery to change server id from database etc, but luckily you can do everything using configuration wizard. Lets come back to Configuration Wizard and choose the option “I have existing database to use … “


Figure 10: Start upgrade wizard using existing databases

The wizard will prompt you to choose the instance of Sql Server and the database to use.


Figure 11: Choose database to use for the upgrade

Until now it is the standard Upgrade Wizard, but the next screen is the great news of this new installer because it will present you the option to create a Pre-Production environment.


Figure 12: This is the new option, you can choose to create a Pre-Production Upgrade Testing

Pressing Next you will see another screen that reminds you the step that the wizard will perform to create the Clone environment. As you can see the wizard will take care of remapping connection string, changing all identifiers and remove all scheduled backup jobs.


Figure 13: Overview of the Pre-Production scenario

Thanks to the wizard you can create a Test Clone of your Production TFS without worrying to corrupt your Production environment. The wizard will takes care of everything

Now the wizard will continue, but there is another good surprise, each screen contains suggestions to minimize risk of Production Environment corruption.


Figure 14: Wizard suggests you to use a different user to run TFS Services

The suggestion in Figure 14 is the most important one, I usually use an account called TfsService to run my TFS Server and that account has several privileges in my network. In the Pre-Production environment it is better to use standard Network Service account or a different account. This is a really important security settings, because if the Pre-Production server will try to perform some operations on other servers it will probably be blocked because the account has no right permission.

Never use for Pre-Production environment the same users that you use for Production environment to minimize risk of corruption. Use Network Service or users with no privilege in the network created specifically for Cloned Environments

Clearly the wizard will suggest you to use different url than production server. Resist the temptation to use the same url and rely on hosts file redirection, it is really better to use a new name. This will allows you to communicate this new name to the team and ask them to access Pre-Production server to verify that everything is working, as an example after a test upgrade.


Figure 15: Use a different url than production environment

You can now follow the wizard, basically the screen are the same of the upgrade, but each screen will suggest you to use different accounts and different resources than production instance.

At the end of the wizard you will have a perfect clone of your production environment to play with.


Figure 16: Configuration is finished, you have now a Clone of your environment.

Step 4: extra steps for furter extra security

If you want to be Extra sure that your production environment is safe from corruption, configure the firewall of your Production system to block any access from IP of any machine part of cloned environment. This extra security measure will prevent human errors.

Some customers have custom software that connect to TFS instance to perform some custom logic. As an example you could have software that use bisubsribe.exe or hooks to listen to TFS events then send command to TFS. Suppose you want to test this kind of software against your Cloned environment, so you let people install and configure everything on Pre-Production machine, but someone did a bad mistake and configured the software to listen to the Pre-Production environment, but send command against Production Environment. If you blocked all traffic from Pre-Production machines to your TFS Production environment you will be protected against this kind of mistake.

If you are good in networking, probably the best solution is creating all machines part of Pre-Production environment (TFS, SQL, build server, etc) in another network segment, than configure routing / firewall to allow machines in pre-prod network to access only domain controllers or in general to access only machine that are stricly needed. This will prevent machines from Pre-Production environment to connect any machine of your Production Environment. You can then allow selected ip from your regular network to access Pre-Production for testing.

Gian Maria

Impressions on installing TFS “15” Preview

Microsoft released a preview of the new version of Team Foundation Server, codename TFS “15” and as usual I immediately downloaded and installed on some of my test server. I’m not going to show you full steps of installation or upgrade, because installing TFS is now a Next/Next/Next experience, but I want to highlight a couple of really interesting aspect of the new installer.

Support to Pre-Production environment

When it is time to upgrade your TFS production server, it is always a good practice to perform the upgrade in a pre-production environment, to avoid having surprises upgrading production instance. Traditionally this is a not so immediate task, because you need to backup and restore database to a test server, then run some command line instructions to change the id of the server in db and then perform the upgrade.

I’ve done a post in the past on how to create a safe clone of your TFS Environment and I suggest you to have a read of that post, but with the new version of TFS installer, when you choose the upgrade path, you have this new screen


Figure 1: You can choose to create a pre-production test directly from Wizard

This is a really big improvement, because now you can simply backup and restore your databases on a new server, run the wizard, choose to do a pre-production Upgrade Testing and you have a clone of you server upgraded to the new version where you can do all of your tests to verify the upgrade process.

Thanks to Pre-Production Upgrade Testing option, creating a test clone of your server where to perform the upgrade is a breeze

This option is not only useful for upgrades, but allows you to quickly create a clone of your TFS to do whatever experiment you want to do with real Production data, without harming your production server.

Great work Microsoft!!

Code search and ElasticSearch

Another interesting wizard screen is the one dedicated to installing code Search capabilities.


Figure 2: Code search installing screen

Code search was introduced in VSTS lots of time ago, but now is available even on TFS installation. It uses ElasticSearch under the hood, but the installer takes care of everything for you. From Figure 2 you can see that you only need to specify a folder where to store the index, and choose if you want to enable code search for every project collection and the game is done.

Sadly enough, you are not allowed to use an exising installation of ElasticSearch, here is the error you got if you already have ES installed on the machine.


Figure 3: You cannot re-use an existing ES installation

You should uninstall every installation of ES you have if your machine before installing code search capabilities. Since ES depends on Java, if you do not have Java JRE or JDK installed on the machine the wizard gives you an error during the verification process.


Figure 4: An error is present if your machine has no Java installed

You can check the box where you accept the Oracle Binary Code License Agreement and the installer will download and install Java for you, or you can simply download and install Java manually, then re-run the Readiness Checks. If you are going to do a manual install of Java or if you have a pre-existing Java installation please check in ES site if your version is compatibile or have some problem.

Usually you have no Java in your TFS Box, so it is safe to check the checkbox and have the installer do everything for your automatically.

SSH Support

To support SSH Protocol for Git, you should tell the installer that you want to enable the SSH Service and choose the port (22 by default)


Figure 5: SSH Support in TFS for Git

As TFS Evolves the Configure / Upgrade Wizard is more and more complete, and allows you to istall TFS without leaving the Wizard, and configuring / installing all dependent services for you.

Gian Maria.

Running Unit Tests on different machine during TFS 2015 build

First of all I need to thanks my friend Jackob Ehn that pointed me to the right direction to create a particular build.  In this post I’ll share with you my journey to run tests on a different machine than the one that is running the build.

For some build it is interesting to have the ability to run some Unit Test (nunit in my scenario) on a machine different from that one that is running the build. There are a lot of legitimate reasons for doing this, for a project I’m working with, to run a set of test I need to have a huge amount of pre-requisites installed (LibreOffice, ghostscript, etc). Instead of installing those prerequisite on all agent machines, or install those one on a single build agent and using capabilities, I’d like to being able to run the build on any build agent, but run the test in a specific machine that had all the prerequisite installed.

Sometimes it is necessary to run tests during build on machine different from that one where the build agent is running.

The solution is quite simple, because VSTS / TFS already had all build tasks needed to solve my need and to execute tests on different machine.

The very first steps is copying all the dlls that contains tests on the target machine, this is accomplished by the Windows Machine File Copy task.


Figure 1: File copy task in action

This is a really simple task, the only suggestion is to never specify the password in clear format, because everyone that can edit the build can read the password. In this situation the password is stored in the RmTestAdminPassword variable, and that variable is setup as secret.


Figure 2: Store sensitive information as secret variables of the build

Then we need to add a Visual Studio Test Agent Deployment task, to deploy Visual Studio Test Runner on target machine.


Figure 3: Visual Studio Test Agent Deployment

Configuration is straightforward, you need to specify a machine group or a list of target machines (point 1), then you should specify the user that will be used to run test agent (point 2), finally I’ve specified a custom location in my network for the Test Agent Installer. If you do not specify anything, the agent is downloaded from but this will download approximately 130MB of data. For faster build it would be preferrable to download the agent and move the installer in a shared network folder to instruct the Task to grab the agent from that location.

Finally you use the Run Functional Tests task to actually execute tests in the target machine.


Figure 4: Running functional test from target machine configuration

You specify the machine(s) you want to use (point 1), then all the dll that contains tests (point 2) and you can also specify code coverage (point 3). Even if the task is called Run Functional Tests, it actually use Visual Studio Test runner to run tests, so you can run whatever test you like.

Thanks to TFS 2015 / VSTS build, we already have all tasks needed to run Unit Tests on target machines.

If you are running Nunit test or whatever test framework different from MsTest, this task will fail, because the target machine has no test adapter to run the test. The failure output tells you that the agent was not capable of finding any test to run in specified location. This happens even if you added Nuget Nunit adapter to the project. The solution is simple, first of all locates all needed dll in package location of your project.


Figure 5: Installing Nunit TestAdapter Nuget package, downloads all required dll to your machine

Once you located those four dll in your HD, you should copy them to the target machine in folder: C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow\Extensions, this folder was created by Visual Studio Test Agent deployment task and will contain all extension that will be automatically loaded by Visual Studio Test Agent. Once you copied the dll, those machine will be able to run nunit test without problem.

Once you copied all required dll in target folder, re-run the build, and verify that tests are indeed executed on the target machine.


Figure 6: Output of running tests on remote machine

Test output is transferred to the build machine, and attached to the build result as usual, so you do not need anything else to visualize test result in the same way as if the test were executed by agent machine.


Figure 7: Output of test is included in the build output like standard Unit Tests ran by the build agent

Once tasks are in place, everything is carried over by the test agent, test results are downloaded and attached to the build results, as for standard unit tests executed on Build Agent machine.

The only drawback of this approach is that it needs some times (in my system about 30 seconds) before the test started execution in target machine, but apart this problem, you can execute tests on remote machine with little effort.

Gian Maria.

Avoid using Shell command in PowerShell scipts

I have setup scripts that are used to install software, they are simply based on this paradigm

The build produces a zip file that contains everything needed to install the software, then we have a script that accepts the zip file as parameter as well as some other parameters and does install sofwtare on a local machine

This simple paradigm is perfect, because we can manually install a software launching powershell, or we can create a Chocolatey package to automate the installation. Clearly you can use the very same script to release the software inside TFS Release Management.

When PowerShell script runs in the RM Agent (or in a build agent) it has no access to the Shell. This is usually the default, because the agents does not run interactively and usually in Release Management, PowerShell scripts are executed remotely. This fact imply that you should not use anything related to shell in your script. Unfortunately, if you looks in the internet for code to unzip a zip file with PowerShell you can find code that uses shell.application object:

function Expand-WithShell(
    [string] $zipFile,
    [string] $destinationFolder,
    [bool] $deleteOld = $true,
    [bool] $quietMode = $false) 
    $shell = new-object -com shell.application

    if ((Test-Path $destinationFolder) -and $deleteOld)
          Remove-Item $destinationFolder -Recurse -Force

    New-Item $destinationFolder -ItemType directory

    $zip = $shell.NameSpace($zipFile)
    foreach($item in $zip.items())
        if (!$quietMode) { Write-Host "unzipping " + $item.Name }

This is absolutely a problem if you run a script that uses this function in a build or RM agent. A real better alternative is a funcion that uses classes from Framework.

function Expand-WithFramework(
    [string] $zipFile,
    [string] $destinationFolder,
    [bool] $deleteOld = $true
    Add-Type -AssemblyName System.IO.Compression.FileSystem
    if ((Test-Path $destinationFolder) -and $deleteOld)
          Remove-Item $destinationFolder -Recurse -Force
    [System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $destinationFolder)

With this function you are not using anything related to Shell, and it can be run without problem even during a build or a release management. As you can see the function is simple and probably is a better solution even for interactive run of the script.

Another solution is using 7zip from command line, it gives you a better compression, it is free, but you need to install into any server where you want to run the script. This imply that probably zip files are still the simplest way to package your build artifacts for deployment.

Gian Maria.