Prepare the required environment variables environment variables for the Terraform Providers. Also, fill in the default values for the Terraform variables in the the variables.tf file, or declare Terraform variables. Here are examples of the values expressed on a Bash shell:
# Environment variables required for the Terraform provider for
# Azure Resource Manager requirements. These reflect the credentials
# for an account principal, not your own Azure AD credentials.
#
export ARM_CLIENT_ID="1234abcd-56ef-12ab-34cd-567890efabcd"
export ARM_CLIENT_SECRET="1234abcd-56ef-12ab-34cd-567890efabcd"
export ARM_SUBSCRIPTION_ID="1234abcd-56ef-12ab-34cd-567890efabcd"
export ARM_TENANT_ID="1234abcd-56ef-12ab-34cd-567890efabcd"
# Environment variables required for the Terraform provider for Azure
# DevOps, and the Terraform provider for Terraform Cloud or Enteprise.
# Note that the values are exclusive to your Azure DevOps organization
# and your Terraform Cloud organization.
#
export AZDO_ORG_SERVICE_URL=https://dev.azure.com/hashicat-azdo
export AZDO_PERSONAL_ACCESS_TOKEN="REPLACE-ME-WITH-YOUR-AZDO-PAT"
export TFE_TOKEN="REPLACE-ME-WITH-YOUR-TFE-TOKEN"
# Terraform variables required to bootstrap the demo.
# Please note that that the AZDO project name and the
# AZDO repo name are arbitrary and must not already exist
# in Azure DevOps. If either exist, the deployment fails.
#
# Also, the values TFC org name and TFC token are exclusive
# to your Terraform Cloud organization.
#
export TF_VAR_azure_devops_project_name="azdo-primer-101"
export TF_VAR_azure_devops_repo_name="pipeline-starter"
export TF_VAR_azure_devops_org_name="hashicat-azdo"
export TF_VAR_arm_client_secret=$ARM_CLIENT_SECRET
export TF_VAR_tfc_org_name="interrupt-software"
export TF_VAR_tfc_token=$TFE_TOKEN
Use the following to stage the working environment.
terraform init
Use terraform apply
to build the entire environment. This will deploy all resources included in the source repositories.
terraform apply -auto-approve
With a successful run, you should be be able to navigate to the your Azure DevOps organization and see the project instantiated. It should look somewhat like this:
Alternatively, you have the option to walk through each module to create an additive rhythm to the demonstration of these assets. With this approach, create the Azure DevOps Project first.
terraform apply -target module.project
Once you are familiar with the pipelines, you can decide which example to run. For instance, if you are comfortable with the "Hello World, Let's Terraform" example, you can pick that directly as follows:
terraform apply -target module.pipeline1
The code in pipeline1/main.tf creates an Azure DevOps Pipeline that references the pipelines1/azdo-pipeline-01.yml resouce in this repo.
There is a potential error during the creation of the pipelines that may occur sporadically. AzureDevOps Guide writes that the error TF400898: An Internal Error Occurred
mostly occurs when you are trying to access information from Azure DevOps via some REST API calls. According to Microsoft this is mostly a timeout issue from Azure DevOps. If you try it again after sometime this error seems to goes away.
Error: error creating resource Build Definition: TF400898: An Internal Error Occurred.
Activity Id: 51d6419d-e7be-41a4-be56-1ba41886d8d5.
on pipeline3/main.tf line 24, in resource "azuredevops_build_definition" "build3":
24: resource "azuredevops_build_definition" "build3" {
The solution is to issue another terraform apply
command.
From Pipeline #4 and above, we assigning Collaborator
priviliges to the default service account in the pipeline. The privilege allows the pipeline service account to automatically commit changes to the repository's master branch. To look up the principal name
for that account and apply the permissions, we use a complicated sammich with hard-coded values. When we modify the Azure DevOps organization, you may find an error:
Error: Error in function call
on pipeline4/main.tf line 107, in data "azuredevops_users" "user":
107: principal_name = element(data.azuredevops_users.all_users.users[*],
index(data.azuredevops_users.all_users.users[*].display_name,
"azdo-starter-99 Build Service (hashicat-ado)")).principal_name
|----------------
| data.azuredevops_users.all_users.users is set of object with 3 elements
Call to function "index" failed: item not found.
The reason for that error is that the call is unable to find the appropriate organization. From the environment variable $AZDO_ORG_SERVICE_URL
above, note that the organization was exposed as hashicat-azdo
and the code references hashicat-ado
.
🗹 TO-DO: Code more better.
Update (09/08/21) - This is fixed. We added variables to handle the dependency.
data "azuredevops_users" "user" {
principal_name = element(data.azuredevops_users.all_users.users[*],
index(data.azuredevops_users.all_users.users[*].display_name,
"${var.project_name} Build Service (${var.org_name})")).principal_name
}
We're leaving this here in case we run into similar issues. If so, please report them.
Each provider has various prerequesite data points that need to be expressed as enviroment variables as part of the initial deployment.
Provider | Description | Environment variables |
---|---|---|
Azure Resource Manager | Provisions Azure Cloud resources through build pipelines. Also provides authentication for Azure subscription services for Azure DevOps tasks. | ARM_CLIENT_ID ARM_CLIENT_SECRET ARM_SUBSCRIPTION_ID ARM_TENANT_ID |
Azure DevOps | Create master project, respostories and build pipelines. We also use it update account permissions for pipeline users | AZDO_ORG_SERVICE_URL AZDO_PERSONAL_ACCESS_TOKEN |
Terraform | Sets up and configures Terraform Cloud workspaces, and links pipeline deployments to the appropriate workspaces. | TFE_TOKEN |
Random | Creates random pet names. | none |
In addition, the IaC deployments require the pass-thru of two specific variables that support the pipeline tasks. These are required by the work carried during the execution of terraform plan
and terraform apply
steps but are not strictly required to build the demo environment.
- TF_VAR_arm_client_secret is used during the
terraform plan
andterraform apply
steps within a pipeline. While the credentials can be inhereted from the triggering account (human/you) withaz login
, the instrospection of data sourcedata azurerm_client_config current {}
does not expose theclient secret
.
It is true that we can avoid exposing these variables with az login
to authenticate with your Azure Active Directory account instead of an Account Principal. Please note, however, that we are not using these credentials for this demo environment at all. Instead, we are publishing these to the pipelines as local secrets. The pipelines use these credentials to procure Azure services. We want the flexibility to depecreate these credentials once the demonstration exercise is completed.
In addition, we also plant the credentials in Terraform Cloud workspaces as environment secrets. The Azure authentication process from a Terraform Cloud workspace requires account principal credentials. Please note the the requirement for TF_VAR_arm_client_secret
below.
export ARM_CLIENT_ID="1234abcd-56ef-12ab-34cd-567890efabcd"
export ARM_CLIENT_SECRET="1234abcd-56ef-12ab-34cd-567890efabcd"
export ARM_SUBSCRIPTION_ID="1234abcd-56ef-12ab-34cd-567890efabcd"
export ARM_TENANT_ID="1234abcd-56ef-12ab-34cd-567890efabcd"
Create an Azure DevOps personal access token (PAT) to support remote operations with the Terraform Azure DevOps provider. There is a handy guide on how to create personal access tokens in the Azure DevOps Documentation.
Expose your Azure DevOps PAT and organization URL as follows:
export AZDO_PERSONAL_ACCESS_TOKEN="##########"
export AZDO_ORG_SERVICE_URL=https://dev.azure.com/hashicat-azdo
This is not strictly required to build the demo environment. We are using it in this manner to maintain the recommended convention for exposure. Some people like little tomatoes and some tomatillos.
export TFE_TOKEN="##########"
The following variables are required for the Terraform and environment that run in various Azure DevOps pipeline tasks. Please note that TF_VAR_azure_devops_org_name
is here to solve the dependency names on the pipeline service account.
export TF_VAR_arm_client_secret=$ARM_CLIENT_SECRET
export TF_VAR_tfc_token=$TFE_TOKEN
export TF_VAR_azure_devops_org_name="hashicat-azdo"