API for automating Azure DevOps Pipelines? - azure-devops

I want to automate the queue-ing of Azure Pipelines with an API call, get information on the pipeline/build/job status,
Azure Pipelines docs only mention "API" for the "Invoke HTTP Rest API" task: https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/http-rest-api?view=vsts That might come in handy, but is not what I am looking for.
There is a "Azure DevOps Services REST API":
https://learn.microsoft.com/en-us/rest/api/azure/devops/?view=azure-devops-rest-5.1
But I couldn't find any mention of "Pipeline" there, so this doesn't seem to be the right thing as well.
The StackOverflow tag azure-devops-rest-api also only mentions VSTS and TFS:
Visual Studio Team Services REST APIs is a set of APIs allowing management of a Visual Studio Team Services accounts as well as TFS 2015 and 2017 servers.
Besides these two results, I only find other versions or translations of various copies of these - and a lot of unrelated documents that are about Azure in general.
Am I just using the wrong words to search?
Is there an actual API for Azure DevOps Pipelines?
Does it have a usable API Explorer?
Does it have proper clients for languages like JavaScript, Ruby or PHP?

Seems I was bad at googling:
Trigger Azure Pipelines build via API and Start a build and passing variables through VSTS Rest API (found via the searching for [azure-pipelines] apihere on StackOverflow) point me to the Azure DevOps Services REST API that I had mentioned above.

I too have been working on automating DevOps pipelines and keep winding up back here. Some of this information appears to be outdated. As of the time of my writing this, I believe this article in the Microsoft Docs is the most recent. I did have to scratch my head a bit to make it work, but wound up with this code
public static async Task InitiatePipeline(CancellationToken cancellationToken = default)
{
using(HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var token = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", "", AppSettings.DevOpsPAT)));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", token);
var repoGuid = "Put GUID Here"; // You can get GUID for repo from the URL when you select the rpo of interest under Repos is Project Settings
var bodyJson = #"{
""parameters"": {
""parameterName"": ""parameterValue""
},
""variables"": {},
""resources"": {
""repositories"": {
""self"": {
""repository"": {
""id"": """ + repoGuid + #""",
""type"": ""azureReposGit""
},
""refName"": ""refs/heads/master""
}
}
}
}";
var bodyContent = new StringContent(bodyJson, Encoding.UTF8, "application/json");
var pipeLineId = 61; // Can get this from URL when you open the pipeline of interest in Azure DevOps
var response = await client.PostAsync($"https://dev.azure.com/ORG_NAME/PROJECT_NAME/_apis/pipelines/{pipeLineId}/runs?api-version=6.0-preview.1", bodyContent, cancellationToken);
response.EnsureSuccessStatusCode();
}
}

I ran into these problems as well and wound up making a powershell wrapper of the API and then wrapping that into an Azure DevOps Pipeline Template. I've just published it for anyone to use. I hope that anyone who finds this thread can find this template useful.

With AzFunc4DevOps this can be done in an event-driven way. And in C#.
E.g. here is how to trigger a build when another build succeeds:
[FunctionName(nameof(TriggerBuildWhenAnotherBuildSucceeds))]
public static async Task Run(
[BuildStatusChangedTrigger
(
Project = "%TEAM_PROJECT_NAME%",
BuildDefinitionIds = "%BUILD_DEFINITION_ID%",
ToValue = "Completed"
)]
BuildProxy build,
[BuildClient]
BuildHttpClient buildClient,
[BuildDefinition(Project = "%TEAM_PROJECT_NAME%", Id = "%NEXT_BUILD_DEFINITION_ID%")]
BuildDefinitionProxy nextbuildDefinition
)
{
await buildClient.QueueBuildAsync(new Build
{
Definition = nextbuildDefinition,
Project = nextbuildDefinition.Project
});
}
Here are some more examples.

Related

How to Get Azure Access Token using DefaultAzureCredential without storing secrets

I am trying to setup my environment to be able to to access Azure resources from outside Azure.
While looking at different options I cam across mainly below options of many others
Option 1:
Creating a Service Principal with the Azure CLI and use client secrets for Token retrieval and accessing Resources
Get Client secrets Run Time
Option 2:
Using DefaultAzureCredential (Azure.Identity) for Token retrieval and accessing Resources
DefaultAzureCredential
I am currently trying out DefaultAzureCredential option to be able to access Azure resources such as ADF, Blob storage etc.
I am able to do this using the Visual Studio credentials (VS 2019). However challenge remains to perform same action via a Pipeline running outside Azure. I do not want to save any secrets in the code. Does this means that I cannot use environment variables for the Purpose?
If indeed this is still possible then need help with the code.
Environment:
. Net Framework 4.8/Core 3.1
Desired Flow:
Use Visual Studio Credentials for local Development and Test.
Use Environment Variables OR other tasks supported by DefaultAzureCredential via DevOps Pipeline task.
Code:
var tokenCredential = new DefaultAzureCredential();
var accessToken = await tokenCredential.GetTokenAsync(
new TokenRequestContext(scopes: new string[] { ResourceId + "/.default" }) { }
);
I was able to solve this using DefaultAzureCredential. We followed the below approach to solve this
Added code to read the secrets from appsetting.json
Add secrets to environment variables
Use DefaultAzureCredential* to reach to correct override.
Add replace token task in Build/Release pipelines to replace client secret variables with secrets from pipeline parameters.
Code when executed from Visual studio does not find actual value to secret variables from appsetting.json and then uses VisualStudio Credentials.
Read values
string AZURE_CLIENT_SECRET = ConfigurationHelper.GetByName("AZURE_CLIENT_SECRET");
string AZURE_CLIENT_ID = ConfigurationHelper.GetByName("AZURE_CLIENT_ID");
string AZURE_TENANT_ID = ConfigurationHelper.GetByName("AZURE_TENANT_ID");
// Check whether the environment variable exists.
if (AZURE_CLIENT_SECRET != "{{AZURE_CLIENT_SECRET}}"
&& AZURE_CLIENT_ID != "{{AZURE_CLIENT_ID}}" &&
AZURE_TENANT_ID != "{{AZURE_TENANT_ID}}")
{
Environment.SetEnvironmentVariable("AZURE_CLIENT_SECRET", AZURE_CLIENT_SECRET);
Environment.SetEnvironmentVariable("AZURE_CLIENT_ID", AZURE_CLIENT_ID);
Environment.SetEnvironmentVariable("AZURE_TENANT_ID", AZURE_TENANT_ID);
Console.WriteLine("Setting Environment Variables");
}
Call DefaultAzureCredential
var objDefaultAzureCredentialOptions = new DefaultAzureCredentialOptions
{
ExcludeEnvironmentCredential = false,
ExcludeManagedIdentityCredential = true,
ExcludeSharedTokenCacheCredential = true,
ExcludeVisualStudioCredential = false,
ExcludeVisualStudioCodeCredential = false,
ExcludeAzureCliCredential = true,
ExcludeInteractiveBrowserCredential = true
};
var tokenCredential = new DefaultAzureCredential(objDefaultAzureCredentialOptions);
ValueTask<AccessToken> accessToken = tokenCredential.GetTokenAsync(
new TokenRequestContext(scopes: new[] { "https://management.azure.com/.default" }));
If environment variables are present in the active session then the code uses environment variables

Azure DevOps Server 2020.1.1 API - How to get a build including the agent it ran on

I am using an instance of BuildHttpClient of the nuget package Microsoft.TeamFoundationServer.Client to get my builds. Unfortunately the fetched objects of type Build do not include the name or id of the agent used to ran the build.
The property Queue just contains informations about the used queue, but not about the specific agent. There's another property AgentSpecification, but this seems to be always empty.
I could probably parse the attached logs of a build, but that would be pretty cumbersome.
Yes, that's an issue. However, as a workaround, you can try to extract that information from the logs. Small example:
var logs = BuildClient.GetBuildLogsAsync(TeamProjectName, buildId).Result;
foreach(var log in logs)
{
var lines = BuildClient.GetBuildLogLinesAsync(TeamProjectName, buildId, log.Id).Result;
foreach (var line in lines)
if (line.Contains("Agent")) Console.WriteLine(line);
}
Result:

How to create a custom azure devops task from a .net core console app

I would like to create an Azure Devops task based on a console app I have created.
The task should run on a Linux build agent, but I have no idea about how to get started.
Has anyone else done some things similar already?
azure devops task is based on Node.js
from the following articles
Add a build or release task
Reference for integrating custom build tasks into extensions
How To Build An Azure Custom Build/Release Task
azure-pipelines-tasks samples
you can know much about create a task.
The SDK of azure devops task is Node.
Maybe,You can combo the DotNetCoreInstaller and Github and other tasks.
But, the enviroment is Node.js
The easyest way is use Node.js.
There is no direct task handler for .NET core. In order to make a cross platform task, you'll therefore need to create a small typescript project that uses the azure-pipelines-task-lib to call your executable.
I've got a very simple extension which packages an executable here, which can serve as an example of the extension structure:
https://github.com/jessehouwing/azure-pipelines-agent-screenshot
You'll need to change the following things:
vss-extension.json
publisher
extension id
contribution id
task.json
task id (give it a new guid)
task name
remove the current executionhandler and replace it with the Node one.
A very simple task that runs ping.exe based on Typescript can be found here:
https://github.com/jessehouwing/azure-pipelines-demo-ping-task/blob/master/PingTask/ping.ts
The simplest form would look like this:
import tl = require('azure-pipelines-task-lib/task');
import trm = require('azure-pipelines-task-lib/toolrunner');
import { chmodSync } from "fs";
async function run() {
try {
let echoPath = tl.which('ping');
if (!isWindows) {
chmodSync(echoPath, "777");
}
let ping = tl.tool(echoPath);
let result: number = await ping.exec();
}
catch (err) {
tl.setResult(tl.TaskResult.Failed, err.message);
}
}
void run();
All the basic steps to get your task bootstrapped are here:
https://github.com/microsoft/azure-pipelines-task-lib/blob/master/node/README.md
Publishing a task to the marketplace is explained here:
How to test new Azure DevOps extension version before publishing it for everyone

Is there a way to script repetitive tasks in Azure DevOps?

We have a number of tasks that we carry out every time we create a new GIT repository in our project, and I would like to know if there's a way to script (PowerShell or any other method) these out. for e.g. every these are the steps we follow everytime we create a new repo
Create a new GIT repo
Create a Build pipeline for Build validations during
pull request
Add branch policies to Master including a step to validate build using the above build
Create a Build pipeline for releases
Create a Release pipeline
Is there a way to script repetitive tasks in Azure DevOps?
Of course yes! As Daniel said in comment, just use REST API can achieve these all. But since the steps you want to achieve are little much, the script might be little complex.
Create a new GIT repo
If you also want to use API to finish this step, it needs 3 steps to finish that( Since this does not be documented in doc, I will described it very detailed ):
Step1: Create the validation of importing repository
POST https://dev.azure.com/{org name}/{project name}/_apis/git/import/ImportRepositoryValidations?api-version=5.2-preview.1
Request body:
{
"gitSource":
{
"url":"${ReposURL}",
"overwrite":false
},
"tfvcSource":null,
"username":"$(username}"/null,
"password":"${pw}"/"${PAT}"/null
}
Step2: Create the new repos name
POST https://dev.azure.com/{org name}/{project name}/_apis/git/Repositories?api-version=5.2-preview.1
Request body:
{
"name":"${ReposName}",
"project":
{
"name":"{project name}",
"id":"{this project id}"
}
}
Step3: Import repos
POST https://dev.azure.com/{org name}/{project name}/_apis/git/repositories/{the new repos name you create just now}/importRequests?api-version=5.2-preview.1
Request body:
{
"parameters":
{
"deleteServiceEndpointAfterImportIsDone":true,
"gitSource":
{
"url":"${ReposURL}",
"overwrite":false
},
"tfvcSource":null,
"serviceEndpointId":null
}
}
In these script, you can set variables in Variable tab, then use ${} to get them in the script.
Create a Build pipeline for Build validations during pull request
This step you'd better finish manually, because you can configure more about tasks and trigger with UI. If still want use API, refer to this doc: create build definition. There has detailed sample you can try with.
Add branch policies to Master including a step to validate build using the above build
This API still be documented in doc: create build policy. Just refer to that, and ensure use the correct policy type and the corresponding buildDefinitionId.
Create a Build pipeline for releases
This still recommend you finish manually, same with the step3 you mentioned.
Create a Release pipeline
See this doc: create release.
Note: For some parameter which will be used many times, you can set it as variable. For the parameter which need get from previous API response, you can define a variable to get its value then pass this variable into the next API to use.For e.g. :
$resultT= $result.Headers.ETag
Write-Host "##vso[task.setvariable variable=etag;]$resultT"
Now, you can directly use the $(etag) in the next API.

Getting release and build numbers from Azure DevOps Release Pipeline to display in website

I would like to be able to display the release number (*1 in pic) and the build number (*2 in pic) in Asp.Net MVC Core website - FOR that specific website, which was built and released with Azure DevOps.
So if I check code in for an MVC website, the build and release process kicks in and deploys the website - and now I want the same website to display "Release xxxxx" in the footer.
It could be in a JSON file, an environment variable or something - as long as I can access it from Asp.Net Core, I am happy.
How can that be achieved?
If you want in the current app that you built and deployed to display the data you can get the values of the variables like each environment variable:
var releaseName = Environment.GetEnvironmentVariable("Release_ReleaseName", EnvironmentVariableTarget.Process);
var buildNumber= Environment.GetEnvironmentVariable("Build_BuildNumber", EnvironmentVariableTarget.Process);
If you want to display the data in an external app:
You can get all the release data with Azure DevOps Rest API. the API for release is Releases - Get Release:
GET https://vsrm.dev.azure.com/{organization}/{project}/_apis/release/releases/{releaseId}?api-version=5.1
You got a big JSON with the release info, including the release name:
{
"id": 205,
"name": "Release-93",
And in the artifact array, you can get the build id (under definitionReference.version.name):
"version": {
"id": "1439",
"name": "TestVars_20190829.1439..11"
So in your Asp.Net MVC Core app just make a GET rest API call and you have all the data.
In addition, you can use the .NET client libraries for Azure DevOps Services and get the data with GetRelease() method:
string projectName = "TestProject";
VssConnection connection = Context.Connection;
ReleaseHttpClient releaseClient = connection.GetClient<ReleaseHttpClient>();
List<WebApiRelease> releases = releaseClient.GetReleasesAsync(project: projectName).Result;
int releaseId = releases.FirstOrDefault().Id;
// Show a release
WebApiRelease release = releaseClient.GetReleaseAsync(project: projectName, releaseId: releaseId).Result;
// Now get the release name and build nubmer
The solution seems to be to use a plugin for Azure Devops during build Replace Tokens, which replaces strings in a .config file during build/release - and then access those values from the code at runtime.
It is described here in more detail: .NET Core App - How to get build number at runtime