Skip to content

CI CD Pipeline

Chris Panagiotidis edited this page Jun 13, 2026 · 3 revisions

CI/CD Pipeline

The repository uses GitHub Actions as the Terraform delivery pipeline.

CI/CD pipeline

Workflow source:

Custom action source:

Workflow Triggers

The workflow runs on:

Trigger Scope
Push to main Terraform, tfvars, modules, landing zones, environment files
Pull request to main Terraform and tfvars files
Manual workflow dispatch Select environment and action

Manual inputs:

Input Values
environment cheap-lab, dev, lab, prod
action plan, apply, destroy
destroy_confirm Must be DESTROY for destroy

Permissions

Workflow permissions include:

permissions:
  contents: write
  pull-requests: write
  security-events: write
  id-token: write

id-token: write is required for Azure OIDC login.

Required GitHub Secrets

Secret Purpose
AZURE_CLIENT_ID Federated Entra application client ID
AZURE_TENANT_ID Entra tenant ID
AZURE_SUBSCRIPTION_ID Target Azure subscription ID
TF_STATE_RG Resource group containing Terraform state storage account
TF_STATE_SA Terraform state storage account name
INFRACOST_API_KEY Optional Infracost key

Do not configure AZURE_CREDENTIALS for this pipeline.

Job Sequence

Stage Job Purpose Blocking
1 format terraform fmt -check -recursive -diff Yes
2 validate terraform init -backend=false and terraform validate Yes
3 security-tfsec tfsec Terraform scan Yes
3 security-checkov Checkov Terraform scan Yes
3 secret-scan Gitleaks secret scan Yes
4 tflint Terraform lint Advisory in current workflow
4 policy-check OPA policy evaluation Yes
4 terraform-docs Documentation generation/check Depends on action behavior
5 graph Terraform graph visualization Yes for later analysis
5 module-versions Module and provider version analysis Yes for plan
6 cost-estimate Infracost estimate Advisory
7 plan Terraform plan with remote state Yes
8 apply Manual apply from dispatch Protected by GitHub environment
9 destroy Manual destroy from dispatch Requires DESTROY and environment protection
10 metrics Post-apply metrics Runs after apply

Workflow-By-Workflow Review

Workflow area What it proves Evidence to keep
Format Terraform files follow a consistent style Job output with no diff
Validate Terraform graph is syntactically valid terraform validate success output
Security scans Known risky IaC patterns are caught tfsec and Checkov job results
Secret scan Repository does not contain obvious secrets Gitleaks job result
OPA policy Project-specific guardrails are enforced Policy-check output
Plan Backend, profile, variables, and Azure auth work Plan summary and artifact
Apply Approved plan can be applied Apply job and state backup result
Destroy Teardown requires explicit confirmation Destroy job approval and result

Gate Philosophy

The workflow separates hard gates from advisory checks:

  • Hard gates: format, validate, tfsec, Checkov, secret scan, OPA policy, plan.
  • Advisory gates: TFLint in the current workflow, cost estimate when Infracost is not configured.
  • Protected operations: apply and destroy through GitHub environments.

This keeps the lab safe while still allowing cost and lint output to guide improvements.

OIDC Proof Points

The Azure login step should prove:

Proof Expected value
Authentication method Federated token through GitHub OIDC
Client secret JSON Not required
id-token: write Present in workflow permissions
Azure identity Entra app registration with federated credential
Subject Matches repo branch, PR, or environment pattern

Failure signs:

  • Azure login asks for AZURE_CREDENTIALS.
  • Workflow lacks id-token: write.
  • Federated credential subject does not match the workflow context.
  • Apply works locally but fails in Actions because the CI identity lacks RBAC.

Custom Actions

Action Source Purpose
plan .github/actions/plan Azure login, backend init, Terraform plan, outputs plan metadata
apply .github/actions/apply Applies saved plan artifact
destroy .github/actions/destroy Controlled destroy
state-backup .github/actions/state-backup Copies state to backup container before apply/destroy
secret-scan .github/actions/secret-scan Runs Gitleaks
policy-check .github/actions/policy-check Runs OPA/Conftest style checks
cost-estimate .github/actions/cost-estimate Runs cost analysis
graph .github/actions/graph Generates Terraform graph artifact
module-version .github/actions/module-version Reviews module and provider versions
terraform-docs .github/actions/terraform-docs Documentation automation
resource-inventory .github/actions/resource-inventory Captures deployed resource inventory
changelog .github/actions/changelog Writes environment changelog
metrics .github/actions/metrics Writes deployment metrics
terratest .github/actions/terratest Optional Go/Terratest action
validate .github/actions/validate Reusable validation action
security .github/actions/security Reusable security action

Plan Behavior

The plan job:

  • Uses OIDC authentication.
  • Initializes backend using TF_STATE_RG and TF_STATE_SA.
  • Uses state key <environment>.terraform.tfstate.
  • Uses profile file environments/<environment>.tfvars.
  • Publishes plan artifact for apply.
  • Exposes plan counts: add, change, destroy.

Apply Behavior

The apply job:

  • Runs only for manual dispatch with action = apply.
  • Requires the plan job to succeed.
  • Uses the selected GitHub environment.
  • Backs up state before applying.
  • Applies the saved plan artifact.
  • Generates resource inventory and changelog afterward.

Destroy Behavior

The destroy job:

  • Runs only for manual dispatch with action = destroy.
  • Requires destroy_confirm = DESTROY.
  • Uses GitHub environment <environment>-destroy.
  • Backs up state before destroying.
  • Passes the selected tfvars file.

Recommended Environment Protection

Configure GitHub environments:

Environment Protection
cheap-lab Optional approval
lab Required approval
dev Required approval if shared subscription
prod Required approval
cheap-lab-destroy Required approval
lab-destroy Required approval
dev-destroy Required approval
prod-destroy Required approval from owner

Pipeline Validation Checklist

  • OIDC login succeeds without client secret.
  • format, validate, tfsec, checkov, secret-scan, and policy-check fail PRs when findings exist.
  • Cost estimate is visible but does not block when Infracost is not configured.
  • Plan artifact is created and consumed by apply.
  • State backup container is created before state copy.
  • Destroy requires both text confirmation and environment approval.

Gate Failure Examples

Failure Likely cause Fix path
terraform fmt fails Terraform files are not formatted Run terraform fmt -recursive locally and commit the formatting
terraform validate fails Missing variable, invalid type, provider graph issue Fix Terraform before planning
tfsec or Checkov fails Resource violates security baseline Harden resource or document justified suppression
Gitleaks fails Secret-like value was committed Remove value, rotate if real, recommit clean history as needed
OPA fails Project guardrail denied the plan Fix variables/resources or update policy with review
Azure login fails OIDC subject, audience, client ID, tenant ID, or RBAC is wrong Review federated credential and role assignments
Plan fails on backend State storage secret or permissions are wrong Review TF_STATE_RG, TF_STATE_SA, and storage RBAC

CI Evidence Package

For important changes, attach or record:

  • Workflow run URL.
  • Commit SHA.
  • Selected environment.
  • Plan add/change/destroy counts.
  • Security scan pass/fail summary.
  • OPA pass/fail summary.
  • Cost estimate or manual cost review.
  • Apply or destroy approval record.

Next page: Testing and Validation

Clone this wiki locally