Terraform_cicd
Chapter 39: Terraform CI/CD
Section titled “Chapter 39: Terraform CI/CD”This chapter covers integrating Terraform into CI/CD pipelines for automated infrastructure management.
CI/CD Pipeline Overview
Section titled “CI/CD Pipeline Overview”┌─────────────────────────────────────────────────────────────────────────────┐│ Terraform CI/CD Pipeline │├─────────────────────────────────────────────────────────────────────────────┤│ ││ ┌─────────────────────────────────────────────────────────────────────┐ ││ │ Pipeline Stages │ ││ │ │ ││ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ ││ │ │ Init │──▶│ Plan │──▶│ Plan │──▶│ Apply │ │ ││ │ │ │ │ │ │ Review │ │ │ │ ││ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ ││ │ │ ││ │ • terraform • terraform • Comment • terraform │ ││ │ init plan on PR apply │ ││ │ │ ││ │ • Backend • Validate • Auto- • Approval │ ││ │ setup • Format approve (optional) │ ││ │ • Test │ ││ │ │ ││ └─────────────────────────────────────────────────────────────────────┘ ││ │└─────────────────────────────────────────────────────────────────────────────┘GitHub Actions Workflow
Section titled “GitHub Actions Workflow”name: Terraform
on: push: branches: [main] pull_request: branches: [main]
env: TF_VERSION: '1.6.0' AWS_REGION: 'us-east-1'
jobs: terraform: name: Terraform runs-on: ubuntu-latest permissions: id-token: write contents: read
steps: - name: Checkout uses: actions/checkout@v4
- name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/terraform-ci aws-region: ${{ env.AWS_REGION }}
- name: Setup Terraform uses: hashicorp/setup-terraform@v3 with: terraform_version: ${{ env.TF_VERSION }} cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
- name: Terraform Format Check run: terraform fmt -check -recursive continue-on-error: true
- name: Terraform Init run: terraform init working-directory: ./terraform
- name: Terraform Validate run: terraform validate
- name: Terraform Plan id: plan run: terraform plan -no-color -out=tfplan working-directory: ./terraform continue-on-error: true
- name: Post Plan Comment if: github.event_name == 'pull_request' uses: actions/github-script@v7 with: script: | const output = `#### Terraform Format and Style 🖌\`${{ steps.plan.outcome }}\`
#### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\`
#### Terraform Validation 🟰\`${{ steps.validation.outcome }}\`
<details><summary>Show Plan</summary>
\`\`\` ${{ steps.plan.outputs.stdout }} \`\`\`
</details>
*Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: output })
- name: Terraform Apply (on main) if: github.ref == 'refs/heads/main' && github.event_name == 'push' run: terraform apply -auto-approve tfplan working-directory: ./terraformGitLab CI Pipeline
Section titled “GitLab CI Pipeline”stages: - validate - plan - apply
variables: TF_VERSION: "1.6.0" AWS_DEFAULT_REGION: "us-east-1"
terraform: image: hashicorp/terraform:${TF_VERSION} stage: validate script: - terraform init - terraform validate - terraform fmt -check -recursive artifacts: paths: - .terraform/ expire_in: 1 hour
terraform-plan: image: hashicorp/terraform:${TF_VERSION} stage: plan script: - terraform init - terraform plan -out=tfplan - echo "TFPLAN=$(base64 -w0 tfplan)" > tfplan.env artifacts: paths: - tfplan - tfplan.env expire_in: 1 week only: - merge_requests
terraform-apply: image: hashicorp/terraform:${TF_VERSION} stage: apply script: - terraform init - terraform apply tfplan environment: name: production when: manual only: - main dependencies: - terraform-planJenkins Pipeline
Section titled “Jenkins Pipeline”// Jenkinsfilepipeline { agent any
environment { AWS_REGION = 'us-east-1' TERRAFORM_VERSION = '1.6.0' }
stages { stage('Checkout') { steps { checkout scm } }
stage('Terraform Init') { steps { dir('terraform') { sh ''' terraform init terraform workspace select ${WORKSPACE_ENV} || terraform workspace new ${WORKSPACE_ENV} ''' } } }
stage('Terraform Validate') { steps { dir('terraform') { sh 'terraform validate' } } }
stage('Terraform Plan') { steps { dir('terraform') { sh 'terraform plan -out=tfplan' } } }
stage('Terraform Apply') { when { branch 'main' } steps { dir('terraform') { sh 'terraform apply -auto-approve tfplan' } } } }
post { always { cleanWs() } }}Azure DevOps Pipeline
Section titled “Azure DevOps Pipeline”trigger: - main
pr: - main
variables: terraformVersion: '1.6.0' azureServiceConnection: 'azure-sp' resourceGroupName: 'rg-terraform' storageAccountName: 'stterraformstate' containerName: 'tfstate'
stages: - stage: Validate displayName: 'Validate' jobs: - job: terraform_validate displayName: 'Terraform Validate' pool: vmImage: 'ubuntu-latest' steps: - task: TerraformInstaller@1 inputs: terraformVersion: $(terraformVersion)
- task: TerraformTaskV4@4 inputs: provider: 'aws' command: 'validate' workingDirectory: '$(System.DefaultWorkingDirectory)/terraform'
- task: TerraformTaskV4@4 inputs: provider: 'aws' command: 'init' backendServiceAWS: '$(awsServiceConnection)' backendAWSBucketName: '$(storageAccountName)' backendAWSKey: '$(Build.Repository.Name)/terraform.tfstate' workingDirectory: '$(System.DefaultWorkingDirectory)/terraform'
- stage: Plan displayName: 'Plan' jobs: - job: terraform_plan displayName: 'Terraform Plan' pool: vmImage: 'ubuntu-latest' steps: - task: TerraformInstaller@1 inputs: terraformVersion: $(terraformVersion)
- task: TerraformTaskV4@4 inputs: provider: 'aws' command: 'plan' commandOptions: '-out=tfplan' environmentServiceNameAWS: '$(awsServiceConnection)' workingDirectory: '$(System.DefaultWorkingDirectory)/terraform'
- stage: Apply displayName: 'Apply' dependsOn: Plan condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main')) jobs: - job: terraform_apply displayName: 'Terraform Apply' pool: vmImage: 'ubuntu-latest' steps: - task: TerraformInstaller@1 inputs: terraformVersion: $(terraformVersion)
- task: TerraformTaskV4@4 inputs: provider: 'aws' command: 'apply' commandOptions: '-auto-approve tfplan' environmentServiceNameAWS: '$(awsServiceConnection)' workingDirectory: '$(System.DefaultWorkingDirectory)/terraform'Pre-Commit Hooks
Section titled “Pre-Commit Hooks”repos: - repo: https://github.com/antonbabenko/pre-commit-terraform rev: v1.85.0 hooks: - id: terraform_fmt - id: terraform_validate - id: terraform_docs args: ['--args', '--sort-by-required=false'] - id: terraform_tfsec args: ['--args', '--exclude-downloaded-modules'] - id: tfupdate exclude: ^examples/Drift Detection
Section titled “Drift Detection”┌─────────────────────────────────────────────────────────────────────────────┐│ Drift Detection Workflow │├─────────────────────────────────────────────────────────────────────────────┤│ ││ ┌─────────────────────────────────────────────────────────────────────┐ ││ │ Scheduled Job │ ││ │ │ ││ │ ┌─────────────┐ │ ││ │ │ Schedule │ (daily/hourly) │ ││ │ └──────┬──────┘ │ ││ │ │ │ ││ │ ▼ │ ││ │ ┌─────────────┐ │ ││ │ │terraform │ │ ││ │ │ plan │ Compare state vs. reality │ ││ │ └──────┬──────┘ │ ││ │ │ │ ││ │ ▼ │ ││ │ ┌─────────────┐ │ ││ │ │ Drift? │──No──▶ Continue │ ││ │ └──────┬──────┘ │ ││ │ │Yes │ ││ │ ▼ │ ││ │ ┌─────────────┐ │ ││ │ │ Alert/ │ (Slack, PagerDuty, Email) │ ││ │ │ Notify │ │ ││ │ └─────────────┘ │ ││ │ │ ││ └─────────────────────────────────────────────────────────────────────┘ ││ │└─────────────────────────────────────────────────────────────────────────────┘Drift Detection Script
Section titled “Drift Detection Script”#!/bin/bashcd terraform
terraform init -backend=false
PLAN_OUTPUT=$(terraform plan -detailed-exitcode 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -eq 2 ]; then # Changes detected - drift exists echo "DRIFT DETECTED!" echo "$PLAN_OUTPUT"
# Send alert curl -X POST "$SLACK_WEBHOOK_URL" \ -H 'Content-Type: application/json' \ -d "{\"text\": \"⚠️ Terraform drift detected in $ENVIRONMENT\n\n$PLAN_OUTPUT\"}"
exit 1elif [ $EXIT_CODE -eq 0 ]; then echo "No drift detected" exit 0else echo "Error running terraform plan" exit 1fiSummary
Section titled “Summary”In this chapter, you learned:
- CI/CD Overview: Pipeline stages for Terraform
- GitHub Actions: Workflow for GitHub-based CI/CD
- GitLab CI: Pipeline configuration for GitLab
- Jenkins: Pipeline script for Jenkins
- Azure DevOps: YAML pipeline for Azure
- Pre-commit Hooks: Automated validation
- Drift Detection: Monitoring for infrastructure changes