Using Terraform to Manage Azure DevOps Project features

This is the 2nd part in the series of blog posts on managing the Azure DevOps using Terraform. You can find the series index here. In this blog post, we’ll discuss how to manage the Azure DevOps Project features – Boards, Pipelines, Test Plans, Repositories and Artifacts using Terraform. By default, when deploying an Azure DevOps Project, all these features are enabled. However, certain teams may like to enable only certain features for satisfying specific requirements. We can manage the same in Terraform deployment using the features block in the azuredevops_project resource or via using the separate azuredevops_project_features resource. We’ll also discuss which one is a better and preferred method and why.

Using the features block inside azuredevops_project Resource

We can enable or disable the Azure DevOps features – Boards, Pipelines, Test Plans, Repositories and Artifacts explicitly by using features block as below. Here we have used property value enabled or disabled to specify our requirement:

provider "azuredevops" {
version = ">= 0.0.1"
}
resource "azuredevops_project" "tf-example" {
project_name = "tf-example"
description = "Project deployed using Terraform"
version_control = "git"
visibility = "private"
work_item_template = "Agile"
features = {
"boards" = "enabled"
"repositories" = "enabled"
"pipelines" = "enabled"
"testplans" = "disabled"
"artifacts" = "enabled"
}
}
view raw main.tf hosted with ❤ by GitHub

If we now run terraform plan, we can see that it its going to change the project created earlier:

If we go ahead and run terraform apply, it will update the project features accordingly.

Using the azuredevops_project_features Resource

We can also use azuredevops_project_features Resource to specify and control the Azure DevOps project features that we want by using below block of code:

# incomplete block – will not work
resource "azuredevops_project_features" "tf-example-features" {
features = {
"boards" = "enabled"
"repositories" = "enabled"
"pipelines" = "enabled"
"testplans" = "disabled"
"artifacts" = "enabled"
}
}

However we need to tell it which project to operate on. This is done by using the project_id attribute. To find out the project_id associated with the Azure DevOps project, we need to use data source azuredevops_project. Data sources are Terraform’s way of calling the provider and return data in read-only format. We can then refer to the returned information inside the resource block by using dot notation or string interpolation, depending on our needs. Below code contains details for our use case:

provider "azuredevops" {
version = ">= 0.0.1"
}
resource "azuredevops_project" "tf-example" {
project_name = "tf-example"
description = "Project deployed using Terraform"
version_control = "git"
visibility = "private"
work_item_template = "Agile"
}
data "azuredevops_project" "tf-example-data" {
project_name = "tf-example"
}
resource "azuredevops_project_features" "tf-example-features" {
project_id = data.azuredevops_project.tf-example-data.id
features = {
"boards" = "enabled"
"repositories" = "enabled"
"pipelines" = "enabled"
"testplans" = "disabled"
"artifacts" = "enabled"
}
}
view raw main.tf hosted with ❤ by GitHub

Alternatively, since we are creating project using the same file, we can refer to it by using resource references:

provider "azuredevops" {
version = ">= 0.0.1"
}
resource "azuredevops_project" "tf-example" {
project_name = "tf-example"
description = "Project deployed using Terraform"
version_control = "git"
visibility = "private"
work_item_template = "Agile"
}
resource "azuredevops_project_features" "tf-example-features" {
project_id = azuredevops_project.tf-example.id
features = {
"boards" = "enabled"
"repositories" = "enabled"
"pipelines" = "enabled"
"testplans" = "disabled"
"artifacts" = "enabled"
}
}
view raw main.tf hosted with ❤ by GitHub

If we now run terraform plan, it will show us that it needs to add 1 resource:

We can now use terraform apply to apply our changes. Once deployed, we can switch to Azure DevOps to verify that our changes have been applied.

To use inside block or use separate resource?

Although it may seem like an additional complexity, using a separate resource to control the changes is the preferred way wherever possible. This allows the creation of more granular and more re-usable pieces of code. It is also less error-prone compared to the former as it limits the areas of code susceptible to change as per requirements. For example, team A wants to say enable Boards, Repos and Pipelines but team B wants to enable Repos, Test Plans and Pipelines only. Rather than re-writing the same block twice for two teams, we can use azuredevops_project as common resource for both teams and separate them using azuredevops_project_features resource.

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 )

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