Hello!

Yesterday I posted a bit of PowerShell that uses the TFS API to return the last good deployment for a given release and release environment. It was all good until I realised that do part will continue to take longer and longer, and the $result get bigger and bigger as more releases are created. This is because I retrieve ALL the releases before querying them as the until ends when there are no more continuationtoken returned from the Invoke-WebRequest. At the moment there are less than 200 releases, but in a very short space of time that number could be 10* that .All this means that bit of code can be filed under “good idea poorly executed”.

So, I spent a bit of time this morning altering the script, not only to make it a little bit cleaner and a bit more resilient to networks, but also to grab a bunch of deployments and then check to see if they fit the criteria. This will mean that the function won't really get that much slower as time goes on, and I'm less likely to cause memory issues when running said script. I also added an option so that the scripts can be run as part of the build/release to make use of the $env:systemaccesstoken that exists when running in a TFS pipeline. This little change also addresses my moaning about not being able to go back a set number of days: the mains reason I wanted this was so that I did not get the entire history.

Incidentally, the reason that I use Invoke-WebRequest is because the headers are not returned when using Invoke-RestMethod


Function Get-TfsLastSuccessfulDeploymentByReleaseAndEnvironment {
    [CmdletBinding()]
    param(
        [ValidateNotNullOrEmpty()]
        $result,
        [string]
        [ValidateNotNullOrEmpty()]
        $releaseName,
        [string]
        [ValidateNotNullOrEmpty()]
        $releaseEnvironmentName
    )
    $resultAsJson = $result | ConvertFrom-Json
    $FilteredReleases = $resultAsJson.Value | Where-Object { ($_.releaseDefinition.name -eq $releaseName) -and ($_.releaseEnvironment.name -eq $releaseEnvironmentName) }
    $FilteredRelease = $FilteredReleases | Sort-Object -Property $_.release.Id -Descending | Select-Object -First 1
    return $FilteredRelease
}

Function Get-SuccessfulDeployments {
    [CmdletBinding()]
    param(
        [string]
        [ValidateNotNullOrEmpty()]
        $Uri
        , [string]
        $base64Auth
    )
    $retryCount = 0
    $retries = 5
    $result = $null
    while ($null -eq $result) {
        try {
            if ($PSBoundParameters.ContainsKey('base64Auth') -eq $true) {
                $result = Invoke-WebRequest -Uri $uri -Method GET -ContentType "application/json" -Headers @{Authorization = ("Basic {0}" -f $base64Auth) }
            }
            else {
                $result = Invoke-WebRequest -Uri $uri -Method GET -ContentType "application/json" -Headers @{Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" }
            }
        }
        catch {
            if ($retryCount -gt $retries) {
                Write-Host "Failed to get response from $($uri)"
                Throw
            }
            else {
                Write-Host "Request to $uri failed. Retrying in $retries seconds..."
                Start-Sleep -Seconds $retries
                $retryCount ++
            }
        }
    }
    return $result
}
Function Get-TfsDeployment {
    [CmdletBinding()]
    param(
        [string]
        [ValidateNotNullOrEmpty()]
        $tfsUrl
        , [string]
        [ValidateNotNullOrEmpty()]
        $releaseName,
        [ValidateNotNullOrEmpty()]
        $releaseEnvironmentName
        , [string]
        $user
        , [string]
        $token
    )
    if ($PSBoundParameters.ContainsKey('user') -eq $true) {
        $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user, $token))) 
    }
    $uri = $tfsUrl + '/_apis/release/deployments?deploymentStatus=succeeded&$top=25'
    Write-Host $uri
    try {
        if ($PSBoundParameters.ContainsKey('user') -eq $true) {
            $result = Get-SuccessfulDeployments -Uri $uri -base64Auth $base64AuthInfo
            $continuationToken = $result.headers['x-ms-continuationtoken']
            do {
                $releaseToCheck = Get-TfsLastSuccessfulDeploymentByReleaseAndEnvironment -result $result -releaseName $releaseName -releaseEnvironmentName $releaseEnvironmentName
                $ThisUri = "$($uri)&continuationToken=$($continuationToken)"
                $result = Get-SuccessfulDeployments -Uri $thisUri -base64Auth $base64AuthInfo
                $continuationToken = $result.headers['x-ms-continuationtoken']
                }
            until(
                $null -eq $continuationToken -or $null -ne $releaseToCheck.Id
            )
        }
        else {
            $result = Get-SuccessfulDeployments -Uri $uri -base64Auth $base64AuthInfo
            $result = Invoke-WebRequest -Uri $uri -Method GET -ContentType "application/json" -Headers @{Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" }
            $continuationToken = $result.Headers['x-ms-continuationtoken']
            do {
                $releaseToCheck = Get-TfsLastSuccessfulDeploymentByReleaseAndEnvironment -result $result -releaseName $releaseName -releaseEnvironmentName $releaseEnvironmentName
                $ThisUri = "$($uri)&continuationToken=$($continuationToken)"
                $result = Get-SuccessfulDeployments -Uri $thisUri -base64Auth $base64AuthInfo
                $continuationToken = $result.Headers['x-ms-continuationtoken']                
                }
            until(
                $null -eq $continuationToken -or $releaseToCheck.Count -eq 1
            )
        }if ($null -eq $releaseToCheck) {
            Write-Host "no release found."
        }
        else {
            return $releaseToCheck
        }
    }
    catch {
        Throw $_
    }
}

$_tfsUrl = 'http://mytfsurl:8080/tfs/team/project'
$_releaseName = 'my-super-release'
$_releaseEnvironmentName = 'dev-env'
$_user = 'domain\tfsadmin'
$_token = 'c6ylh4y3wu44e4gjki62it3gwh83174361923229838682'

$myDeployment = Get-TfsDeployment -tfsUrl $_tfsUrl -releaseName $_releaseName -releaseEnvironmentName $_releaseEnvironmentName -user $_user -token $_token

$myDeployment