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:
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:
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:
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" | |
} | |
} |
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.