BuildDrop cleanup

There has been a lot of errors with various versions of TFS/DevOps Server about obsoleted builds in the drop folders, and the various versions have fixed part of it. Here comes a script to identify them.

To identify things, we don’t know, we need to get a list of what we know and then compare that with the actual dropfolders.
The following function will iterate through all build definitions in a project and find all the corresponding builds artifact locations

Function Get-TFSBuildDrops{
    param (
        [string]$ServerURL,
        [string]$Project
    )
    $URL = "$($ServerURL)/$($Project)"

    #Get all builddefinitions in Project
    $Builddefinitions = (Invoke-RestMethod -Uri ($URL + '/_apis/build/definitions?api-version=3.2') -Method GET -UseDefaultCredentials).value

    $Builds = @()
    foreach($Builddefinition in $Builddefinitions)
    {
        #Get the Build IDs
        $BuildIDs= (Invoke-RestMethod -Uri ($URL + '/_apis/build/builds?api-version=2.0&definitions=' + $Builddefinition.ID) -Method Get -UseDefaultCredentials).value.ID
        foreach ($Build in $BuildIDs)
        {
            #Get Artifacts
            $Artifacts = (Invoke-RestMethod -Uri ($URL + '/_apis/build/builds/' + $Build + '/artifacts') -Method Get -UseDefaultCredentials).value
            if(!([string]::IsNullOrEmpty($Artifacts.resource.properties.artifactlocation)) -and !([string]::IsNullOrEmpty($Artifacts.name)))
            {
                #Handle multiple artifacts per build
                foreach ($Artifact in $Artifacts)
                {
                    $BuildDrops = New-Object -TypeName psobject
                    Add-Member -InputObject $BuildDrops -MemberType NoteProperty -Name 'Definition' -Value $Builddefinition.name
                    Add-Member -InputObject $BuildDrops -MemberType NoteProperty -Name 'Location' -Value $Artifact.resource.properties.artifactlocation
                    Add-Member -InputObject $BuildDrops -MemberType NoteProperty -Name 'Name' -Value $Artifact.Name
                    $Builds += $BuildDrops 
                }
            }
        }
    }
    Return $Builds
}

Then we can go through all the drop folders and see if we can find subfolders (builds) not known to the server


Drops = Get-TFSBuildDrops -ServerURL $ServerURL -Project $Project

$ObsoletedBuilds =@()
foreach ($Drop in ($Drops | Sort-Object -Property Location -Unique))
{
    $KnownDrops = $Drops | Where-Object {$_.Location -eq $Drop.Location}
    if (Test-Path $Drop.Location)
    {
        $Childs = Get-ChildItem -Path $Drop.Location -Directory | Where-Object {$_.name -notin $KnownDrops.name} | Select-Object fullName
        foreach ($Child in $Childs)
        {
            $ObsoletedBuild = New-Object -TypeName psobject
            Add-Member -InputObject $ObsoletedBuild -MemberType NoteProperty -Name "BuildDefinition" -Value $Drop.Definition
            Add-Member -InputObject $ObsoletedBuild -MemberType NoteProperty -Name "Path" -Value $Child.FullName
            $ObsoletedBuilds += $ObsoletedBuild
        }
    }
}
$ObsoletedBuilds | export-csv C:\Temp\$($Project).csv -Force 

We now have a list of potential obsoleted drops. You need to verify the list!!!
If you have nested drop folders where builds is dropped inside other build drops it will be identified as a false positive. Also if you have builddrop paths containing the buildnumber, instead of in the artifact name it will not be identified, as they are all unique. Full script below
Enjoy

 
$ServerURL = "HTTP://[server]:8080/tfs/defaultcollection"
$Project = "[project]"

Function Get-TFSBuildDrops{
    param (
        [string]$ServerURL,
        [string]$Project
    )
    $URL = "$($ServerURL)/$($Project)"

    #Get all builddefinitions in Project
    $Builddefinitions = (Invoke-RestMethod -Uri ($URL + '/_apis/build/definitions?api-version=3.2') -Method GET -UseDefaultCredentials).value

    $Builds = @()
    foreach($Builddefinition in $Builddefinitions)
    {
        #Get the Build IDs
        $BuildIDs= (Invoke-RestMethod -Uri ($URL + '/_apis/build/builds?api-version=2.0&definitions=' + $Builddefinition.ID) -Method Get -UseDefaultCredentials).value.ID
        foreach ($Build in $BuildIDs)
        {
            #Get Artifacts
            $Artifacts = (Invoke-RestMethod -Uri ($URL + '/_apis/build/builds/' + $Build + '/artifacts') -Method Get -UseDefaultCredentials).value
            if(!([string]::IsNullOrEmpty($Artifacts.resource.properties.artifactlocation)) -and !([string]::IsNullOrEmpty($Artifacts.name)))
            {
                #Handle multiple artifacts per build
                foreach ($Artifact in $Artifacts)
                {
                    $BuildDrops = New-Object -TypeName psobject
                    Add-Member -InputObject $BuildDrops -MemberType NoteProperty -Name 'Definition' -Value $Builddefinition.name
                    Add-Member -InputObject $BuildDrops -MemberType NoteProperty -Name 'Location' -Value $Artifact.resource.properties.artifactlocation
                    Add-Member -InputObject $BuildDrops -MemberType NoteProperty -Name 'Name' -Value $Artifact.Name
                    $Builds += $BuildDrops 
                }
            }
        }
    }
    Return $Builds
}

$Drops = Get-TFSBuildDrops -ServerURL $ServerURL -Project $Project

$ObsoletedBuilds =@()
foreach ($Drop in ($Drops | Sort-Object -Property Location -Unique))
{
    $KnownDrops = $Drops | Where-Object {$_.Location -eq $Drop.Location}
    if (Test-Path $Drop.Location)
    {
        $Childs = Get-ChildItem -Path $Drop.Location -Directory | Where-Object {$_.name -notin $KnownDrops.name} | Select-Object fullName
        foreach ($Child in $Childs)
        {
            $ObsoletedBuild = New-Object -TypeName psobject
            Add-Member -InputObject $ObsoletedBuild -MemberType NoteProperty -Name "BuildDefinition" -Value $Drop.Definition
            Add-Member -InputObject $ObsoletedBuild -MemberType NoteProperty -Name "Path" -Value $Child.FullName
            $ObsoletedBuilds += $ObsoletedBuild
        }
    }
}
$ObsoletedBuilds | export-csv C:\Temp\$($Project).csv -Force 

Author: KimC

TFS admin and deployment fellow

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s