Why Automate Terraform?
Manual terraform apply from a developer laptop is a single point of failure. Who ran it? What was the plan? Were there conflicts with another engineer's changes? Automating Terraform through CI/CD solves all of these problems: every change is peer-reviewed, every plan is recorded, every apply is auditable.
OIDC Authentication (No Long-Lived Secrets)
Configure GitHub Actions to authenticate to AWS without storing access keys:
resource "aws_iam_openid_connect_provider" "github" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
}
resource "aws_iam_role" "github_actions" {
name = "github-actions-terraform"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Federated = aws_iam_openid_connect_provider.github.arn }
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringLike = {
"token.actions.githubusercontent.com:sub" = "repo:YOUR_ORG/YOUR_REPO:*"
}
}
}]
})
}
Plan on Pull Request
# .github/workflows/terraform.yml
name: Terraform
on:
pull_request:
branches: [main]
push:
branches: [main]
permissions:
id-token: write
contents: read
pull-requests: write
jobs:
terraform:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: "~1.7.0"
- name: Terraform Init
run: terraform init
- name: Terraform Validate
run: terraform validate
- name: Terraform Plan
id: plan
run: terraform plan -no-color -out=tfplan
- name: Comment Plan on PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const plan = require('fs').readFileSync('plan.txt', 'utf8');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '## Terraform Plan
```
' + plan + '
```'
});
- name: Terraform Apply
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: terraform apply -auto-approve tfplan
Multi-Environment Promotion
Use separate workflows triggered by branch or Git tag for environment promotion:
jobs:
deploy-dev:
if: github.ref == 'refs/heads/develop'
environment: dev
steps:
- run: terraform workspace select dev && terraform apply -auto-approve
deploy-staging:
if: github.ref == 'refs/heads/main'
environment: staging
needs: [test]
steps:
- run: terraform workspace select staging && terraform apply -auto-approve
deploy-prod:
if: startsWith(github.ref, 'refs/tags/v')
environment: production # requires manual approval in GitHub
needs: [deploy-staging]
steps:
- run: terraform workspace select prod && terraform apply -auto-approve
Cost Estimation with Infracost
- name: Infracost
uses: infracost/actions/setup@v2
with:
api-key: ${{ secrets.INFRACOST_API_KEY }}
- name: Generate cost estimate
run: |
infracost breakdown --path=. --format=json --out-file=infracost.json
infracost comment github --path=infracost.json --repo=$GITHUB_REPOSITORY --github-token=${{ github.token }} --pull-request=${{ github.event.pull_request.number }}
CloudFormation generates GitHub Actions workflows automatically from your canvas design. Click the Pipeline button and select GitHub Actions to get a complete workflow file with plan-on-PR, apply-on-merge, and optional Infracost integration.
Try CloudFormation Free
Design infrastructure visually and generate production-ready Terraform in seconds — 693 resources across 9 cloud providers.
🚀 Open the Designer →