<p>So I wrote this powershell script to be ran by the deployment group agents who have access to the server locally.</p>

<pre><code>Param(
[string]$JenkinsPatArg
)
$isRunningLocal = $false
$jenkinsUrlBase = "[public ipaddress]"

if(Test-Connection -ComputerName "[private ipaddress/DNS name]" -Quiet) 
{
    $isRunningLocal = $true
    $jenkinsUrlBase = "[private ipaddress/dns name]"
}

$buildDefinitionName = $env:RELEASE_ARTIFACTS_GAMEZIP_DEFINITIONNAME
$buildTeamProject = [Uri]::EscapeDataString($env:SYSTEM_TEAMPROJECT)
$targetBuildNumber = $env:RELEASE_ARTIFACTS_[Artifact Alias]_BUILDNUMBER

$Auth = "$($env:username):$($JenkinsPatArg)"

$Bytes = [System.Text.Encoding]::UTF8.GetBytes($Auth)
$Base64bytes = [System.Convert]::ToBase64String($Bytes)
$Headers = @{ "Authorization" = "Basic $Base64bytes"}

Write-Host "Searching for $($targetBuildNumber) in project $($buildTeamProject)-&gt;$($buildDefinitionName)"

$correctBuild

Write-Host "Querying: http://$($jenkinsUrlBase):[port number]/job/$($buildTeamProject)/job/$($buildDefinitionName)/api/json?tree=builds[id,url,actions[buildNumber]]"

ForEach($build in (Invoke-RestMethod -Uri "http://$($jenkinsUrlBase):[port number]/job/$($buildTeamProject)/job/$($buildDefinitionName)/api/json?tree=builds[id,url,actions[buildNumber]]" -ContentType json -Headers $Headers).builds)
{

    if($build.actions.buildNumber -eq $targetBuildNumber)
    {
        $correctBuild = $build
        Write-Host $build.id
        Write-Host $build.url
        Write-Host $build.actions.buildNumber
        break

    }
}

if($correctBuild -eq $null)
{
    Write-Error "Build not found.  Must rebuild source to obtain artifact"
    return;
}

if($isRunningLocal){$correctBuild.url = $correctBuild.url.Replace("[public ip address]", "[local ipaddress/DNS name]")}

$targetZip = "$Env:temp/$($buildDefinitionName)_$($correctBuild.id).zip"

(Invoke-RestMethod -Uri "$($correctBuild.url)/artifact/$($buildDefinitionName)/*zip*/$($buildDefinitionName)_$($correctBuild.id).zip" -ContentType Application/zip -Headers $Headers -OutFile $targetZip)

Add-Type -AssemblyName System.IO.Compression.FileSystem;[System.IO.Compression.ZipFile]::ExtractToDirectory($targetZip, $env:targetDirectory);
#Expand-Archive -Path $targetZip -DestinationPath $env:targetDirectory -Force
Remove-Item $targetZip;
</code></pre>

<p>passing in the a personal API key (created by Jenkins user manager to use by visual studio team services), this script first checks if it is can access the server using local connections, if so set the variables appropriately.</p>

<p>From there we create local handlers for environmental variables.  Our release artifact name defined in VSTS is the same as that in our jenkins server.  The build number used is created by VSTS and passed to jenkins and stored by the VSTS plugin(<a href="https://wiki.jenkins-ci.org/display/JENKINS/Team+Foundation+Server+Plugin" rel="nofollow noreferrer">Jenkins TFS Plugin download</a>).  We get this version number from the environment as the artifact build number.  We also ensure that our team project path url is windows URL encoded <a href="https://stackoverflow.com/questions/23548386/how-do-i-replace-spaces-with-20-in-powershell?utm_medium=organic&amp;utm_source=google_rich_qa&amp;utm_campaign=google_rich_qa">brief explanation</a>.  Next we hold our authentication which is stored as a variable in our VSTS variables.  The <code>$JenkinsPatArg</code> is marked as secret. As such, we must pass it in as it isn't visible as an environment variable.  We then encode the authentication and add it to our request headers.</p>

<p>We then add a debug line to print what target we are looking for and what project we are looking in, just to make sure we are looking for the right thing.  We create a variable to hold the build information that we find and do one more debug write stating what are query url is so that we can manually run it if we run into any issues.</p>

<p>Next we then run this url query against the build server and iterate over all the builds returned.  The query filters out all the JSON to just the build ID, it's absolute url, and the VSTS build number.  We check to see if the build we are examining has the same build number as the artifact we are deploying, if so store it in the <code>$correctBuild</code> variable, print the was found for debugging, and exit the loop.</p>

<p>Coming out of the loop we check to see if we found a result.  If we did not find a build (due to it being cleaned up most likely) we inform the user that the artifact needs to be rebuild from source.  Otherwise we download the artifact zip to a temporary location from the jenkins server and extract it to the target directory, defined as a variable.  Lastly we delete the downloaded zip.</p>

<p>This is ran as a powershell script task and due to it's length, can not be an inline script.  So to deploy this, i have a devops git repo for my scripts and I just include this as an artifact that is downloaded and ran.  This task is preceded by other tasks which stop the current process from running, and delete the current files in place before running this task.  After this task is ran, more scripts are ran to run the downloaded artifacts.</p>

<p>I hope this helps someone else trying to coordinate and link VSTS builds with Jenkins artifacts.</p>

CC BY-SA 4.0
2018-06-27T21:42:28.460Z
I cannot deny that this works, but it does not seem very secure. It so not the simple fact that you are happily sending credential in clear text; it is the unclear design. Which component is responsible for deployment? From what you describe, neither VSTS nor Jenkins. VSTS is deploying binaries it has no knowledge of, Jenkins has no word on what is being deployed.
If you send the artifacts in VSTS, or in some artifact store, you get a picture of what is moving.
Giulio Vian
CC BY-SA 3.0
2018-04-23T22:09:46.643Z
2018-04-23T22:09:46.643Z
xtreampb
<p>So I have a local jenkins server building artifacts.  I trigger this build through VSTS.  When browsing through the logs when uploading the artifacts to VSTS from the local jenkins server I found this: <code>Downloading content from Jenkins server:http://[ip]:[port]//job/[Folder Name]/job/[Build Definition Name]//api/json?tree=lastSuccessfulBuild[id,displayname] with strict SSL:true</code></p>

<p>This could pose an issue where the triggered build may get beat out by another build (theoretically).  While this is not a high likelihood, I would like to mitigate all possible artifact confusions as that is one of the main points my company is moving to build automation and continuous integration.  I did see something related to the VSTS Task in issue <a href="https://github.com/Microsoft/vsts-tasks/issues/4110" rel="nofollow noreferrer" title="4110">4110</a> but not sure what is coming of this.</p>

<p>What I'm trying to accomplish is that when Jenkins finishes the build it uploads it to VSTS.  Because of the size of the build, when we go to releases, I would like to not download the artifacts back to our local premise, (using deployment groups) but instead copy it internally directly from the Jenkins build server.  Does anyone have any suggestions?</p>

<jenkins><azure-devops>
Linking VSTS build with jenkins artifacts
xtreampb
# Linking VSTS build with jenkins artifacts

(Asked by: username_0 on 2018-04-23)

So I have a local jenkins server building artifacts. I trigger this build through VSTS. When browsing through the logs when uploading the artifacts to VSTS from the local jenkins server I found this: `Downloading content from Jenkins server:http://[ip]:[port]//job/[Folder Name]/job/[Build Definition Name]//api/json?tree=lastSuccessfulBuild[id,displayname] with strict SSL:true`

This could pose an issue where the triggered build may get beat out by another build (theoretically). While this is not a high likelihood, I would like to mitigate all possible artifact confusions as that is one of the main points my company is moving to build automation and continuous integration. I did see something related to the VSTS Task in issue [4110](https://github.com/Microsoft/vsts-tasks/issues/4110 "4110") but not sure what is coming of this.

What I'm trying to accomplish is that when Jenkins finishes the build it uploads it to VSTS. Because of the size of the build, when we go to releases, I would like to not download the artifacts back to our local premise, (using deployment groups) but instead copy it internally directly from the Jenkins build server. Does anyone have any suggestions?

**Tags:** jenkins, azure-devops


---

## 1 answers

### Answer by username_0 on 2018-04-23. Score: 0 | Accepted answer

So I wrote this powershell script to be ran by the deployment group agents who have access to the server locally.
Param(
[string]$JenkinsPatArg
)
$isRunningLocal = $false
$jenkinsUrlBase = "[public ipaddress]"

if(Test-Connection -ComputerName "[private ipaddress/DNS name]" -Quiet) 
{
    $isRunningLocal = $true
    $jenkinsUrlBase = "[private ipaddress/dns name]"
}

$buildDefinitionName = $env:RELEASE_ARTIFACTS_GAMEZIP_DEFINITIONNAME
$buildTeamProject = [Uri]::EscapeDataString($env:SYSTEM_TEAMPROJECT)
$targetBuildNumber = $env:RELEASE_ARTIFACTS_[Artifact Alias]_BUILDNUMBER

$Auth = "$($env:username):$($JenkinsPatArg)"

$Bytes = [System.Text.Encoding]::UTF8.GetBytes($Auth)
$Base64bytes = [System.Convert]::ToBase64String($Bytes)
$Headers = @{ "Authorization" = "Basic $Base64bytes"}

Write-Host "Searching for $($targetBuildNumber) in project $($buildTeamProject)->$($buildDefinitionName)"

$correctBuild

Write-Host "Querying: http://$($jenkinsUrlBase):[port number]/job/$($buildTeamProject)/job/$($buildDefinitionName)/api/json?tree=builds[id,url,actions[buildNumber]]"

ForEach($build in (Invoke-RestMethod -Uri "http://$($jenkinsUrlBase):[port number]/job/$($buildTeamProject)/job/$($buildDefinitionName)/api/json?tree=builds[id,url,actions[buildNumber]]" -ContentType json -Headers $Headers).builds)
{

    if($build.actions.buildNumber -eq $targetBuildNumber)
    {
        $correctBuild = $build
        Write-Host $build.id
        Write-Host $build.url
        Write-Host $build.actions.buildNumber
        break

    }
}

if($correctBuild -eq $null)
{
    Write-Error "Build not found.  Must rebuild source to obtain artifact"
    return;
}

if($isRunningLocal){$correctBuild.url = $correctBuild.url.Replace("[public ip address]", "[local ipaddress/DNS name]")}

$targetZip = "$Env:temp/$($buildDefinitionName)_$($correctBuild.id).zip"

(Invoke-RestMethod -Uri "$($correctBuild.url)/artifact/$($buildDefinitionName)/*zip*/$($buildDefinitionName)_$($correctBuild.id).zip" -ContentType Application/zip -Headers $Headers -OutFile $targetZip)

Add-Type -AssemblyName System.IO.Compression.FileSystem;[System.IO.Compression.ZipFile]::ExtractToDirectory($targetZip, $env:targetDirectory);
#Expand-Archive -Path $targetZip -DestinationPath $env:targetDirectory -Force
Remove-Item $targetZip;

passing in the a personal API key (created by Jenkins user manager to use by visual studio team services), this script first checks if it is can access the server using local connections, if so set the variables appropriately.
From there we create local handlers for environmental variables.  Our release artifact name defined in VSTS is the same as that in our jenkins server.  The build number used is created by VSTS and passed to jenkins and stored by the VSTS plugin(Jenkins TFS Plugin download).  We get this version number from the environment as the artifact build number.  We also ensure that our team project path url is windows URL encoded brief explanation.  Next we hold our authentication which is stored as a variable in our VSTS variables.  The $JenkinsPatArg is marked as secret. As such, we must pass it in as it isn't visible as an environment variable.  We then encode the authentication and add it to our request headers.
We then add a debug line to print what target we are looking for and what project we are looking in, just to make sure we are looking for the right thing.  We create a variable to hold the build information that we find and do one more debug write stating what are query url is so that we can manually run it if we run into any issues.
Next we then run this url query against the build server and iterate over all the builds returned.  The query filters out all the JSON to just the build ID, it's absolute url, and the VSTS build number.  We check to see if the build we are examining has the same build number as the artifact we are deploying, if so store it in the $correctBuild variable, print the was found for debugging, and exit the loop.
Coming out of the loop we check to see if we found a result.  If we did not find a build (due to it being cleaned up most likely) we inform the user that the artifact needs to be rebuild from source.  Otherwise we download the artifact zip to a temporary location from the jenkins server and extract it to the target directory, defined as a variable.  Lastly we delete the downloaded zip.
This is ran as a powershell script task and due to it's length, can not be an inline script.  So to deploy this, i have a devops git repo for my scripts and I just include this as an artifact that is downloaded and ran.  This task is preceded by other tasks which stop the current process from running, and delete the current files in place before running this task.  After this task is ran, more scripts are ran to run the downloaded artifacts.
I hope this helps someone else trying to coordinate and link VSTS builds with Jenkins artifacts.

> **Comments:**
> **username_1**: I cannot deny that this works, but it does not seem very secure. It so not the simple fact that you are happily sending credential in clear text; it is the unclear design. Which component is responsible for deployment? From what you describe, neither VSTS nor Jenkins. VSTS is deploying binaries it has no knowledge of, Jenkins has no word on what is being deployed. If you send the artifacts in VSTS, or in some artifact store, you get a picture of what is moving.  

---

