Skip to content

Latest commit

 

History

History
359 lines (274 loc) · 8.69 KB

File metadata and controls

359 lines (274 loc) · 8.69 KB

Azure DevOps Pipeline Configuration Guide

Quick Setup Checklist

  • Azure subscription with appropriate permissions
  • Azure DevOps project
  • Azure Resource Manager service connection
  • Backend storage account for Terraform state (optional)
  • Pipeline variables configured
  • Environment created for approvals (optional)

Detailed Configuration Steps

1. Service Connection Setup

Create Service Principal

# Create a service principal
az ad sp create-for-rbac --name "terraform-sp" --role Contributor --scopes /subscriptions/{subscription-id}

Save the output - you'll need:

  • appId (Client ID)
  • password (Client Secret)
  • tenant (Tenant ID)

Create Service Connection in Azure DevOps

  1. Navigate to: Project Settings → Service connections → New service connection
  2. Select "Azure Resource Manager"
  3. Choose "Service principal (manual)"
  4. Fill in the details from the service principal
  5. Name it (e.g., "azure-terraform-connection")
  6. Grant access permissions to all pipelines

2. Backend Storage Configuration

Option A: Create Using Azure CLI

#!/bin/bash
# Set variables
RESOURCE_GROUP_NAME="tfstate-rg"
STORAGE_ACCOUNT_NAME="tfstate$(date +%s)"  # Must be globally unique
CONTAINER_NAME="tfstate"
LOCATION="eastus"

# Create resource group
az group create --name $RESOURCE_GROUP_NAME --location $LOCATION

# Create storage account
az storage account create \
  --name $STORAGE_ACCOUNT_NAME \
  --resource-group $RESOURCE_GROUP_NAME \
  --location $LOCATION \
  --sku Standard_LRS \
  --encryption-services blob \
  --https-only true \
  --min-tls-version TLS1_2

# Create blob container
az storage container create \
  --name $CONTAINER_NAME \
  --account-name $STORAGE_ACCOUNT_NAME

echo "Backend configuration:"
echo "resource_group_name: $RESOURCE_GROUP_NAME"
echo "storage_account_name: $STORAGE_ACCOUNT_NAME"
echo "container_name: $CONTAINER_NAME"

Option B: Create Using Azure Portal

  1. Go to Azure Portal → Storage Accounts → Create
  2. Fill in:
    • Resource Group: tfstate-rg
    • Storage Account Name: tfstatestorage (must be unique)
    • Region: East US
    • Performance: Standard
    • Redundancy: LRS
  3. Review + Create
  4. After creation, go to Containers → Add Container
  5. Name: tfstate

3. Pipeline Variables Configuration

Option A: In Pipeline YAML

Update variables section in the pipeline file:

variables:
  - name: azureServiceConnection
    value: 'azure-terraform-connection'  # Your service connection name
  - name: backendResourceGroupName
    value: 'tfstate-rg'
  - name: backendStorageAccountName
    value: 'tfstatestorage'  # Your storage account name
  - name: backendContainerName
    value: 'tfstate'
  - name: backendKey
    value: 'terraform.tfstate'

Option B: Using Variable Groups

  1. Go to: Pipelines → Library → Variable groups
  2. Create a new variable group named "terraform-config"
  3. Add variables:
    • azureServiceConnection
    • backendResourceGroupName
    • backendStorageAccountName
    • backendContainerName
    • backendKey
  4. In your pipeline, reference the variable group:
variables:
  - group: terraform-config

4. Terraform Variables Configuration

Method 1: Using terraform.tfvars (Not Recommended for Production)

# Copy example file
cp terraform/terraform.tfvars.example terraform/terraform.tfvars

# Edit with your values
# DO NOT commit this file

Method 2: Using Azure DevOps Pipeline Variables (Recommended)

Add variables in Azure DevOps:

  • TF_VAR_resource_group_name
  • TF_VAR_location
  • TF_VAR_storage_account_name

These automatically become Terraform variables.

Method 3: Using Variable Files in Pipeline

Create environment-specific variable files:

  • terraform/dev.tfvars
  • terraform/prod.tfvars

Update pipeline to pass the file:

- template: templates/terraform-plan.yml
  parameters:
    workingDirectory: $(workingDirectory)
    commandOptions: '-var-file="dev.tfvars"'

5. Environment Setup (For Approvals)

  1. Go to: Pipelines → Environments
  2. Click "New environment"
  3. Name: production
  4. Click "Create"
  5. Click on the environment
  6. Click "..." → Approvals and checks
  7. Add approvals:
    • Add yourself or team members as approvers
    • Configure timeout settings
  8. Optionally add other checks:
    • Business hours
    • Invoke REST API
    • Azure function

6. Grant Service Principal Access to Backend Storage

# Get storage account id
STORAGE_ID=$(az storage account show \
  --name $STORAGE_ACCOUNT_NAME \
  --resource-group $RESOURCE_GROUP_NAME \
  --query id --output tsv)

# Grant service principal access
az role assignment create \
  --assignee $SP_APP_ID \
  --role "Storage Blob Data Contributor" \
  --scope $STORAGE_ID

Pipeline Permissions

Required Permissions

Service Principal needs:

  • Contributor role on the subscription or resource groups
  • Storage Blob Data Contributor role on the state storage account

Limiting Permissions (Best Practice)

Instead of Contributor at subscription level:

# Create resource group first
az group create --name myapp-rg --location eastus

# Grant permissions only to specific resource group
az role assignment create \
  --assignee $SP_APP_ID \
  --role "Contributor" \
  --scope "/subscriptions/{subscription-id}/resourceGroups/myapp-rg"

Testing Your Configuration

Test 1: Verify Service Connection

# In Azure DevOps pipeline, add a test stage
- stage: Test
  jobs:
    - job: TestConnection
      steps:
        - task: AzureCLI@2
          inputs:
            azureSubscription: '$(azureServiceConnection)'
            scriptType: 'bash'
            scriptLocation: 'inlineScript'
            inlineScript: 'az account show'

Test 2: Verify Backend Access

# Test storage account access
az storage blob list \
  --container-name tfstate \
  --account-name tfstatestorage

Test 3: Manual Terraform Run

# Clone repo and test locally
cd terraform
terraform init \
  -backend-config="resource_group_name=tfstate-rg" \
  -backend-config="storage_account_name=tfstatestorage" \
  -backend-config="container_name=tfstate" \
  -backend-config="key=terraform.tfstate"
terraform plan

Environment-Specific Configurations

Using Workspaces

# In your pipeline
- script: |
    terraform workspace select $(environment) || terraform workspace new $(environment)
    terraform plan

Using Separate Backends

Update pipeline per environment:

# Dev pipeline
variables:
  - name: backendKey
    value: 'dev/terraform.tfstate'

# Prod pipeline  
variables:
  - name: backendKey
    value: 'prod/terraform.tfstate'

Troubleshooting Configuration Issues

Issue: "Backend initialization required"

Cause: Backend not properly configured Solution: Ensure all backend variables are set correctly

Issue: "Authorization failed"

Cause: Service principal lacks permissions Solution:

# Check current role assignments
az role assignment list --assignee $SP_APP_ID --output table

# Add missing permissions
az role assignment create --assignee $SP_APP_ID --role "Contributor" --scope "/subscriptions/{sub-id}"

Issue: "Storage account not found"

Cause: Storage account doesn't exist or wrong name Solution: Verify storage account exists:

az storage account show --name $STORAGE_ACCOUNT_NAME --resource-group $RESOURCE_GROUP_NAME

Advanced Configuration

Using Managed Identity Instead of Service Principal

Update service connection to use managed identity if Azure DevOps agent is Azure-hosted.

Implementing State Locking

Azure Storage automatically provides state locking. Verify it's enabled:

az storage account show \
  --name $STORAGE_ACCOUNT_NAME \
  --query "properties.supportsHttpsTrafficOnly"

Implementing State Encryption

Enable encryption at rest (default for Azure Storage):

az storage account update \
  --name $STORAGE_ACCOUNT_NAME \
  --resource-group $RESOURCE_GROUP_NAME \
  --encryption-services blob

Using Azure Key Vault for Secrets

- task: AzureKeyVault@2
  inputs:
    azureSubscription: '$(azureServiceConnection)'
    KeyVaultName: 'mykeyvault'
    SecretsFilter: '*'
    RunAsPreJob: true

Next Steps

After configuration:

  1. Run the validate stage to test Terraform syntax
  2. Run the plan stage to preview changes
  3. Review the plan output carefully
  4. Approve and run the apply stage
  5. Verify resources in Azure Portal

Getting Help