Managing Azure DevOps with Terraform
Learn how to provision Azure DevOps projects, repositories, pipelines, and service connections using the Terraform azuredevops provider
Managing Azure DevOps with Terraform
Azure DevOps CI/CD setup — projects, repos, pipelines, service connections, branch policies — is usually clicked together manually and then forgotten. The azuredevops Terraform provider lets you define that setup as code instead.
Prerequisites
- An Azure DevOps organization
- A Personal Access Token (PAT) with permissions to manage projects and pipelines
- Terraform installed (version 1.0.0 or later)
Provider Configuration
terraform {
required_providers {
azuredevops = {
source = "microsoft/azuredevops"
version = "~> 1.0"
}
}
}
provider "azuredevops" {
org_service_url = "https://dev.azure.com/${var.organization_name}"
personal_access_token = var.azdo_pat
}
Store azdo_pat in a secrets manager or CI secret store — never commit it to the repository.
Project and Repository
resource "azuredevops_project" "main" {
name = var.project_name
description = "Managed by Terraform"
visibility = "private"
version_control = "Git"
work_item_template = "Agile"
}
resource "azuredevops_git_repository" "main" {
project_id = azuredevops_project.main.id
name = "${var.project_name}-repo"
initialization {
init_type = "Clean"
}
}
Service Connection to Azure
resource "azuredevops_serviceendpoint_azurerm" "main" {
project_id = azuredevops_project.main.id
service_endpoint_name = "azure-connection"
credentials {
serviceprincipalid = var.service_principal_id
serviceprincipalkey = var.service_principal_key
}
azurerm_spn_tenantid = var.tenant_id
azurerm_subscription_id = var.subscription_id
azurerm_subscription_name = var.subscription_name
}
Build Pipeline
resource "azuredevops_build_definition" "main" {
project_id = azuredevops_project.main.id
name = "${var.project_name}-ci"
repository {
repo_type = "TfsGit"
repo_id = azuredevops_git_repository.main.id
branch_name = azuredevops_git_repository.main.default_branch
yml_path = "azure-pipelines.yml"
}
ci_trigger {
use_yaml = true
}
}
Branch Policy
resource "azuredevops_branch_policy_min_reviewers" "main" {
project_id = azuredevops_project.main.id
settings {
reviewer_count = 2
submitter_can_vote = false
scope {
repository_id = azuredevops_git_repository.main.id
repository_ref = azuredevops_git_repository.main.default_branch
match_type = "Exact"
}
}
}
Best Practices
- Secrets — keep the PAT and service principal credentials out of state files where possible; use a remote backend with encryption and restricted access.
- Least privilege — scope the PAT to only the permissions Terraform needs (project and pipeline management), not full organization admin.
- Branch protection — pair
azuredevops_build_definitionwith a branch policy so CI is required before merge, not optional. - State drift — Azure DevOps allows manual pipeline edits in the UI; if teams edit pipelines outside Terraform,
terraform planwill show constant drift. Decide on one source of truth.
Conclusion
Provisioning Azure DevOps projects and pipelines with Terraform makes onboarding a new repository (project, repo, service connection, CI trigger, branch policy) a single apply instead of a checklist of manual portal steps.