Azure Setup
One command configures everything you need to run APEX workflows against your Azure environment — Entra ID app registration, OIDC federated credentials, RBAC roles, GitHub secrets, variables, and environments.
Quick Start
Section titled “Quick Start”From inside the dev container:
npm run setupThe wizard prompts for your Azure subscription, management group, and app name, then creates everything automatically. Safe to re-run — it skips completed steps.
Prerequisites
Section titled “Prerequisites”| Requirement | How to Check |
|---|---|
| Azure CLI logged in | az account show |
| GitHub CLI authenticated | gh auth status |
| jq installed | jq --version |
| Permission to create Entra app registrations | Ask your Azure AD admin |
| Permission to assign RBAC roles | Owner or User Access Administrator |
What Gets Created
Section titled “What Gets Created”The wizard creates these resources across Azure and GitHub:
Azure Resources
Section titled “Azure Resources”| Resource | Details |
|---|---|
| Entra ID App Registration | apex-github-oidc-{repo-name} |
| Service Principal | Linked to the app registration |
Federated Credential: github-main | Subject: repo:{owner}/{repo}:ref:refs/heads/main |
Federated Credential: github-env-dev | Subject: repo:{owner}/{repo}:environment:dev |
Federated Credential: github-env-staging | Subject: repo:{owner}/{repo}:environment:staging |
Federated Credential: github-env-prod | Subject: repo:{owner}/{repo}:environment:prod |
| RBAC: Reader | At Management Group scope (governance reads) |
| RBAC: Contributor | At Subscription scope (deployments) |
GitHub Resources
Section titled “GitHub Resources”| Resource | Details |
|---|---|
Secret: AZURE_CLIENT_ID | Entra app client ID |
Secret: AZURE_TENANT_ID | Azure AD tenant ID |
Secret: AZURE_SUBSCRIPTION_ID | Target subscription ID |
Variable: GOVERNANCE_BASELINE_ENABLED | true (kill switch) |
Variable: GOVERNANCE_MG_ID | Management Group to scan |
Variable: GOVERNANCE_MAX_SUBSCRIPTIONS | Max subscriptions (default: 100) |
Environment: dev | For development deployments |
Environment: staging | For staging deployments |
Environment: prod | For production deployments |
| GitHub Pages | Enabled with Actions source |
| Auto-merge | Enabled on repository |
Architecture
Section titled “Architecture”GitHub Actions workflows authenticate to Azure using OpenID Connect (OIDC) — no client secrets to rotate.
sequenceDiagram
participant GH as GitHub Actions
participant OIDC as GitHub OIDC Provider
participant Entra as Microsoft Entra ID
participant ARM as Azure Resource Manager
GH->>OIDC: Request OIDC token
OIDC->>GH: JWT with repo/branch/env claims
GH->>Entra: Exchange JWT for Azure token
Note over Entra: Validates issuer, subject,<br/>audience against federated credential
Entra->>GH: Azure access token
GH->>ARM: API calls with Azure token
ARM->>GH: Resource data
Why OIDC? No secrets to store or rotate. The federated credential binds a specific GitHub repo + branch/environment to the Azure service principal. Tokens are short-lived and scoped.
Headless Mode
Section titled “Headless Mode”For CI automation or scripted provisioning, pass --non-interactive and
set environment variables:
export AZURE_TENANT_ID="00000000-0000-0000-0000-000000000000"export AZURE_SUBSCRIPTION_ID="00000000-0000-0000-0000-000000000000"export GOVERNANCE_MG_ID="mg-contoso-root"export GOVERNANCE_MAX_SUBSCRIPTIONS="100"export APP_DISPLAY_NAME="apex-github-oidc-my-project"export DEPLOY_ENVIRONMENTS="dev,staging,prod"
npm run setup -- --non-interactiveAll variables are required in headless mode. The wizard exits with an error if any are missing.
Manual Setup
Section titled “Manual Setup”If you cannot run the wizard (for example, a different admin must create the Entra app), follow these steps manually.
1. Create App Registration
Section titled “1. Create App Registration”# Create the appaz ad app create --display-name "apex-github-oidc-my-project"
# Note the appId from the output, then create the service principalaz ad sp create --id <APP_ID>2. Add Federated Credentials
Section titled “2. Add Federated Credentials”APP_ID="<your-app-id>"REPO="owner/repo"
# Main branch (for scheduled workflows like governance baseline)az ad app federated-credential create --id "$APP_ID" --parameters '{ "name": "github-main", "issuer": "https://token.actions.githubusercontent.com", "subject": "repo:'"$REPO"':ref:refs/heads/main", "audiences": ["api://AzureADTokenExchange"]}'
# Repeat for each environment (dev, staging, prod)for ENV in dev staging prod; do az ad app federated-credential create --id "$APP_ID" --parameters '{ "name": "github-env-'"$ENV"'", "issuer": "https://token.actions.githubusercontent.com", "subject": "repo:'"$REPO"':environment:'"$ENV"'", "audiences": ["api://AzureADTokenExchange"] }'done3. Assign RBAC Roles
Section titled “3. Assign RBAC Roles”SP_OBJECT_ID=$(az ad sp show --id "$APP_ID" --query id -o tsv)
# Reader at Management Group (for governance policy reads)az role assignment create \ --assignee-object-id "$SP_OBJECT_ID" \ --assignee-principal-type ServicePrincipal \ --role "Reader" \ --scope "/providers/Microsoft.Management/managementGroups/<MG_ID>"
# Contributor at Subscription (for deployments)az role assignment create \ --assignee-object-id "$SP_OBJECT_ID" \ --assignee-principal-type ServicePrincipal \ --role "Contributor" \ --scope "/subscriptions/<SUBSCRIPTION_ID>"4. Configure GitHub Repository
Section titled “4. Configure GitHub Repository”# Secretsgh secret set AZURE_CLIENT_ID --body "$APP_ID"gh secret set AZURE_TENANT_ID --body "<TENANT_ID>"gh secret set AZURE_SUBSCRIPTION_ID --body "<SUBSCRIPTION_ID>"
# Variablesgh variable set GOVERNANCE_BASELINE_ENABLED --body "true"gh variable set GOVERNANCE_MG_ID --body "<MG_ID>"gh variable set GOVERNANCE_MAX_SUBSCRIPTIONS --body "100"
# Environmentsfor ENV in dev staging prod; do gh api "repos/<OWNER>/<REPO>/environments/$ENV" --method PUTdoneTroubleshooting
Section titled “Troubleshooting””Not logged in to Azure CLI”
Section titled “”Not logged in to Azure CLI””Run az login --use-device-code inside the dev container.
”Cannot list Management Groups”
Section titled “”Cannot list Management Groups””Your account needs the Management Group Reader role at the tenant root level, or at least at the target MG. Ask your Azure admin.
”Could not create environment”
Section titled “”Could not create environment””GitHub environment creation requires admin access to the repository. If you are a collaborator, ask the repo owner to create the environments or run the wizard from their account.
”Federated credential subject mismatch”
Section titled “”Federated credential subject mismatch””The scheduled governance workflow runs on main with no environment
context. Its OIDC subject is repo:{owner}/{repo}:ref:refs/heads/main.
If you see authentication failures, verify this exact subject exists on
the federated credential.
Re-running the wizard
Section titled “Re-running the wizard”The wizard is idempotent. State files in .azure/.setup-state/ track
completed phases. To start fresh:
npm run setup -- --resetnpm run setupCleanup
Section titled “Cleanup”To completely remove all resources created by the wizard:
APP_ID=$(az ad app list --display-name "apex-github-oidc-my-project" \ --query '[0].appId' -o tsv)SP_OBJECT_ID=$(az ad sp show --id "$APP_ID" --query id -o tsv)
# Remove RBAC assignmentsaz role assignment delete --assignee "$SP_OBJECT_ID" --role "Reader" \ --scope "/providers/Microsoft.Management/managementGroups/<MG_ID>"az role assignment delete --assignee "$SP_OBJECT_ID" --role "Contributor" \ --scope "/subscriptions/<SUBSCRIPTION_ID>"
# Delete the app registration (also deletes SP and federated credentials)az ad app delete --id "$APP_ID"
# Remove GitHub secrets and variablesgh secret delete AZURE_CLIENT_IDgh secret delete AZURE_TENANT_IDgh secret delete AZURE_SUBSCRIPTION_IDgh variable delete GOVERNANCE_BASELINE_ENABLEDgh variable delete GOVERNANCE_MG_IDgh variable delete GOVERNANCE_MAX_SUBSCRIPTIONS