Update Portal Settings for multiple team projects

There is a great Codeplx project that allows you to update Portal Settings for multiple team projects at the same time: https://features4tfs.codeplex.com/. Whenever you upgrade TFS, and have to re-enable and re-point team projects in your upgraded TFS project collections. If you have a lot of team projects to update, it becomes very tedious task. Feature4TFS tool allows you to update all of those items in one step. Very handy tool. The only issue with this tool is that it does not work with TFS 2015 Update 3 (only Update 2 or earlier.

I have downloaded the source code and updated the solution to make it work with TSF 2015 Update 3. You can download updated solution from https://1drv.ms/f/s!AoNW-kvNWJ9ygsNWPA5LHTJ5fYREXQ. There two files: binaries only and source code and binaries.

Getting output from Remote PowerShell on Target Machines

Remote PowerShell on Target Machines task in TFS/VSTS is awesome. Easy to use and it works. The only things that bugs me about that task is that I don't see the output of the script on the build console output. Luckily, this can be easily fixed. All you have to do that is to use Write-Verbose -verbose instead of Write-Output command inside your PowerShell script.

For example, instead of:

Write-Output "Remote script is executing step 1-2-3"

use the following

Write-Verbose "Remote script is executing step 1-2-3" -verbose

You should now see the remote script output in build console.

Persisting PowerShell variables between steps

In VSTS and in TFS 2015 Update 1 (and higher), you can use the following command to persist PowerShell variables between steps by simply writing it to standard out (Write-Host) in powershell

Write-Host ("##vso[task.setvariable variable=testvar;]testvalue")

 

This will make the variable available to be consumed by the later steps in an input via the $(testvar) syntax and the replacement will happen as expected. It's that easy.

You can see the full list of the commands you can use from the scripts at https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md

Customize TFS build steps on the fly based on build trigger

I have the following scenario. The client currently has multiple build definitions, which are very similar (not identical, but similar), but have different triggers. For example, nightly builds run more/different tests, while gated or CI builds run less tests and perhaps run less steps. And, they want to combine/merge those build definitions into one. With TFS 2015 you can create multiple triggers on the same build definitions, so we should be able to merge those build definitions into one. The problem is that the build steps must be the same, or we need to figure out the way to update those steps on the fly somehow. Having the same steps is not an option, since we want gated/CI builds to be smaller/faster. So, we need to figure out a way to modify build steps on the fly. Somehow. PowerShell to the rescue.

I wrote a small PowerShell script that checks if the running build is gated build and update certain build variables (in my case, I used a variable called TestFilterCriteriaToSet) in memory, which are later consumed by next steps in the build definition.

 

function Is-This-Gated-Build ($buildid)

{

    $buildDetails=Invoke-RestMethod -Uri "http://TFSSERVER:8080/tfs/TFSCOLLECTION/TEAMPROJECT/_apis/build/builds/$($buildid)?sapi-version=2.0" -UseDefaultCredentials -Method Get

    $buildReason = $buildDetails.reason

    if ($buildReason -eq 'CheckInShelveset')

    {

        return $true

    }

    else

    {

        return $false

    }

}

 

if (!$gatedBuild)

{

Write-Output "Running non-gated build..."

    

# DO SOME POWERSHELL MAGIC HERE

 

    $TestFilterCriteriaToSet = "(Name!=TestProcessQueue)"

    Write-Host ("##vso[task.setvariable variable=TestFilterCriteria;]$TestFilterCriteriaToSet")

}

else

{

    Write-Output "Running gated build..."

    $TestFilterCriteriaToSet = "(TestCategory!=LongRunning & TestCategory!=Commit & TestCategory!=IntermittentFailure & TestCategory!=ExecutableLaunch)"

    Write-Host ("##vso[task.setvariable variable=TestFilterCriteria;]$TestFilterCriteriaToSet")

}

 

Next steps: Save the script. Check it in. Add this script as a step in the build definition. Consume TestFilterCriteriaToSet variable in the next step as such $( TestFilterCriteriaToSet). For example, in Visual Studio Test step. That's it.

Downgrade SQL Server database from Enterprise to Standard Edition

I've seen quite a few clients who have SQL Server Enterprise Edition installed, but even though they don't use any of the enterprise features of SQL Server. For example, TFS does not require SQL Server Enterprise Edition, unless of course you need encryption or compression or SQL clusters. It works just fine with SQL Server Standard Edition. So, if you're not using enterprise features, having SQL Server Enterprise Edition installed seems like an overkill. An expensive overkill. Fortunately, this can be fixed. If you need to downgrade SQL Server database from Enterprise Edition to Standard Edition.

First, let's check what edition of SQL Server you're running. To do that open SQL Server Management Studio, connect to your SQL Server and create/run new SQL query:

SELECT @@version

Let's assume that you're running SQL Server Enterprise Edition. To downgrade database from Enterprise Edition to Standard Edition, we need to disable compression on that database. Before we do that we need to make sure we do the following:

  • BACKUP your database because you know… it's smart thing to do
  • Make sure that your SQL Server has enough disk space available to accommodate larger database sizes. Remember that uncompressing a database might/will increase the size of the database.

To disable compression, create/run the following script against the database where you want to disable compression.

SELECT DISTINCT 'ALTER TABLE [' + SCHEMA_NAME(schema_id) + '].[' + NAME + '] REBUILD PARTITION = ALL WITH (DATA_COMPRESSION = NONE);'

FROM sys.partitions p

join sys.objects o

on p.object_id = o.object_id

WHERE o.TYPE = 'u'

and data_compression_desc != 'NONE'

UNION

SELECT 'ALTER INDEX ALL ON [' + SCHEMA_NAME(schema_id) + '].[' + NAME + '] REBUILD PARTITION = ALL WITH (DATA_COMPRESSION = NONE);'

FROM sys.partitions p

join sys.objects o

on p.object_id = o.object_id

WHERE o.TYPE = 'u'

and data_compression_desc != 'NONE'

   

If any of the tables in the database have compression enabled, this script will generate more SQL scripts as an output. When that happens, copy the generated SQL scripts and execute those scripts against the same database to disable compression. Do that for all the databases that have compression enabled. That's it.

Sharing files between Host/VM

Since Windows 8.1 I have had Hyper-V installed on my machine. As an MVP I do a lot of demo’s so this is wonderful. I used to have a spare machine running Windows 2008 just for this.

One thing I have found difficult to do, is share files between my VM and the host machine. All the solutions I was coming across were just not as simple as I would like. Until now. I came across this Blog which outlines 5 ways to achieve this.

Below is, in my opinion the easiest way to share files between a host and VM.

1. Start Hyper-V Manager
2. Right click the Hyper-V host and select "Hyper-V Settings"


image

 

 

 

 

 

 

 

 

3. Ensure the tick box is ticked under Enhanced Session Mode and click OK.

image

 

 

 

 

 

 

4. Right click the virtual machine-> Settings -> Integration Services at bottom left hand side of the menu.
5. Check "Guest Services" and click OK.

image

 

 

 

 

 

 

 

 

 

 

6. Start up the virtual machine, copy files from physical machine and paste in to virtual the machine!

Build and Release Tasks with DropDown Arguments

I have been creating a lot of Build Tasks for the new TFS 2015 Build and Release.

I have discovered a few tricks that were not simple to find. I thought I would Blog about them for my own record and anyone who happens by.

This Blog post makes the assumption that you have already been creating your own build tasks. If not, and you want some background take a look here first.

In this example I created a Release task to perform a DACPAC deployment. The script calls SQLPackage.exe which can do several things. Deploy the changes directly to a Database or generate a Deployment Report, a Drift Report or the SQL that needs to be run to update the target, amongst others.

So I figured I would make my Build Task smart enough to let the end user select which action they would they would like to perform. To do that I need the user to select which action and have that passed into my Powershell script.

The input value that is defined in my task.json file looks like this.

{
   "name": "action",
   "type": "pickList",
   "label": "Action",
   "defaultValue": "Publish",
   "required": true,
   "helpMarkDown": "Select the Action to perform",
   "options": {
     "Publish": "Publish Changes",
     "Script": "Script Changes",
     "DeployReport": "Generate Deployment Report"
   }
 }

The type needs to be “picklist”. I also needed to define options. Each option is comprised of two strings seperated by a semicolon. The string before each semicolon is the value to be returned and the string after the semicolon is the display name. Notice I set one of the values to be the defaultValue.

Once published to TFS the build task inputs will look something like this.

image

 

 

 

 

 

 

 

Now the selected value will be passed into my Script and I can change my call to SQLPackage.exe accordingly.

Making a TFS Build wait

In my career I have created Automated builds for many different technologies. TFS never lets me down. The old XAML build did a good job however the new TFS 2015 Builds are awesome. First of all I love PowerShell, which makes my life much easier. I can just call out to a script sitting in source control or better yet create my own Build Task and upload it to the server.

I recently had to create a build for a development tool that generates documents from various system data. This tool has it’s own editor, version control,  release management everything. It’s kind of a closed eco system.

So how do we get the rest of the team involved in it’s builds and deployments. How do I trigger a build and have this system drop a deployable package for me, that I can deploy to different environments without having to ask the developers? Letting QA be responsible for their own environment.

After talking with an expert in this tool. It turns out, it also has an automation component. The developers can write scripts to perform builds and deployments. However we want this to be part of a larger eco system with our other applications.

It was decided that we would create a file share on the network where I could drop a file to let the other system know I wanted a new build dropped. This way TFS would still generate a build number the testers can deploy to the QA environment and reference from their test plan so we get the full end to end traceability.

So I wrote a script that would drop a file (Build.txt) in a known location on the network. Said products automation tool watches for my file and triggers a script to perform a build when it sees it and then drops that build package back for me to turn into a build artifact. I will put the build number in this file so the other system can use that as a label in it’s own Version Control system.

Then the build waits and watches for a file named BuildComplete.txt to be placed back in that working folder. When my build sees it I know the built package is there and I can create a build artifact. By the way the BuildComplete.txt either contains the word successful or an error log if something went wrong. I will pull that out and toss it up into the build console.

My build has two tasks one to Drop the file that triggers the other system and one to create a build artifact. The second one is already built into TFS “Copy and Publish Build Artifacts”. The first step is one Powershell script. See below with Comments:

The Powershell script takes two arguments The WorkingFolder and a timeout.

image

 

 

 

The comments in line explain how it works.

   1: param()
   2:  
   3: #This is how we do parameters in a custom build task 
   4: Trace-VstsEnteringInvocation $MyInvocation
   5: try {
   6:     Import-VstsLocStrings "$PSScriptRoot\Task.json" 
   7:     [int32]$timeout = Get-VstsInput -Name TimeOut
   8:     [string]$WorkingFolder = Get-VstsInput -Name WorkingFolder 
   9: } 
  10: finally 
  11: { 
  12:      Trace-VstsLeavingInvocation $MyInvocation 
  13: } 
  14:  
  15: #Start the Timer at zero
  16: $timesofar = 0
  17: #This is the file we are looking for to know the package is complete 
  18: $BuildComplete = "$WorkingFolder\BuildComplete.txt"
  19: #Clean the Working Folder
  20: Remove-Item  "$WorkingFolder\*"
  21: #Create file containing Build number to trigger the build
  22: New-Item -path "$WorkingFolder" -name 'Build.txt' -type 'file' -value "$env:BUILD_BUILDNUMBER"
  23:  
  24: Write-Host "Triggered Build"
  25: #While BuildComplete.txt does not exist and timer has not reached the timeout specified
  26: while (!(Test-Path $BuildComplete) -and !($timesofar -eq $timeout)) 
  27: { 
  28:     #Wait for 10 seconds
  29:     Start-Sleep 10 
  30:     #Add 10 seconds to the time So Far
  31:     $timesofar = $timesofar + 10
  32: }
  33:  
  34: #Either the File has been dropped by the other system or the timeout was reached
  35: if (!(Test-Path $BuildComplete)) 
  36: {
  37:     #If the file was not dropped it must be the timeout so fail the build write to the console
  38:     Write-Error "Build Timed out after $timeout seconds."
  39: }
  40: else
  41: {
  42:     #If we got here the file was dropped
  43:     #Get the content of the file
  44:     $BuildResult = Get-Content $BuildComplete -Raw
  45:  
  46:     #Check if it says successful
  47:     if ($BuildResult.StartsWith("successful"))
  48:     {
  49:         #On Success say so
  50:         Write-Host "Successful Build"
  51:         #write the contents out in case the calling system wants to tell us something
  52:         write-Host "$BuildResult"
  53:     }
  54:     else
  55:     {
  56:         #On Error Faile the build and say so 
  57:         Write-Error "Build Error"
  58:         #write the contents out so we can see why it failed
  59:         Write-Error "$BuildResult"
  60:     }
  61: }

Build Toronto June 10th 2016

  • 400+ attendees in Toronto (Allstream Centre)
  • 1000+ attendees online on Channel 9 with live Q&A
  • Keynote highlighting the most important announcements from Build 2016.
  • Deep Dive sessions on Universal Windows Platform, Xamarin, Web Apps, Project Centennial, and Azure IoT.
  • Live Panel discussion with influential members of the CDN Dev Community
  • Free to attend. Space is limited.

DevOps Days Hackathon Toronto

One day DevOps Hackathon event is coming to Toronto on May 25th. It's a full day event where you will get a chance to chance for some hands-on learning experience with DevOps concepts and automation using tools from Microsoft & Chef. Here is an agenda for hackathon event:

Agenda:

9:00 AM – 9:30 AM Registration, Breakfast & Introduction

9:30 AM – 10:00 AM Session I – DevOps and the Cloud: Microsoft Azure

10:00 AM – 10:45 AM Session II – Chef + Microsoft Azure, Managing your cloud consistently, securely and transparently

11:00 AM – 12:00 PM Hackathon Begins

12:00 PM – 1:00 PM Working Lunch

1:00 PM – 3:00 PM Hackathon Continues

3:00 PM – 4:45 PM Hackathon Ends & Group Presentations (5 min/group)

4:45 PM – 5:00 PM Closing Remarks & Winners Announced

 

If you are interested in participating, fill free to register at https://www.eventbrite.com/e/devops-days-toronto-2016-tickets-17038721274?access=DEVOPSTOHACKATHON. This event will take place a day before DevOps Days Toronto kicks off. It's an excellent event, which is unfortunately already all booked, but hackathon event has only recently opened up and promises to be very informative. So, go ahead and register. It will be fun… See you there.