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 setupcommand. 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:
| Workflow | Purpose |
|---|---|
tflint-scan.yml | Validates Terraform code quality and best practices |
checkov-scan.yml | Scans for security misconfigurations |
terraform-plan-pr-comment.yml | Posts 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:
| Trigger | Jobs executed |
|---|---|
| Push to main | TFLint → Checkov → Terraform Check → Terraform Apply |
| Pull request | TFLint → Checkov → Terraform Check → Terraform Plan → Plan Comment |
| Manual dispatch | Full 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.ymlconfiguration - Posts results to GitHub Step Summary
Key settings:
soft_fail: true- Workflow continues even if issues founddownload_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: stagingenables 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:
- GitHub generates JWT token for the workflow
- Workflow assumes IAM role using JWT
- AWS validates token against OIDC provider
- 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:
- Go to Settings → Environments
- Select environment (staging, production)
- Enable Required reviewers
- 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
- Local Development - Day-to-day workflows
- Environments - Multi-environment setup
- OIDC Provider - Module implementation