production-platform-Azure

An Azure platform for running a service on AKS with Terraform, Argo CD, CI, observability and incident handling.

View the Project on GitHub Franklindot04/production-platform-Azure

The GitHub Actions workflows of the repository. Every action is pinned to a full commit SHA with its version in a comment. No workflow uses a stored client secret. The plan workflow uses secrets that hold identifiers only. Each one sets the permissions it needs and nothing more.

Workflow File Runs on Permissions
Terraform checks terraform-checks.yml Pull requests that change infrastructure/ contents: read
Terraform plan terraform-plan.yml Pull requests that change infrastructure/ or .github/workflows/terraform-plan.yml None at the top, and contents: read and id-token: write in the one job
Service, chart and manifest checks app-checks.yml Pull requests that change applications/, helm/, kubernetes/, gitops/ or tests/kind/ contents: read
Security scans security-scans.yml Pull requests that change infrastructure/, kubernetes/, helm/, gitops/, applications/ or .trivyignore.yaml contents: read
Publish image image-publish.yml Pushes to main that change applications/sample-service/, and manual runs contents: read, and packages: write in the one job that pushes
Open pull request for the image tag image-tag-bump.yml When Publish image succeeds on main None at the top, and contents: write and pull-requests: write in the one job

Terraform plan

Runs terraform plan -lock=false for the two dev roots (ADR 0022) on pull requests from this repository. It logs in with the plan identity through OIDC, passes the owner ID and the repository ID from the GitHub context to the persistent plan, and needs the four repository secrets listed under “Repository settings the workflows need”. If AZURE_CLIENT_ID is empty or not set, the plan is skipped with a notice.

Service, chart and manifest checks

Two jobs, both running tests/kind/check.sh:

The checks are described in tests/kind/README.md.

Security scans

Two jobs run Trivy (ADR 0020).

Publish image

Builds applications/sample-service for linux/amd64, which the AKS nodes use, and pushes it to ghcr.io/<owner>/sample-service with the full commit SHA as the only tag (ADR 0013). It logs in with the built-in token. The image carries the labels set by the metadata action.

Open pull request for the image tag

After Publish image succeeds on main, this workflow checks out main and sets the tag in gitops/dev/values/sample-service.yaml to the commit SHA with .github/scripts/set-image-tag.sh. .github/scripts/open-image-tag-pr.sh then commits it to a branch named chore/sample-service-image-<short SHA> and opens a pull request (ADR 0018). It never pushes to main.

The branch is named after the SHA, so it can exist already:

The pull request is opened in each case. If one is already open for the branch, that one is used. Open pull requests from earlier branches of that name are closed.

A pull request created with the built-in token does not start other workflows, so the checks do not run on it.

Repository settings the workflows need

These are settings in GitHub. A workflow cannot change them.

  1. The plan workflow needs the secrets AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_SUBSCRIPTION_ID and TFSTATE_ACCOUNT_NAME. They are set after the persistent root is applied. Without AZURE_CLIENT_ID the workflow prints a notice and skips its steps.
  2. Actions must be allowed to create pull requests: Settings, Actions, General, “Allow GitHub Actions to create and approve pull requests”. Without it, the last step of the tag workflow fails.
  3. The package must be public after its first publish: the package settings on GitHub. Until then the cluster cannot pull the image without a credential. A public package cannot be made private again.

Check the workflows locally

actionlint
./tests/kind/check.sh --static
.github/scripts/set-image-tag.sh gitops/dev/values/sample-service.yaml <40 character SHA>

The last command changes the file. Reset it with git checkout gitops/dev/values/sample-service.yaml.