Moving beyond demos: secure identity and cost visibility
Why Infrastructure Pipelines Still Fail
Too often, Terraform runs still happen on laptops with static AWS keys. The result?
Credentials at risk — long-lived keys sitting in repos or local machines
Undocumented changes — no audit trail, no shared visibility
Environment drift — dev, staging, and prod no longer match
Forgotten test resources — quietly inflating cloud bills
Running IaC this way feels fast — until it breaks consistency, costs money, or exposes credentials.
CI/CD fixes this, but to move beyond a “demo pipeline,” two practices make the difference in production:
- OIDC-based authentication → no long-lived credentials
- Cost visibility in PRs → prevent financial surprises
The 8 Steps of CI/CD (Infrastructure Edition)
- Code pushed → pipeline triggers
- Build → package artifact / generate Terraform plan
-
Validate → syntax check,
terraform validate
- Scan → security/compliance checks
- Deploy to dev/staging → safe sandbox
- Integration tests → real environment validation
- Deploy to prod → gated promotion
- Monitor → logs, costs, drift
This is the same framework as app pipelines — only the artifact changes.
Production-Ready Differentiators
1. OIDC: Secure, Short-Lived Credentials
Instead of storing AWS keys in GitHub secrets, pipelines use OIDC to assume roles dynamically.
- Short-lived credentials, nothing to leak
- Environment-specific IAM roles (dev/staging/prod)
- Every assumption logged in CloudTrail
Minimal AWS trust policy for GitHub OIDC (example):
resource "aws_iam_role" "terraform_dev" {
name = "uketui-terraform-cicd-dev"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.github.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"token.actions.githubusercontent.com:sub" = "repo:Anthonyuketui/Terraform-Project-with-Multi-Environment-Support-on-AWS:environment:dev"
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
}
}
}
]
})
}
2. Cost Visibility: Infracost in PRs
With a single step, you can post cost estimates into pull requests after terraform plan
.
Example PR comment:
This puts financial awareness into the code review process. A financial mistake gets caught before merge, not on the next AWS bill.
Example Dev Pipeline (GitHub Actions)
This workflow runs in a dev environment for iteration. It’s intentionally lightweight, but notice how it already includes the production-grade practices (OIDC, cost checks, environment isolation) that make the same design safe to scale into staging and prod.
name: Terraform Dev DevSecOps Pipeline
on:
pull_request:
branches:
- dev
push:
branches:
- dev
jobs:
cost-analysis:
runs-on: ubuntu-latest
environment: dev
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
- name: Run Infracost
uses: infracost/actions/setup@v2
with:
api-key: ${{ secrets.INFRACOST_API_KEY }}
- name: Generate Infracost diff
run: |
cd environments/dev
terraform init -backend=false
infracost breakdown --path . \
--format table \
--out-file cost-estimate.txt
- name: Output cost estimate
run: cat environments/dev/cost-estimate.txt
- name: Post cost estimate on PR
if: github.event_name == 'pull_request'
run: |
body="#### Infracost Estimate
\`\`\`
$(cat environments/dev/cost-estimate.txt)
\`\`\`"
echo "comment=${body}" >> $GITHUB_OUTPUT
id: cost_estimate
- name: Add PR comment
if: github.event_name == 'pull_request'
uses: actions/github-script@v6
with:
script: |
const body = `${{ steps.cost_estimate.outputs.comment }}`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});
terraform-dev:
runs-on: ubuntu-latest
environment: dev
needs: cost-analysis
permissions:
id-token: write
contents: read
env:
TF_IN_AUTOMATION: true
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.TERRAFORM_ROLE_ARN }}
aws-region: ${{ vars.AWS_REGION }}
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
- name: Terraform Format Check
working-directory: environments/dev
run: terraform fmt -recursive
- name: Terraform Init
working-directory: environments/dev
run: terraform init -backend-config=backend.tfvars
- name: Terraform Validate
working-directory: environments/dev
run: terraform validate
- name: Terraform Plan
working-directory: environments/dev
run: terraform plan -var-file=terraform.tfvars -out=tfplan
# -------------------------
# Optional: Conftest (policy checks)
# -------------------------
# - name: Run Conftest
# uses: instrumenta/conftest-action@master
# with:
# files: environments/dev/tfplan
# policy: policy/
# -------------------------
# Optional: Security checks (uncomment for strict DevSecOps)
# -------------------------
# security-checks:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout code
# uses: actions/checkout@v4
#
# - name: Run Checkov
# uses: bridgecrewio/checkov-action@v12
# with:
# directory: environments/dev
# framework: terraform
# skip_check: CKV_AWS_79,CKV_AWS_126
#
# - name: Run tfsec
# uses: aquasecurity/tfsec-action@v1.0.3
# with:
# working_directory: environments/dev
#
# - name: Run Terrascan
# uses: tenable/terrascan-action@main
# with:
# iac_type: 'terraform'
# iac_dir: 'environments/dev'
- name: Terraform Apply (on push only)
if: github.event_name == 'push'
working-directory: environments/dev
run: terraform apply -auto-approve tfplan
For staging/prod, add approval gates and stricter IAM roles.
Multi-Environment Setup
Use separate directories and state backends:
├── environments/
│ ├── dev/
│ ├── staging/
│ └── prod/
├── modules/
└── .github/workflows/
Each environment:
- Own
terraform.tfvars
+backend.tfvars
- Own S3 bucket + DynamoDB lock table
- Role isolation via IAM
Why This Matters
For engineers: this structure eliminates environment drift and makes infra deployments reproducible.
For managers: it reduces credential risk, prevents surprise costs, and creates a clear audit trail.
Top comments (0)