In this blog post, we’ll walk through an Azure DevOps pipeline designed to deploy Terraform configurations. This pipeline has two primary steps:
- Running the Terraform Plan to identify changes.
- Executing the Terraform Apply to deploy the changes.
Additionally, the pipeline ensures a manual approval process via the
Kubernetes environment in Azure DevOps, where admins are notified to review and approve the deployment before proceeding. Let’s dive into the details.
Pipeline Overview
The pipeline is triggered by updates to the
main branch and allows the flexibility to target different AKS clusters (
cluster-dev,
cluster-qa,
cluster-prod). It uses an Ubuntu agent to execute the Terraform commands.
Step 1: Terraform Plan
The first stage of the pipeline,
Terraform_Plan, runs the Terraform plan command to identify changes in the infrastructure.
Code:
trigger:
- main
variables:
DEFAULT_AKS_CLUSTER: 'cluster-dev'
parameters:
- name: aksCluster
type: string
default: cluster-dev
values:
- cluster-qa
- cluster-dev
- cluster-prod
displayName: 'Select AKS Cluster'
pool:
vmImage: ubuntu-latest
stages:
- stage: Terraform_Plan
displayName: "Plan: ${{ parameters.aksCluster }}"
jobs:
- job: Plan
displayName: "Run Terraform Plan"
steps:
- script: |
terraform --version
displayName: Verify Terraform Installation
- script: |
echo "az login"
az login --service-principal -u $ARM_CLIENT_ID -p $(ARM_CLIENT_SECRET) --tenant $ARM_TENANT_ID
echo "az account show"
az account show
displayName: 'Azuze login'
- script: |
cd terraform
echo "Selecting workspace for ${{ parameters.aksCluster }}..."
ARM_CLIENT_SECRET=$(ARM_CLIENT_SECRET) terraform init
echo "terraform select workspace"
ARM_CLIENT_SECRET=$(ARM_CLIENT_SECRET) terraform workspace select ${{ parameters.aksCluster }}
echo "Running terraform plan for cluster ${{ parameters.aksCluster }}..."
ARM_CLIENT_SECRET=$(ARM_CLIENT_SECRET) terraform plan -var-file=environments/${{ parameters.aksCluster }}/terraform.tfvars -input=false -no-color -detailed-exitcode
retVal=$?
if [ $retVal -eq 2 ]; then
echo "Terraform plan changes detected"
echo "##vso[task.setvariable variable=ChangeDetected;isOutput=true]true"
fi
displayName: 'Terraform plan'
- script: |
echo "Logging out of Azure..."
az logout
displayName: 'Azure Logout'
Explanation:
-
Verify Terraform Installation: Ensures the Terraform CLI is available.
-
Azure Login: Authenticates to Azure using a service principal.
-
Terraform Plan: Initializes Terraform, selects the appropriate workspace, and generates a plan based on the terraform.tfvars file for the selected cluster. If changes are detected, it sets a variable (ChangeDetected) for the next stage.
-
Azure Logout: Cleans up by logging out of Azure.
The result of this stage determines if there are infrastructure changes requiring deployment.
Step 2: Terraform Apply
If changes are detected during the plan stage, the
Terraform_Apply stage deploys the updates. This stage uses the
Kubernetes environment in Azure DevOps, which requires manual approval by an admin.
Code:
- stage: Terraform_Apply
displayName: "Apply: ${{ parameters.aksCluster }}"
dependsOn: Terraform_Plan
condition: eq(dependencies.Terraform_Plan.outputs['Plan.CheckPlan.ChangeDetected'], 'true')
jobs:
- deployment: ApplyJob
displayName: "Run Terraform Apply"
environment: kubernetes # Requires manual approval configured in the Azure DevOps UI
strategy:
runOnce:
deploy:
steps:
- checkout: self
- script: |
echo "az login"
az login --service-principal -u $ARM_CLIENT_ID -p $(ARM_CLIENT_SECRET) --tenant $ARM_TENANT_ID
echo "az account show"
az account show
displayName: 'Azuze login'
- script: |
echo "az login"
az login --service-principal -u $ARM_CLIENT_ID -p $(ARM_CLIENT_SECRET) --tenant $ARM_TENANT_ID
echo "az account show"
az account show
displayName: 'Azuze login'
- script: |
cd terraform
echo "Selecting workspace for ${{ parameters.aksCluster }}..."
ARM_CLIENT_SECRET=$(ARM_CLIENT_SECRET) terraform init
ARM_CLIENT_SECRET=$(ARM_CLIENT_SECRET) terraform workspace select ${{ parameters.aksCluster }}
echo "Running terraform apply for cluster ${{ parameters.aksCluster }}..."
ARM_CLIENT_SECRET=$(ARM_CLIENT_SECRET) terraform apply -auto-approve -var-file=environments/${{ parameters.aksCluster }}/terraform.tfvars
displayName: 'Terraform Apply'
- script: |
echo "Logging out of Azure..."
az logout
displayName: 'Azure Logout'
Explanation:
-
Precondition: This stage runs only if changes were detected in the previous stage.
-
Manual Approval: The Kubernetes environment triggers an email notification to admins, requiring them to approve the deployment.
-
Terraform Apply: Once approved, the pipeline logs into Azure, initializes Terraform, selects the appropriate workspace, and applies the changes.
-
Azure Logout: Logs out of Azure to ensure secure session management.
How Manual Approval Works
The manual approval is configured in the Azure DevOps UI for the
kubernetes environment. When the pipeline reaches the
Terraform_Apply stage, it pauses and sends an email notification to the designated admins. Only after an admin approves the deployment does the pipeline proceed with applying the Terraform changes.
This feature ensures that sensitive infrastructure updates are reviewed and authorized before execution, adding an extra layer of control and security.
Conclusion
This Azure DevOps pipeline provides an efficient and secure way to manage Terraform deployments. By dividing the process into
plan and
apply stages and incorporating a manual approval step, it ensures that all infrastructure changes are reviewed and implemented with minimal risk.
Whether you’re deploying to a development, QA, or production environment, this approach offers the flexibility and control needed for robust infrastructure management.