Skip to content

About

Reusable GitHub Actions and callable workflows for Terraform. Plan, apply, validate, destroy, multi-environment matrix deployments.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

github-actions-terraform-platform

CI License: MIT Conventional Commits

Reusable GitHub Actions and callable workflows for Terraform — plan, apply, validate, security scan, and multi-environment deployments.

A single source of truth for your Terraform CI/CD platform. Drop one uses: line into any repository and get a production-grade pipeline instantly.


What's inside

Composite actions

Atomic building blocks. Use them individually to compose custom pipelines.

Action Description
terraform-install Installs a pinned Terraform version via the official HashiCorp action
terraform-init terraform init with optional S3 backend config via inputs
terraform-validate fmt -check + validate — reports both results before failing
terraform-plan Plan with -detailed-exitcode, saves plan file, posts formatted PR comment
terraform-apply Applies a saved plan file — deterministic, no drift from what was reviewed
terraform-destroy Destroy with a confirm='destroy' safety guard
tflint TFLint with configurable rules or a .tflint.hcl config file
terraform-docs Generates docs via terraform-docs; optionally commits updated README

Callable reusable workflows

Full pipelines. Call with a single uses: line from any repository.

Workflow Description
terraform-qa Quality gate: fmt → validate → tflint → Checkov. No AWS credentials required.
terraform-deploy Full plan + apply with OIDC auth, PR comment, artifact handoff, and apply gate
terraform-multi-env Matrix plan across N environments in parallel; sequential apply dev → staging → prod

Quick start

Option A — one-line quality gate on every PR

# .github/workflows/terraform-ci.yml
on:
  pull_request:
    paths: ["terraform/**"]

jobs:
  qa:
    uses: MujahidAbdul/github-actions-terraform-platform/.github/workflows/terraform-qa.yml@main
    with:
      working_directory: terraform
      terraform_version: "1.8.0"
    secrets:
      github_token: ${{ secrets.GITHUB_TOKEN }}

Option B — plan on PR, apply on merge

# .github/workflows/deploy.yml
on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

jobs:
  qa:
    uses: MujahidAbdul/github-actions-terraform-platform/.github/workflows/terraform-qa.yml@main
    with:
      working_directory: terraform

  deploy:
    needs: qa
    uses: MujahidAbdul/github-actions-terraform-platform/.github/workflows/terraform-deploy.yml@main
    with:
      environment: prod
      working_directory: terraform
      aws_region: eu-west-2
      backend_config: |
        bucket=my-tfstate-bucket
        key=prod/terraform.tfstate
        region=eu-west-2
        dynamodb_table=terraform-state-lock
    secrets:
      aws_role_arn: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
      github_token: ${{ secrets.GITHUB_TOKEN }}

Option C — multi-environment matrix with promotion gates

# .github/workflows/multi-env.yml
on:
  push:
    branches: [main]

jobs:
  deploy:
    uses: MujahidAbdul/github-actions-terraform-platform/.github/workflows/terraform-multi-env.yml@main
    with:
      environments: '["dev","staging","prod"]'
      working_directory_prefix: environments
      terraform_version: "1.8.0"
      backend_config_template: |
        bucket=my-tfstate-bucket
        key={env}/terraform.tfstate
        region=eu-west-2
    secrets:
      aws_role_arn_dev:     ${{ secrets.AWS_ROLE_DEV }}
      aws_role_arn_staging: ${{ secrets.AWS_ROLE_STAGING }}
      aws_role_arn_prod:    ${{ secrets.AWS_ROLE_PROD }}
      github_token:         ${{ secrets.GITHUB_TOKEN }}

See examples/ for complete copy-paste pipelines with directory structure guidance.


AWS authentication

All deploy workflows use OIDC (OpenID Connect) — no long-lived AWS access keys stored as secrets.

IAM role trust policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::<account-id>:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:<org>/<repo>:environment:<env>"
        },
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        }
      }
    }
  ]
}

Minimum IAM permissions for the deploy role

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket",
        "dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:DeleteItem"
      ],
      "Resource": [
        "arn:aws:s3:::my-tfstate-bucket/*",
        "arn:aws:s3:::my-tfstate-bucket",
        "arn:aws:dynamodb:eu-west-2:<account-id>:table/terraform-state-lock"
      ]
    }
  ]
}

Add your resource-specific permissions on top of this backend minimum.


Versioning

Pin to a tag in production — never use @main in a real pipeline:

uses: MujahidAbdul/github-actions-terraform-platform/.github/workflows/terraform-deploy.yml@v1.0.0

Tags follow Semantic Versioning. Breaking input changes increment the major version.


Repository structure

github-actions-terraform-platform/
├── actions/
│   ├── terraform-install/   action.yml
│   ├── terraform-init/      action.yml
│   ├── terraform-validate/  action.yml
│   ├── terraform-plan/      action.yml
│   ├── terraform-apply/     action.yml
│   ├── terraform-destroy/   action.yml
│   ├── tflint/              action.yml
│   └── terraform-docs/      action.yml
├── workflows/
│   ├── terraform-qa.yml
│   ├── terraform-deploy.yml
│   └── terraform-multi-env.yml
├── examples/
│   ├── simple-pipeline/
│   └── multi-env-pipeline/
├── .github/workflows/ci.yml
├── .pre-commit-config.yaml
├── CONTRIBUTING.md
└── LICENSE

Design principles

Composable over monolithic. Every composite action does one thing. The callable workflows compose them — but you can compose them differently if your pipeline needs it.

No secrets in files. Backend config and AWS role ARNs are always passed as inputs or secrets, never committed. OIDC eliminates stored credentials entirely.

Deterministic apply. The plan file is always saved as an artifact and passed explicitly to apply. There is no "plan again on apply" pattern — what you reviewed is exactly what gets applied.

Environment isolation. Each environment maps to a separate IAM role and a separate GitHub environment. A compromised dev pipeline cannot touch prod state.

DRY across repos. One update to this repo propagates to every consumer on the next run. No copy-paste drift across dozens of pipeline files.


Requirements

Tool Minimum version
GitHub Actions runner ubuntu-latest (ubuntu-22.04+)
Terraform >= 1.5.0
TFLint >= 0.50.0
terraform-docs >= 0.18.0

Contributing

See CONTRIBUTING.md.

License

MIT © 2026 Mujahid Abdul

About

Reusable GitHub Actions and callable workflows for Terraform. Plan, apply, validate, destroy, multi-environment matrix deployments.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors