CI/CD Authentication — GitHub Actions to Azure (OIDC)
How the GitHub Actions pipelines authenticate to Azure to run tofu apply against the CNCL environment, using OIDC (workload identity federation) so no client secrets are stored in GitHub. Complements ASR Deployment Procedures (Infrastructure as Code) and the Deployment View.
Target environment (CNCL-DEV)
Section titled “Target environment (CNCL-DEV)”| Value | |
|---|---|
| Tenant | TopsectorLogistiek.nl — 5024789e-bb64-49c4-bb41-5983c2b0ceef |
| Subscription | ”Azure subscription 1” — 66ff09d0-4495-4a1d-8f5a-5b577c96b76c |
| Resource group | CNCL-DEV |
| Location | West Europe (westeurope) |
These values are also kept in .env (gitignored) for local tofu runs and map to the env/dev.tfvars var-file used by the root module in infrastructure/tofu.
Authentication model
Section titled “Authentication model”The azurerm provider authenticates via OIDC: GitHub Actions requests a short-lived ID token, Entra ID trusts it through a federated credential on a dedicated app registration, and grants the app’s RBAC roles. No secret to store or rotate.
Responsibilities
Section titled “Responsibilities”The Azure environment is managed by the platform party (tenant / Entra ID governance) and the delivery party delivers the application. The only hard dependency on the platform party is an Entra Application Developer role grant: guest accounts cannot create directory objects (app registrations) by default. With that role, the DIL side creates the app registration and handles the RBAC and state setup itself, because the solution architect already holds Contributor + User Access Administrator on CNCL-DEV.
Account model
Section titled “Account model”Azure and deployment work runs under DIL accounts (guest accounts in the TopsectorLogistiek tenant, home domain datainlogistics.nl). GitHub work uses the delivery party’s own GitHub accounts. The OIDC trust binds the repository to the service principal, so it is independent of any personal identity.
Platform party (Entra ID)
Section titled “Platform party (Entra ID)”Guest accounts cannot create app registrations by default, so the platform party:
- Grants the Entra Application Developer role to the DIL accounts so the app registration + federated credentials can be created in-house. (Requested.) (The specific DIL accounts are listed in the request to the platform party, not here.)
- Assigns Contributor on CNCL-DEV to the delivery party’s developers’ DIL accounts. (Not yet assigned.)
DIL (architect — has Contributor + UAA on CNCL-DEV)
Section titled “DIL (architect — has Contributor + UAA on CNCL-DEV)”Once Application Developer is granted:
-
Creates the app registration / service principal (e.g.
gh-cncl-bdi-deploy) → provides the Client ID used asARM_CLIENT_ID. -
Adds the federated credentials. Issuer is always
https://token.actions.githubusercontent.com. One credential per scenario:Scenario Subject Push to main repo:Basic-Data-Infrastructure/CNCL-BDI:ref:refs/heads/mainEnvironment devrepo:Basic-Data-Infrastructure/CNCL-BDI:environment:devPull requests (plan only) repo:Basic-Data-Infrastructure/CNCL-BDI:pull_requestRepo:
https://github.com/Basic-Data-Infrastructure/CNCL-BDI(public) -
Assigns the SP Contributor on
CNCL-DEV(UAA permits the role assignment). -
Creates the OpenTofu remote-state storage account + container and grants the SP Storage Blob Data Contributor.
Delivery party (application)
Section titled “Delivery party (application)”- Adds
infrastructure/tofuand.github/workflowsto the repo. - Sets the GitHub repository variables (below).
- Provisions the app’s Azure services in
CNCL-DEVvia OpenTofu and runs the pipeline (tofu apply).
Role assignments: creating resources needs Contributor; assigning roles (e.g. managed identity → Key Vault / storage) needs UAA. If the OpenTofu assigns roles itself, coordinate with the DIL architect (who has UAA) rather than widening the pipeline identity’s permissions.
GitHub configuration (no secrets with OIDC)
Section titled “GitHub configuration (no secrets with OIDC)”Repository variables (Settings → Secrets and variables → Actions → Variables):
| Variable | Value |
|---|---|
ARM_CLIENT_ID | Client ID of the app registration |
ARM_TENANT_ID | 5024789e-bb64-49c4-bb41-5983c2b0ceef |
ARM_SUBSCRIPTION_ID | 66ff09d0-4495-4a1d-8f5a-5b577c96b76c |
Workflow requirements:
permissions: id-token: write # request the OIDC token contents: read
env: ARM_USE_OIDC: "true" ARM_CLIENT_ID: ${{ vars.ARM_CLIENT_ID }} ARM_TENANT_ID: ${{ vars.ARM_TENANT_ID }} ARM_SUBSCRIPTION_ID: ${{ vars.ARM_SUBSCRIPTION_ID }}The same ARM_* variables are read by the azurerm provider for local tofu runs (via .env); tofu variable values (location, rg name) come from env/dev.tfvars.
Remote state
Section titled “Remote state”Recommended: an azurerm backend in a storage account inside CNCL-DEV. The deploy service principal needs Storage Blob Data Contributor on that account. The DIL architect creates the storage account + container and the role assignment (has Contributor + UAA), or it is handled in a small bootstrap step.
Environment validated (2026-07-08)
Section titled “Environment validated (2026-07-08)”Live check against CNCL-DEV confirmed the environment is further along than this checklist assumed:
- RGs
CNCL-NET/CNCL-DEV/CNCL-TST, state accountcnclbdidashtfstate(containertfstate) and ACRcnclbdidashacralready exist; the four required resource providers are Registered. - The DIL architect already holds Contributor + User Access Administrator on
CNCL-DEVandCNCL-NET— so the app registration, role assignments and state-backend access can all be done in-house without waiting on the platform party’s Contributor grant. - Entra admin group
CNCL-BDI-DEV-AKS-Adminscreated and wired into thedevtfvars. - The architect account was granted Storage Blob Data Contributor on the state account (for the tofu backend). The deploy SP still needs the same role once the app registration exists.
Handoff checklist
Section titled “Handoff checklist”Platform party
- Entra Application Developer role granted to DIL accounts (only needed if the app registration is not created by a global admin)
- Contributor on CNCL-DEV for the delivery party’s developers (DIL accounts)
DIL (architect)
- App registration created → Client ID
- Federated credential(s) per subject (repo
Basic-Data-Infrastructure/CNCL-BDI) - Deploy SP assigned Contributor on CNCL-DEV
- State storage account + container exist; Blob Data Contributor granted (architect done; deploy SP pending)
Delivery party
-
infrastructure/tofu+.github/workflowsadded to the repo - GitHub repository variables set (
ARM_CLIENT_ID/TENANT_ID/SUBSCRIPTION_ID) - App’s Azure services provisioned in CNCL-DEV via OpenTofu
- Pipeline runs
tofu applysuccessfully against CNCL-DEV
In samenwerking met




