github twitter linkedin email rss
Running Latest .NET Core on VSTS Hosted Build Agent
Jul 7, 2016
3 minutes read

Depending on your project, you might need to run the latest version of the .NET Core SDK on your hosted build agent. Hosted agents are pulled from the VSTS hosted pool. With great flexibility comes great responsibility, so the build agent has some limitations when it comes to picking the software that needs to be deployed.

Specifically, if you go over the existing constraints, you will notice that there are two that will prevent a simple silent install:

Q: Does your build depend on software other than this software that is installed on hosted build resources?

A: No. Then you can use the hosted pool.

Q: Do any of the processes for your build need administrator privileges?

A: No. Then you can use the hosted pool.

So what do you do in this case? The great thing about the .NET Core SDK is the fact that you don’t really need to perform an install, but rather simply copy the files to the build agent and re-set the PATH environment variable to point to the newly deployed SDK.

NOTE: The original .NET Core SDK is installed under C:\Program Files\dotnet. You will get an “Access Denied” error if you attempt to overwrite the contents.

The solution revolves around some PowerShell trickery. First, you need to create a new script that will download and extract the .NET Core SDK. In my case, I deployed this to my GitHub repo:

[reflection.assembly]::LoadWithPartialName("System.Net.Http") | Out-Null
[reflection.assembly]::LoadWithPartialName("System.Threading") | Out-Null
[reflection.assembly]::LoadWithPartialName("System.IO.Compression.FileSystem") | Out-Null

$SourcePath = "https://dotnetcli.blob.core.windows.net/dotnet/Sdk/rel-1.0.0/dotnet-dev-win-x64.latest.zip";
$DestinationPath = "C:\dotnet"
$TempPath = [System.IO.Path]::GetTempFileName()

if (($SourcePath -as [System.URI]).AbsoluteURI -ne $null)
{
    $handler = New-Object System.Net.Http.HttpClientHandler
    $client = New-Object System.Net.Http.HttpClient($handler)
    $client.Timeout = New-Object System.TimeSpan(0, 30, 0)
    $cancelTokenSource = New-Object System.Threading.CancellationTokenSource
    $uri = New-Object -TypeName System.Uri $SourcePath
    $responseMsg = $client.GetAsync($uri, $cancelTokenSource.Token)
    $responseMsg.Wait()

    if (!$responseMsg.IsCanceled)
    {
        $response = $responseMsg.Result
        if ($response.IsSuccessStatusCode)
        {
            $fileMode = [System.IO.FileMode]::Create
            $fileAccess = [System.IO.FileAccess]::Write
            $downloadedFileStream = New-Object System.IO.FileStream $TempPath,$fileMode,$fileAccess
            $copyStreamOp = $response.Content.CopyToAsync($downloadedFileStream)
            $copyStreamOp.Wait()
            $downloadedFileStream.Close()

            if ($copyStreamOp.Exception -ne $null)
            {
                throw $copyStreamOp.Exception
            }
        }
    }
}
else
{
    throw "Cannot copy from $SourcePath"
}

[System.IO.Compression.ZipFile]::ExtractToDirectory($TempPath, $DestinationPath)
Remove-Item $TempPath

The URL for the 1.0.0 release is hard-coded in the script – I am downloading it and extracting locally. Remember that in a hosted agent, no files are persistent, so post-build those are deleted. You will need to re-download the package every single time you want to kick off a new build.

Once the package is deployed, you will need to simply run this snippet of PowerShell code:

dotnet --version; 
$path = Get-ChildItem Env:path; 
Write-Host $path.Value; 
$pathValue = $path.Value -Replace "C:\\Program Files\\dotnet","C:\dotnet"; 
Write-Host $pathValue; 
$env:Path = $pathValue; 
dotnet --version

What this snippet does is get the PATH from the current environment variables, swap it for the right location for the .NET Core SDK and then display it – for your convenience, I am also showing the dotnet version before an after. You can reduce it to just the necessary parts.

After the execution, you will be able to trigger dotnet build and dotnet run (and other capabilities) with the latest version.


Back to posts