Skip to content

feat: add CI/CD best-practices project for GitHub Actions to Azure Container Apps - #138

Merged
tungbq merged 1 commit into
mainfrom
project/cicd-best-practices-container-apps
Aug 30, 2026
Merged

feat: add CI/CD best-practices project for GitHub Actions to Azure Container Apps#138
tungbq merged 1 commit into
mainfrom
project/cicd-best-practices-container-apps

Conversation

@tungbq

@tungbq tungbq commented Aug 30, 2026

Copy link
Copy Markdown
Owner

Summary

New project: cicd-best-practices-container-apps — a deliberately minimal Express app, and a GitHub Actions pipeline built to be an actual checklist of production deploy-pipeline practices, not just a happy-path demo:

  • No stored cloud credentials anywhere — OIDC federated auth for both az CLI and the Terraform azurerm provider (ARM_USE_OIDC); ACR has admin_enabled = false, CI pushes via its own granted AcrPush role, the Container App pulls via its own user-assigned managed identity with AcrPull — no shared registry password.
  • One definition of "passing" — the deploy workflow calls the PR-verify workflow as a reusable workflow_call job, so a production deploy can never skip a check a PR would have had to pass.
  • Immutable image tags — every build is tagged by github.sha, never :latest.
  • Vulnerability scan is a hard gate — Trivy runs and can fail the job before the image is ever pushed to the registry, both in PR-verify (on a throwaway build) and in deploy (on the real image about to ship).
  • Two-layer deploy safety — a typed "deploy" confirmation input, on top of a production GitHub Environment approval gate, on top of concurrency control so deploys can't overlap.
  • A smoke test that actually checks something — confirms the response body's commitSha matches the commit being deployed, not just a 200 status.
  • Automatic rollback — if the smoke test fails, the pipeline redeploys the previously-live image automatically.
  • Remote Terraform state (backend "azurerm" {}, populated via -backend-config from repo secrets) instead of runner-local state.

What was verified locally (no Azure credentials in this environment)

  • app/: builds, lints (eslint), unit-tests (jest+supertest), builds as a real multi-stage Docker image, runs correctly in a container, serves / and /health, confirmed non-root (whoaminode) and confirmed test files never reach the runtime image.
  • terraform/: fmt/validate pass; terraform init -backend=false + terraform plan resolve the entire local dependency graph (including the lifecycle.ignore_changes on the container image, so app deploys and terraform apply don't fight each other) against the real installed provider schema.
  • Both new workflow YAML files pass actionlint with zero findings — catches expression-syntax errors and malformed with: inputs, a stronger check than YAML-syntax validation alone.

What was NOT verified (documented in the project README)

  • No real workflow_dispatch run of deploy-cicd-best-practices-container-apps.yml — needs a real Azure subscription, an OIDC federated identity, and a pre-existing Terraform state storage account, none of which exist here. The project README's "Deploying for real" section is the complete setup walkthrough (OIDC app registration, state bootstrap, repo secrets, production Environment).

Test plan

  • ./demo_project.sh run end-to-end (app build/lint/test/run + Terraform validate) — all green
  • actionlint on both new workflow files — zero findings
  • Main README.md project table updated (row 23)
  • Real workflow_dispatch deploy against an actual Azure subscription (left for whoever configures OIDC + secrets for this repo — see project README)

https://claude.ai/code/session_01DgVRQrXf1xhAHKYsV2xVAQ

…ntainer Apps

New sample project: a minimal Express app plus a GitHub Actions pipeline
demonstrating OIDC-only auth (no stored client secret or registry
password), immutable commit-SHA image tags, a vulnerability scan gated
before push, Dockerfile linting, remote Terraform state, a typed
confirmation plus environment-approval gate before traffic shifts, a
smoke test that checks the deployed commit (not just a 200), automatic
rollback on smoke-test failure, and deploy concurrency control. The PR
verify path and the deploy path share one definition of "passing" via a
reusable workflow.

Verified locally: app builds, lints, unit-tests, and runs correctly in a
real Docker container; Terraform validates against the installed provider
schema and resolves its full local graph via `terraform init -backend=false`;
both workflow YAML files pass `actionlint` with zero findings. Not run
through a real workflow_dispatch deploy — no Azure credentials or OIDC
federated identity exist in this environment, matching every other Azure
project in this repo.
@tungbq
tungbq merged commit 7641495 into main Aug 30, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant