Tag Archives: Programmatically create task

Call the Azure DevOps REST API

In this post, I introduced the DevOps CLI. Here, I’m going to expand on that by interrogating the DevOps API, and generating a new work item in the board. A few years ago I did the same thing in TFS.

Configuration

The first step here is to generate a personal access token. You can do this from the CLI, see here for details on how to do that.

From the UI, generating a personal access token is trivial; from your project, select Personal Access Tokens from the drop down menu:

In real life, the next screen is quite important, as you’ll want to scope down the access to the bare minimum. However, we’re just playing around, so for test purposes, we can grant full access:

You’ll then be given the token – take a copy of this:

The Code – Getting a list of projects in the organisation

The following code (heavily based on this link) should get a list of team projects within the organisation that you provide:

using System.Net.Http.Headers;

string personalaccesstoken = "abcdef";
string organisation = "devopsplayground1";

using HttpClient client = new HttpClient();

client.DefaultRequestHeaders.Accept.Add(
	new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
	Convert.ToBase64String(
		System.Text.ASCIIEncoding.ASCII.GetBytes(
			string.Format("{0}:{1}", "", personalaccesstoken))));

using HttpResponseMessage response = await client.GetAsync(
			$"https://dev.azure.com/{organisation}/_apis/projects");

response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);

personalaccesstoken is taken from the access token that you generated earlier, and the organisation is the name of your DevOps organisation; you can find it here if you’re unsure:

The Code – Creating a DevOps Work Item

Now that we can get a list of projects, we can pretty much do anything via the API; for example, if you wanted a list of work item types, you might use this:

    HttpResponseMessage responseWIT = await client.GetAsync(
                $"https://dev.azure.com/{organisation}/{project}/_apis/wit/workitemtypes?api-version=6.1-preview.2");

    responseWIT.EnsureSuccessStatusCode();
    string responseBodyWIT = await responseWIT.Content.ReadAsStringAsync();
    Console.WriteLine(responseBodyWIT);    

Updating or creating is a little different; let’s take creating a new work item. If I’m honest, the interface here doesn’t feel particularly RESTful, but nevertheless:

    var obj = new[] { new { from = (string?)null, op = "add", path = "/fields/System.Title", value = "pcmtest" } };
    string serialisedObj = System.Text.Json.JsonSerializer.Serialize(obj);
    var content = new StringContent(
        serialisedObj,
        Encoding.UTF8, 
        "application/json-patch+json");

    string type = "bug";
    HttpResponseMessage response = await client.PatchAsync(
                $"https://dev.azure.com/{organisation}/{project}/_apis/wit/workitems/${type}?api-version=6.1-preview.3",
                content);

    //response.EnsureSuccessStatusCode();
    string responseBody = await response.Content.ReadAsStringAsync();
    Console.WriteLine(responseBody);

See here for the docs. There’s a few things to note here:

1. The type = “bug” comes from the types that we got above.
2. Note that the content that’s passed in to the Api call is an array – if you miss that and simply pass in a single object, you’ll get the error:

You must pass a valid patch document in the body of the request.

3. You will not see the above error unless, like me, you remove the EnsureSuccessStatusCode() call!
4. The API version matters, but it will give you a sensible error if you get it wrong.
5. Yes, this is a Patch call, even though you’re creating a new resource!

Here’s the new work item:

References

https://docs.microsoft.com/en-us/rest/api/azure/devops/?view=azure-devops-rest-6.1&WT.mc_id=DT-MVP-5004601