Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with:
node-version: "24"
cache: "npm"
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ jobs:
contents: read

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Azure login (OIDC — staging managed identity)
uses: azure/login@v2
uses: azure/login@v3
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
Expand Down Expand Up @@ -65,10 +65,10 @@ jobs:
APP_NAME: ca-acroyoga-web-staging

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Azure login (OIDC — staging managed identity)
uses: azure/login@v2
uses: azure/login@v3
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
Expand Down Expand Up @@ -114,10 +114,10 @@ jobs:
APP_NAME: ca-acroyoga-web-production

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Azure login (OIDC — production managed identity)
uses: azure/login@v2
uses: azure/login@v3
with:
client-id: ${{ secrets.AZURE_CLIENT_ID_PRODUCTION }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
Expand Down
16 changes: 8 additions & 8 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ jobs:
contents: read

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with:
node-version: "24"
cache: "npm"
Expand Down Expand Up @@ -110,10 +110,10 @@ jobs:
contents: read

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Azure login (OIDC — nightly identity for ACR push)
uses: azure/login@v2
uses: azure/login@v3
with:
client-id: ${{ secrets.AZURE_CLIENT_ID_NIGHTLY }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
Expand Down Expand Up @@ -159,10 +159,10 @@ jobs:
APP_NAME: ca-acroyoga-web-nightly

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Azure login (OIDC — nightly managed identity)
uses: azure/login@v2
uses: azure/login@v3
with:
client-id: ${{ secrets.AZURE_CLIENT_ID_NIGHTLY }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
Expand All @@ -179,8 +179,8 @@ jobs:
dbAdminPassword="${{ secrets.DB_ADMIN_PASSWORD }}" \
nextAuthSecret="$(openssl rand -base64 32)" \
sharedContainerRegistryLoginServer="${{ secrets.AZURE_CONTAINER_REGISTRY }}" \
githubOrg="microsoft" \
githubRepo="CommunityManagement-Sample-Spec-Kit"
githubOwnerId="${{ github.repository_owner_id }}" \
githubRepoId="${{ github.repository_id }}"

- name: Wait for readiness
run: |
Expand Down
12 changes: 6 additions & 6 deletions infra/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ param memorySize string = '1Gi'
@description('Email address for alert notifications')
param alertEmailAddress string = ''

@description('GitHub organisation name (e.g. "microsoft"). When set together with githubRepo, OIDC federated identity credentials are provisioned on the managed identity so GitHub Actions can authenticate without stored secrets.')
param githubOrg string = ''
@description('GitHub repository owner (organisation) numeric ID (e.g. "6154722"). When set together with githubRepoId, OIDC federated identity credentials are provisioned on the managed identity so GitHub Actions can authenticate without stored secrets.')
param githubOwnerId string = ''

@description('GitHub repository name (e.g. "CommunityManagement-Sample-Spec-Kit"). Required when githubOrg is set.')
param githubRepo string = ''
@description('GitHub repository numeric ID (e.g. "1182392763"). Required when githubOwnerId is set.')
param githubRepoId string = ''

@description('Deploy Azure Front Door CDN. Set to false for non-user-facing environments like nightly.')
param deployFrontDoor bool = true
Expand All @@ -101,8 +101,8 @@ module identity 'modules/managed-identity.bicep' = {
params: {
environmentName: environmentName
location: location
githubOrg: githubOrg
githubRepo: githubRepo
githubOwnerId: githubOwnerId
githubRepoId: githubRepoId
// Only the staging identity gets the main-branch FIC (for the build-and-push CI job)
addMainBranchFic: environmentName == 'staging'
}
Expand Down
22 changes: 12 additions & 10 deletions infra/modules/managed-identity.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ param environmentName string
@description('Azure region')
param location string = resourceGroup().location

@description('GitHub organisation name (e.g. "microsoft"). When set, OIDC federated identity credentials are created on the managed identity so GitHub Actions can authenticate without stored secrets (Constitution XIV).')
param githubOrg string = ''
@description('GitHub repository owner (organisation) numeric ID (e.g. "6154722"). When set together with githubRepoId, OIDC federated identity credentials are created using the org-level customised subject format (Constitution XIV).')
param githubOwnerId string = ''

@description('GitHub repository name (e.g. "CommunityManagement-Sample-Spec-Kit"). Required when githubOrg is set.')
param githubRepo string = ''
@description('GitHub repository numeric ID (e.g. "1182392763"). Required when githubOwnerId is set.')
param githubRepoId string = ''

@description('When true, add a federated credential for the main branch push event (used by the build-and-push CI job). Only set to true for the staging environment.')
param addMainBranchFic bool = false
Expand All @@ -28,25 +28,27 @@ resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-
}

// Federated Identity Credential — GitHub Actions environment (deploy job)
// Subject: repo:<org>/<repo>:environment:<env> (protected by GH environment rules)
resource ficEnvironment 'Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials@2023-01-31' = if (!empty(githubOrg) && !empty(githubRepo)) {
// Subject uses org-level customised OIDC claim template:
// repository_owner_id:<owner_id>:repository_id:<repo_id>:environment:<env>
resource ficEnvironment 'Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials@2023-01-31' = if (!empty(githubOwnerId) && !empty(githubRepoId)) {
parent: managedIdentity
name: 'github-actions-env-${environmentName}'
properties: {
issuer: gitHubIssuer
subject: 'repo:${githubOrg}/${githubRepo}:environment:${environmentName}'
subject: 'repository_owner_id:${githubOwnerId}:repository_id:${githubRepoId}:environment:${environmentName}'
audiences: ficAudiences
}
}

// Federated Identity Credential — GitHub Actions main branch (build-and-push job)
// Subject: repo:<org>/<repo>:ref:refs/heads/main (only granted on staging identity)
resource ficMainBranch 'Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials@2023-01-31' = if (addMainBranchFic && !empty(githubOrg) && !empty(githubRepo)) {
// Subject uses org-level customised OIDC claim template:
// repository_owner_id:<owner_id>:repository_id:<repo_id>:ref:refs/heads/main
resource ficMainBranch 'Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials@2023-01-31' = if (addMainBranchFic && !empty(githubOwnerId) && !empty(githubRepoId)) {
parent: managedIdentity
name: 'github-actions-main-branch'
properties: {
issuer: gitHubIssuer
subject: 'repo:${githubOrg}/${githubRepo}:ref:refs/heads/main'
subject: 'repository_owner_id:${githubOwnerId}:repository_id:${githubRepoId}:ref:refs/heads/main'
audiences: ficAudiences
}
}
Expand Down
4 changes: 2 additions & 2 deletions specs/020-azure-nightly-publish/contracts/infrastructure.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ File: `infra/main.parameters.nightly.json`
"entraClientId": { "value": "${ENTRA_CLIENT_ID}" },
"entraTenantId": { "value": "${ENTRA_TENANT_ID}" },
"entraTenantDomain": { "value": "${ENTRA_TENANT_DOMAIN}" },
"githubOrg": { "value": "${GITHUB_ORG}" },
"githubRepo": { "value": "${GITHUB_REPO}" },
"githubOwnerId": { "value": "${GITHUB_OWNER_ID}" },
"githubRepoId": { "value": "${GITHUB_REPO_ID}" },
"customDomainHostname": { "value": "" },
"alertEmailAddress": { "value": "" }
}
Expand Down
2 changes: 1 addition & 1 deletion specs/020-azure-nightly-publish/data-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ This feature does not introduce traditional application data entities (database
|----------|-------|-------|
| name | `id-acroyoga-nightly` | Pattern: `id-acroyoga-{env}` |
| federatedCredentials[0].name | `github-actions-env-nightly` | OIDC for deploy job |
| federatedCredentials[0].subject | `repo:{org}/{repo}:environment:nightly` | GitHub environment binding |
| federatedCredentials[0].subject | `repository_owner_id:{owner_id}:repository_id:{repo_id}:environment:nightly` | Org-level customised OIDC subject |
| federatedCredentials[0].issuer | `https://token.actions.githubusercontent.com` | GitHub OIDC issuer |
| federatedCredentials[0].audiences | `['api://AzureADTokenExchange']` | Standard Entra audience |

Expand Down
4 changes: 2 additions & 2 deletions specs/020-azure-nightly-publish/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ az identity federated-credential create \
--identity-name "$IDENTITY_NAME" \
--resource-group "$RG_NIGHTLY" \
--issuer "https://token.actions.githubusercontent.com" \
--subject "repo:${GH_REPO}:environment:nightly" \
--subject "repository_owner_id:${GH_OWNER_ID}:repository_id:${GH_REPO_ID}:environment:nightly" \
--audiences "api://AzureADTokenExchange"

# ── 4. Role assignments ──
Expand Down Expand Up @@ -132,7 +132,7 @@ The `deploy` job runs `az deployment group create` with `infra/main.bicep` + `in

| Symptom | Likely Cause | Fix |
|---------|-------------|-----|
| OIDC login fails | FIC subject mismatch | Verify FIC subject is `repo:microsoft/CommunityManagement-Sample-Spec-Kit:environment:nightly` |
| OIDC login fails | FIC subject mismatch | Verify FIC subject matches org-level OIDC template: `repository_owner_id:<owner_id>:repository_id:<repo_id>:environment:nightly` |
| ACR push denied | Missing AcrPush role | Re-run the `az role assignment create --role AcrPush` command |
| Bicep deploy fails on role assignment | Identity needs Owner, not Contributor | Re-run the `az role assignment create --role Owner` command |
| Container app can't pull image | Missing AcrPull role | Re-run the `az role assignment create --role AcrPull` command |
Expand Down
Loading