Skip to main content

Using Terraform with CI/CD

1. Integrating with GitHub Actions

Step Description Action
1 Set up a GitHub repository with Terraform configuration files terraform init
2 Configure GitHub Secrets for credentials (e.g., AWS access key) Repository Settings > Secrets and Variables
3 Create a GitHub Actions workflow file (e.g., .github/workflows/terraform.yml) YAML configuration in .github/workflows
4 Use the Terraform GitHub Action in your workflow hashicorp/setup-terraform

Example: GitHub Actions Workflow

name: "Terraform"
on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  terraform:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3

      - name: Terraform Init
        run: terraform init

      - name: Terraform Plan
        run: terraform plan -out=tfplan

      - name: Terraform Apply
        run: terraform apply tfplan
Note: Ensure that sensitive variables like AWS credentials are stored as GitHub Secrets and not hardcoded in workflows.
Warning: Always review the plan output before applying changes in a production environment.

Best Practices for CI/CD Integration

  • Use terraform plan before applying changes to prevent unintended modifications.
  • Store state files securely (e.g., in S3 with versioning and encryption).
  • Use terraform validate in workflows to catch configuration errors early.
  • Implement proper branching strategies (e.g., feature branches, main branch protection).

2. Integrating with GitLab CI

Feature Description Usage
Terraform Cloud Integration Use Terraform Cloud for remote operations and state management. terraform cloud
GitLab CI Variables Securely store and manage sensitive data using GitLab CI variables. TF_VAR_<variable>
Terraform State Storage Store state in remote backends like S3 or GCS for CI/CD. backend "s3"

Example: GitLab CI Pipeline Configuration

stages:
  - validate
  - plan
  - apply

validate:
  stage: validate
  script:
    - terraform init
    - terraform validate

plan:
  stage: plan
  script:
    - terraform init
    - terraform plan -out=tfplan
  artifacts:
    paths:
      - tfplan

apply:
  stage: apply
  script:
    - terraform init
    - terraform apply tfplan
  only:
    - master
Note: Ensure that GitLab CI runners have appropriate permissions to access Terraform state and execute commands.

3. Integrating with Jenkins

Property Description Example
Jenkins Plugin Plugin to manage Terraform in Jenkins pipelines terraform-plugin
Terraform Version Version of Terraform to use in Jenkins 1.9.5
Workspace Workspace to use for Terraform operations production

Example: Jenkins Pipeline Configuration

pipeline {
    agent any
    stages {
        stage('Setup') {
            steps {
                sh 'terraform init'
            }
        }
        stage('Plan') {
            steps {
                sh 'terraform plan -out=tfplan'
            }
        }
        stage('Apply') {
            steps {
                sh 'terraform apply tfplan'
            }
        }
    }
}
Note: Ensure Jenkins has appropriate permissions to access Terraform state and secrets.

Comparison: Jenkins vs Other CI/CD Tools

  • Jenkins: Extensible with plugins, supports complex workflows
  • GitHub Actions: Integrated with GitHub, simpler for basic pipelines
  • GitLab CI: Built-in with GitLab, easy to configure

4. Integrating with Azure DevOps

Feature Description Notes
Azure Pipeline YAML Define CI/CD pipelines using YAML files in Azure Repos Supports Terraform lifecycle operations
Terraform Cloud Integration Use Terraform Cloud for remote operations and state management Requires Azure DevOps integration setup
Service Connections Securely connect to Azure resources from pipelines Use service principals or managed identities

Example: Azure Pipeline YAML for Terraform

trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

steps:
- task: TerraformTaskV2@2
  inputs:
    provider: 'azurerm'
    command: 'init'
    backendType: 'azurerm'
    backendServiceArm: 'my-azure-service-connection'
    backendSettings: |
      resource_group_name = "tfstate-rg"
      storage_account_name = "tfstatestorage"
      container_name = "tfstatecontainer"
      key = "terraform.tfstate"

- task: TerraformTaskV2@2
  inputs:
    provider: 'azurerm'
    command: 'apply'
    environmentServiceNameARM: 'my-azure-service-connection'
Note: Ensure the Azure DevOps service connection has appropriate permissions to manage resources and access the Terraform backend storage.

Key Steps for Integration

  • Create an Azure DevOps project and repository
  • Set up a service connection to Azure with necessary permissions
  • Configure Terraform backend to use Azure Storage Account
  • Define pipeline YAML with Terraform tasks for init, plan, and apply

5. Automating Terraform Init

Option Values Notes
terraform init terraform init Initialize a Terraform working directory
-reconfigure -reconfigure Reconfigure the backend and providers
-upgrade -upgrade Upgrade to latest provider versions

Example: Automating terraform init in a CI pipeline

#!/bin/bash
set -e
echo "Initializing Terraform..."
terraform init \
  -reconfigure \
  -upgrade \
  -backend-config="bucket=terraform-state-bucket" \
  -backend-config="key=prod/terraform.tfstate"
Note: Ensure that the CI environment has appropriate credentials and permissions to access the backend storage (e.g., AWS S3, GCS).

6. Automating Plan and Apply

Example: CI/CD Pipeline with Terraform Plan and Apply

name: Terraform CI/CD
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  terraform:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3

      - name: Terraform Init
        run: terraform init

      - name: Terraform Plan
        run: terraform plan -out=tfplan
        env:
          TF_VAR_environment: ${{ github.ref_name }}

      - name: Terraform Apply
        run: terraform apply -auto-approve tfplan
        env:
          TF_VAR_environment: ${{ github.ref_name }}
Step Description Action
Checkout Fetches the code from the repository actions/checkout@v4
Setup Terraform Installs the specified version of Terraform hashicorp/setup-terraform@v3
Terraform Init Initializes the Terraform working directory terraform init
Terraform Plan Generates an execution plan and saves it to a file terraform plan -out=tfplan
Terraform Apply Applies the changes from the plan file terraform apply -auto-approve tfplan
Note: Using -auto-approve in CI/CD pipelines should be done carefully and only in trusted environments.
Warning: Ensure that sensitive variables (like API keys) are not exposed in logs or outputs.

Best Practices for Automating Terraform Plan and Apply

  • Use -out=tfplan to store the plan as a file for review and approval before applying.
  • Implement -auto-approve cautiously, preferably in environments with strict access control.
  • Validate changes using terraform plan before applying them in production.
  • Use environment-specific variables to manage different configurations (e.g., dev, staging, prod).
  • Ensure that CI/CD pipelines are configured to run only on specific branches or tags.

7. Storing State Remotely

Storing Terraform state remotely is essential for collaboration, security, and consistency in production environments. Remote state allows multiple team members to work with the same infrastructure while maintaining a single source of truth.

Backend Description Notes
local Stores state in a local file (terraform.tfstate) Not recommended for team collaboration or production use
remote Stores state in a remote backend (e.g., AWS S3, Azure Blob Storage) Enables shared state and locking for concurrent operations
s3 Uses AWS S3 for state storage and DynamoDB for locking Highly recommended for production environments
azurerm Uses Azure Blob Storage for state and Table Storage for locking Recommended for Azure-based infrastructures
gcs Uses Google Cloud Storage for state and Firestore for locking Recommended for GCP-based infrastructures

Example: Configuring S3 Backend

terraform {
  backend "s3" {
    bucket = "my-terraform-state-bucket"
    key    = "prod/terraform.tfstate"
    region = "us-west-2"
    dynamodb_table = "terraform-locks"
  }
}
Note: Ensure that the IAM role or user used for Terraform has appropriate permissions to read and write to the S3 bucket and DynamoDB table.
Warning: Never store sensitive backend configuration in version control. Use environment variables or encrypted secrets for sensitive values.

8. Managing Secrets

Method Description Notes
Terraform Variables Use variable blocks with sensitive = true Secrets are not logged in plan/apply output
Environment Variables Use TF_VAR_ prefix in CI/CD Securely pass secrets to Terraform
Vault Integration Use vault_generic_secret data source Store secrets in HashiCorp Vault
AWS Secrets Manager Use aws_secretsmanager_secret resource Store secrets in AWS Secrets Manager

Example: Using Sensitive Variables

variable "api_key" {
  description = "API key for external service"
  sensitive   = true
}

resource "aws_instance" "web" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"

  tags = {
    Name = "web-server"
  }
}

Example: Using Environment Variables

export TF_VAR_api_key="secret-key-here"
terraform plan
Note: Never store secrets in plain text Terraform files or version control.
Warning: Ensure CI/CD systems properly handle secret exposure in logs or output.

9. Using Terraform Cloud Runs

Field Description Values
terraform_cloud.runs Enables Terraform Cloud runs integration true
terraform_cloud.workspace Name of the Terraform Cloud workspace to use "my-workspace"
terraform_cloud.token API token for Terraform Cloud access "tfp.xxxxxxxxxx"

Example: Configuring Terraform Cloud Runs

terraform {
  cloud {
    organization = "myorg"
    workspaces {
      name = "my-workspace"
    }
  }
}

resource "aws_instance" "web" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"
}
Note: Ensure your Terraform Cloud token has appropriate permissions for the workspace.
Warning: Terraform Cloud runs require a valid organization and workspace name.

10. Implementing GitOps Workflows

Component Description Notes
Git Repository Source of truth for infrastructure as code (IaC) Must be version-controlled with history
Terraform State Tracks current infrastructure state and configuration Should be stored remotely (e.g., S3, Azure Blob)
CI/CD Pipeline Automates deployment and validation of infrastructure Can be implemented using GitHub Actions, GitLab CI, Jenkins
Branch Protection Rules Prevents direct pushes to main/master branches Enforces pull request reviews before merging

Example: GitHub Actions Workflow

name: "Terraform CI/CD"
on:
  push:
    branches: [ "main" ]
jobs:
  terraform:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v2

      - name: Terraform Init
        run: terraform init

      - name: Terraform Validate
        run: terraform validate

      - name: Terraform Plan
        run: terraform plan -out=tfplan

      - name: Terraform Apply
        run: terraform apply tfplan
Note: Ensure that sensitive variables (e.g., AWS credentials) are stored securely in GitHub Secrets or similar.

Key GitOps Principles

  • Infrastructure defined in version-controlled files
  • Changes reviewed through pull requests
  • Automated testing and validation before deployment
  • State managed externally (e.g., remote backend)

Was this article helpful?