Terraforming Azure DevOps: Importing a Build Definitions
Recently, I was working with the Azure DevOps Terraform provider to automate pipeline provisioning at work. Everything was going smoothly until I encountered a permissions issue: my Azure DevOps organization and project did not grant me the ability to delete Build Definitions. This presented a challenge because, while I could create and update Build Definitions using Terraform, I could not destroy them. As a result, importing existing resources became crucial.
The Challenge: Managing Pipelines in a Feature Branch
To streamline deployment, I created local Terraform modules that provision a single service across multiple environments. These pipelines are still in a feature branch, awaiting final approval before merging into the main branch. Fortunately, Azure DevOps allows referencing YAML files from a specific branch, making it possible to continue development without disrupting the main branch.
The Error: Conflict in Build Definition Creation
When I attempted to create a Build Definition using Terraform, I encountered an error indicating that a build pipeline with the same name already existed in the project:
Since Terraform identified an existing Build Definition, I needed to import it rather than attempt to create it from scratch. The challenge now was determining the correct Resource ID format for the import process.
module.svc_foo_processor.module.dev.azuredevops_build_definition.plan: Creating… ╷ │ Error: Ceating Build Definition: Build pipeline svc-foo-processor-dev PLAN already exists for project MyProject. │ │ with module.svc_foo_processor.module.dev.azuredevops_build_definition.plan, │ on modules/application-environment/main.tf line 2, in resource “azuredevops_build_definition” “plan”: │ 2: resource “azuredevops_build_definition” “plan” { │ ╵
Identifying the Correct Resource ID Format
At first, I assumed the resource name alone would be sufficient, so I tried importing it using only the Build Definition name:
module.svc_foo_processor.module.dev.azuredevops_build_definition.plan: Preparing import… [id=svc-foo-processor-dev PLAN] ╷ │ Error: error parsing the resource ID from the Terraform resource data: unexpected format of ID (svc-foo-processor-dev PLAN), expected projectid/resourceName │ │ ╵
This error message provided a valuable clue: the correct Resource ID format required both the Project ID and the Resource Name.
Extracting the Project ID and Resource Name
The Project ID can be retrieved using the azuredevops_project data source in Terraform:
data "azuredevops_project" "main" {
name = "MyOrg"
}
By dropping into the Terraform console and running:
data.azuredevops_project.main.id
I confirmed that the Project ID was, unsurprisingly, a GUID. However, the Resource Name was more elusive. Initially, I assumed it would be the Build Definition name, but further investigation in the Azure DevOps UI revealed that each Build Definition has a numeric definitionId visible in the URL:
https://dev.azure.com/MyOrg/MyProject/_build?definitionId=123456
This confirmed that the Build Definition’s identifier is a numeric integer rather than a GUID or a human-readable name.
Importing the Build Definition
With both required components in hand, I constructed the correct Resource ID by concatenating the Project ID (a GUID) and the Build Definition ID (an integer):
import {
id = "00000000-0000-0000-0000-000000000000/123456"
to = module.svc_foo_processor.module.dev.azuredevops_build_definition.plan
}
Success!
After running terraform apply, the output confirmed that the Build Definition was successfully imported:
Apply complete! Resources: 1 imported, 0 added, 1 changed, 0 destroyed.
By understanding the expected Resource ID format and leveraging Terraform’s import functionality, I was able to integrate my existing Azure DevOps Build Definition into my automated pipeline management workflow. This approach ensures consistency across environments while maintaining full control through Infrastructure as Code.