Getting started with Azure DevOps API with PowerShell
Tags: powershell, azure, devops
Published: 2020-01-26 18:07:58

I use Azure DevOps every day for different kinds of clients, teams, and projects. I need to set up access, whenever I need Boards, Test Plans or other Azure DevOps services. I also need to decide how to configure the repository or the board. Sometimes I may have to import work items or initialize the wiki. 
For some organization or some project, I also need to verify user configuration for compliance, security and license management. 
These tasks are manual, time-consuming and I always forget to do one thing or another.  
But there is a way to automate Azure DevOps Services set up, the Azure DevOps Rest API. This API lets you perform actions I mentioned and more. This short blog post will explain how.

Note, I will use PowerShell to operate, but you can choose the language of your choice. I use API version 5.1. The documentation can be found [here](https://docs.microsoft.com/en-us/rest/api/azure/devops/?view=azure-devops-rest-5.1)

First, we need a way to authenticate to an Azure DevOps organization. There is two way to authenticate to Azure DevOps, using Azure Active Directory or using a Personal Access Token. It depends on the situation and on what you will need to build. For Azure Active Directory access you will need a client library (for .NET and PowerShell) or you can use Personal Access Token (PAT). 

First, we need a way to authenticate to an Azure DevOps organization. There is two way to authenticate to Azure DevOps, using Azure Active Directory or using a Personal Access Token. It depends on the situation and on what you will need to build. For Azure Active Directory access you will need a client library (for .NET and PowerShell) or you can use Personal Access Token (PAT). 


To create a Personal Access Token, login to Azure DevOps in this organization. On the right top corner click on the user icon. 

![PAT Icon](https://thepracticaldev.s3.amazonaws.com/i/h49lwhvg2qi2yd2uvzbv.jpg)

Select "Personal access tokens"


Then Click on "New Token". You will be asked to provide a name for the token, the expiration date, Organization Access, and the scope you want to apply, either all scopes or specify access for Work items, code (git repository), Build, Release, test and packaging.  

![token](https://thepracticaldev.s3.amazonaws.com/i/vwwiv5nu1g8kgeeooq5n.jpg)

Defining scope is important for your application; it defines how the application associated with the token will interact with Azure DevOps Services. Unless you are testing the API, never choose full access, review your needs and select the appropriate scopes.


After pushing the "Create" button, the token is displayed. Make sure to save the token securely, there is no way to retrieve it later!


To access Azure DevOps Service Rest API, we need to send a basic authentication header with every http request to the service. The basic authentication HTTP header look like 

Authorization: basic <credentials>

The credential needs to be Base64 encoded. In PowerShell you can do it like this.

```powershell
$AzureDevOpsAuthenicationHeader = @{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($AzureDevOpsPAT)")) }
```

Do not forget the extra white space between Basic and the “:”

Now that we know how to authenticate to Azure DevOps API, let’s see what we can do with the API. 

We need first to build our URI. Using the API you will soon notice the different URI like https://dev.azure.com or https://vssps.dev.azure.com and many more. Most of the time, to be valid the URI needs to include, at least the organization name.  

https://dev.azure.com/<MyOrganization> or https://vssps.dev.azure.com/<MyOranization>


Now, we can start to dig into the API. First, let's try to get a list of all projects within the organization.

Finally, we need to add "_apis"

https://dev.azure.com/<MyOrganization>/_apis or https://vssps.dev.azure.com/<MyOranization>/_apis

This will be our base URI for most operations.

Let’s start by getting the list of projects inside an organization.  

```powershell
$AzureDevOpsPAT = "ocd2rrtds7bj6mff6jcxjllmaaXXXXXXXXXXXXXXXXXXXXXXXX"
$OrganizationName = "DEMOXXXXXXXXXXXXXXX"

$AzureDevOpsAuthenicationHeader = @{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($AzureDevOpsPAT)")) }

$UriOrga = "https://dev.azure.com/$($OrganizationName)/" 
$uriAccount = $UriOrga + "_apis/projects?api-version=5.1"

Invoke-RestMethod -Uri $uriAccount -Method get -Headers $AzureDevOpsAuthenicationHeader 
```

The API will return two elements. Count, the number of projects in the current organization and value, an array with the name, ID, visibility, revision, URI and last update time for each project.

Now how can we add a new project by using the rest API? 

To create a project we need to provide a name, an optional description, visibility (private or public), a source control (Git or TFS) and the process model. 
We need the process model ID and not only the name. This is because you can create your process model.

To get the process module ID, we must use another request to the API to get these ID. 


```powershell
$uriProcess = $UriOrga + "_apis/process/processes?api-version=5.1"
Invoke-RestMethod -Uri $uriProcess -Method get -Headers $AzureDevOpsAuthenicationHeader
```
Now we can start to build the request body to add a project. For the process template I choose the Basic Process, b8a3a935-7e91-48b8-a94c-606d37c3e9f2. 

```powershell
$projectConfiguration = @{
        "name" = "TestProject"
        "description" = "Test Project"
        "ProjectVisibility" = "private"
        "capabilities" = @{
            "versioncontrol" = @{
                "sourceControlType" = "Git"
            }
            "processTemplate" = @{
                "templateTypeId" = "b8a3a935-7e91-48b8-a94c-606d37c3e9f2"
            }
        }
}  | ConvertTo-Json -Depth 5


Invoke-RestMethod -Uri $uriProject -Method Post -Headers $AzureDevOpsAuthenicationHeader -Body $projectConfiguration -ContentType "application/json"
```

The API does not create the project right away. Instead, it queues de request and response with a 202 Accepted HTTP code and 3 values, an ID on the request, a status (not set or queue most of the time) and a URI.  

The last URI can be used to monitor the project creation.

We can now add users to this project. But we need first to list users currently in the organization. There are 3 kinds of users in an Azure DevOps organization, Azure Active Directory user, Microsoft Account user and build user (services). This is what you see in the organization settings. 

```powershell
$uriOrgaUsers = "https://vsaex.dev.azure.com/$($OrganizationName)/_apis/userentitlements?api-version=5.1-preview.2"

(Invoke-RestMethod -Uri $uriOrgaUsers  -Method get -Headers $AzureDevOpsAuthenicationHeader).members
```

With our user list, we can add them to the project we created in the last steps. We can not add members directly to the project. By default, when we created the project the Azure DevOps service create a default team, named after project name. 

We can get the default Team ID by query the Project properties.

```powershell
$UriOrga = "https://dev.azure.com/$($OrganizationName)/" 
$ProjectID = "576e2e9d-c7ee-4fd5-XXXXXXXXXX"

$uriProject = $UriOrga + "_apis/projects/$($ProjectID)/properties?api-version=5.1-preview.1"

(Invoke-RestMethod -Uri $uriProject -Method get -Headers $AzureDevOpsAuthenicationHeader).value
```

We can add the user to this team by using the Team ID and one of the user IDs we collected.

```powershell
$TeamID = "5a18ffd6-1f25-44b0XXXXXXXXXX"
$userID = "3fa53ee0-XXXXXXXXx"

$AddUserUri = "https://vsaex.dev.azure.com/$($OrganizationName)/_apis/GroupEntitlements/$($TeamID)/members/$($userID)?api-version=5.1-preview.1"

Invoke-RestMethod -Uri $AddUserUri -Method put -Headers $AzureDevOpsAuthenicationHeader
```

With the Azure DevOps Services Rest API, you can automate Projects, Teams creation, and onboarding. You can also create a git branch, a pull request or work items, and many other things.

The difficult part, as you may notice, the URL is not unified, and you may have to deal with API version and URI. You will need to follow the documentation and the internal logic of the product. But after a few tries, you will be able to what you need.