CI/CD Workflows

Understand how the automated CI/CD pipeline validates, tests, and deploys your Terraform infrastructure.


Overview

The starter kit's CI/CD pipeline automates infrastructure validation, security scanning, and deployment through GitHub Actions. Workflows use OpenID Connect (OIDC) for secure, keyless authentication to AWS.

Note: All workflows are automatically generated by the make setup command. See the Install guide.

Pipeline architecture

The starter kit includes four workflow files:

.github/workflows/
├── tflint-scan.yml                    # Reusable linting workflow
├── checkov-scan.yml                   # Reusable security scan workflow
├── terraform-plan-pr-comment.yml      # Reusable plan comment workflow
└── terraform-deploy-staging.yml       # Generated per environment

Reusable workflows

Three workflows are called from environment-specific workflows:

WorkflowPurpose
tflint-scan.ymlValidates Terraform code quality and best practices
checkov-scan.ymlScans for security misconfigurations
terraform-plan-pr-comment.ymlPosts Terraform plan to PR comments

Environment-specific workflows

Generated by make setup for each environment (e.g., terraform-deploy-staging.yml). These workflows:

  • Call the three reusable workflows
  • Contain environment-specific configuration
  • Handle the complete deployment lifecycle

Workflow triggers

Environment workflows trigger on three events:

on:
  push:
    branches:
      - main
    paths:
      - "environments/staging/**"
      - "modules/**"
      - ".github/workflows/terraform-deploy-staging.yml"
  pull_request_target:
    branches:
      - main
  workflow_dispatch:
TriggerJobs executed
Push to mainTFLint → Checkov → Terraform Check → Terraform Apply
Pull requestTFLint → Checkov → Terraform Check → Terraform Plan → Plan Comment
Manual dispatchFull pipeline (configurable)

Job breakdown

1. TFLint scan

Validates Terraform code quality:

tflint:
  name: TFLint Scan
  uses: your-org/your-repo/.github/workflows/tflint-scan.yml@main # local workflow reference

What it does:

  • Checks out code
  • Caches TFLint plugin directory
  • Runs tflint -f compact --recursive
  • Posts results to GitHub Step Summary

Local equivalent: make lint

2. Checkov security scan

Scans for security misconfigurations:

checkov:
  name: Checkov Security Scan
  uses: your-org/your-repo/.github/workflows/checkov-scan.yml@main # local workflow reference
  with:
    working_directory: "environments/staging"
    soft_fail: true

What it does:

  • Runs Checkov with Terraform framework
  • Downloads external modules for scanning
  • Uses .checkov.yml configuration
  • Posts results to GitHub Step Summary

Key settings:

  • soft_fail: true - Workflow continues even if issues found
  • download_external_modules: true - Scans module dependencies

Local equivalent: make security-scan

3. Terraform check

Validates configuration and formatting:

terraform-check:
  needs: [tflint, checkov]
  steps:
    - name: Terraform Format Check
      run: terraform fmt -check -recursive

    - name: Terraform Init
      run: terraform init -backend=false

    - name: Terraform Validate
      run: terraform validate

What it does:

  • Validates code formatting
  • Initializes without backend (-backend=false)
  • Validates configuration syntax

Local equivalent: make validate-full ENV=staging

4. Terraform plan (PR only)

Generates execution plan for review:

terraform-plan:
  needs: terraform-check
  if: github.event_name == 'pull_request_target'
  steps:
    - name: Configure AWS credentials (OIDC)
      uses: aws-actions/configure-aws-credentials@v4
      with:
        role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/${{ env.GITHUB_ACTIONS_ROLE_NAME }}

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

    - name: Save Plan Artifact
      uses: actions/upload-artifact@v5

Key features:

  • Only runs on pull requests
  • Uses OIDC for AWS authentication
  • Saves plan binary as artifact

5. Plan comment (PR only)

Posts plan to pull request:

plan-comment:
  needs: terraform-plan
  if: github.event_name == 'pull_request_target'
  uses: your-org/your-repo/.github/workflows/terraform-plan-pr-comment.yml@main # local workflow reference

Uses towardsthecloud/terraform-plan-pr-commenter@v1 to:

  • Convert binary plan to readable format
  • Post formatted plan as PR comment
  • Include header with environment and region

6. Terraform apply (push to main)

Applies changes to AWS:

terraform-apply:
  needs: terraform-check
  if: github.ref == 'refs/heads/main' && github.event_name == 'push'
  environment: staging
  steps:
    - name: Configure AWS credentials (OIDC)
    - name: Terraform Apply
      run: terraform apply -auto-approve
    - name: Terraform Output
      run: terraform output >> $GITHUB_STEP_SUMMARY

Key features:

  • Only runs on pushes to main
  • environment: staging enables protection rules
  • Uses OIDC for authentication
  • Posts outputs to GitHub Step Summary

OIDC authentication

Workflows use OIDC for secure, keyless AWS authentication:

- name: Configure AWS credentials (OIDC)
  uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/${{ env.GITHUB_ACTIONS_ROLE_NAME }}
    aws-region: ${{ env.AWS_REGION }}

How it works:

  1. GitHub generates JWT token for the workflow
  2. Workflow assumes IAM role using JWT
  3. AWS validates token against OIDC provider
  4. AWS STS returns temporary credentials

Security benefits:

  • No AWS credentials stored in GitHub Secrets
  • Temporary credentials automatically expire
  • Repository-scoped access via trust policy
  • Full audit trail via CloudTrail

Required permissions:

permissions:
  id-token: write
  contents: read
  pull-requests: write

Learn more: How to set up GitHub OIDC federation for AWS

Environment protection

The environment: staging field enables GitHub environment protection:

terraform-apply:
  environment: staging  # Enables protection rules

To require manual approval:

  1. Go to SettingsEnvironments
  2. Select environment (staging, production)
  3. Enable Required reviewers
  4. Add approvers

Path-based filtering

Workflows only run when relevant files change:

paths:
  - "environments/staging/**"
  - "modules/**"
  - ".github/workflows/terraform-deploy-staging.yml"

Benefits:

  • No runs for unrelated changes
  • Environment changes don't trigger other environments
  • Faster CI/CD with fewer unnecessary runs

Environment variables

Each workflow includes environment variables:

env:
  AWS_ACCOUNT_ID: ${{ vars.AWS_ACCOUNT_ID || '123456789012' }}
  AWS_REGION: ${{ vars.AWS_REGION || 'eu-west-1' }}
  GITHUB_ACTIONS_ROLE_NAME: ${{ vars.GITHUB_ACTIONS_ROLE_NAME || 'GitHubActionsServiceRole-Terraform' }}
  ENVIRONMENT: staging
  TF_WORKING_DIR: environments/staging
  TF_STATE_BUCKET: ${{ vars.TF_STATE_BUCKET || 'terraform-state-123456789012-eu-west-1' }}

Values are embedded as defaults during setup. Override by creating GitHub repository variables.

Next steps