Create App Registrations with Federated Credentials for GitHub Actions
This page describes how to self-service the creation of Azure App Registrations with federated credentials, enabling GitHub Actions workflows to access Azure resources without storing long-lived credentials as secrets.
Overview
Federated credentials allow your GitHub Actions workflows to authenticate to Azure using OpenID Connect (OIDC) without needing to maintain long-lived credentials. This is a more secure approach than storing service principal secrets or connection strings in GitHub.
Prerequisites
- You need write access to the azure-github-federation-config repository
- Familiarity with YAML syntax
- Understanding of your GitHub repository path (
<org>/<repo>) - Knowledge of which Azure scopes (subscriptions/resource groups/resources) your GitHub Actions need to access
- Knowledge of which Azure role you need (e.g.,
Reader,Contributor)
Key Concepts
Federated Credentials vs. Long-lived Secrets
- Traditional approach: Store Azure credentials in GitHub secrets, risk of exposure if compromised
- Federated credentials: GitHub Actions receive a short-lived token that is only valid for specific repository contexts, significantly reducing security risk
Subject Identifiers
A “subject” defines which GitHub repository contexts can use the App Registration. Each subject maps to a specific combination of repository and trigger condition.
Common subject formats:
- repo:<org>/<repo>:ref:refs/heads/<branch> - Workflow running on a specific branch
- repo:<org>/<repo>:pull_request - Workflow running on a pull request
- repo:<org>/<repo>:ref:refs/tags/<tag> - Workflow running on a specific tag
- repo:<org>/<repo>:environment:<environment> - Workflow running in a specific GitHub environment
The 20 Federated Credentials Limit
Azure has a hard limit of 20 federated credentials per App Registration. Each subject line counts as one credential. If you need more than 20 subjects, create multiple App Registrations (e.g., naming them with a numeric suffix like -1, -2, etc.).
Role-Based Access Control (RBAC)
Roles define what permissions the GitHub Actions workflow has. Common roles include:
- Reader - Read-only access to resources
- Contributor - Full management access
- Custom roles - Organization-specific roles for fine-grained control
Scopes
Scopes define where the role is assigned. You can assign roles at multiple levels:
- Management Group: /providers/Microsoft.Management/managementGroups/<group-name>
- Subscription: /subscriptions/<subscription-id>
- Resource Group: /subscriptions/<subscription-id>/resourceGroups/<resource-group-name>
- Specific Resource: /subscriptions/<subscription-id>/resourceGroups/<resource-group-name>/<resource-type>/<resource-name>
Getting Started: Adding a New App Registration
Step 1: Gather Required Information
Before editing the configuration, collect:
1. App Registration Name - A descriptive name for your app (e.g., “My Repository Deployment”)
2. GitHub Subjects - The repository contexts that will use this app:
- Your repository name (<org>/<repo>)
- The branches/tags/environments that will trigger deployments
3. Required Permissions:
- Which Azure roles you need
- Which Azure scopes (subscriptions, resource groups, resources)
4. Optional Owners - User IDs of additional owners (if different from the default deployment service account)
Step 2: Clone the Repository
git clone https://github.com/hmcts/azure-github-federation-config.git
cd azure-github-federation-config
Step 3: Edit the Configuration File
Open app-registrations.yaml in your text editor and add your App Registration configuration.
Simple Example: Read-only access to a subscription
- name: My Repository Reader
subjects:
- 'repo:hmcts/my-repository:ref:refs/heads/main'
- 'repo:hmcts/my-repository:pull_request'
permissions:
- role_definition_name: Reader
scopes:
- /subscriptions/12345678-1234-1234-1234-123456789012
Complex Example: Multi-environment deployment
- name: My Repository Deployer
subjects:
- 'repo:hmcts/my-repository:ref:refs/heads/main'
- 'repo:hmcts/my-repository:ref:refs/heads/develop'
- 'repo:hmcts/my-repository:pull_request'
- 'repo:hmcts/my-repository:environment:production'
permissions:
- role_definition_name: Reader
scopes:
- /subscriptions/12345678-1234-1234-1234-123456789012
- role_definition_name: Contributor
scopes:
- /subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/my-rg
Example with Multiple Subjects at the Limit
- name: My Repository GitHub Actions-1
subjects:
- 'repo:hmcts/my-repository:ref:refs/heads/main'
- 'repo:hmcts/my-repository:ref:refs/heads/develop'
- 'repo:hmcts/my-repository:ref:refs/heads/staging'
- 'repo:hmcts/my-repository:pull_request'
- 'repo:hmcts/my-repository:environment:production'
- 'repo:hmcts/another-repository:ref:refs/heads/main'
- 'repo:hmcts/another-repository:pull_request'
- 'repo:hmcts/third-repository:ref:refs/heads/main'
# ... up to 20 total subjects
permissions:
- role_definition_name: Contributor
scopes:
- /subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/shared-rg
Step 4: Validate Your Configuration
Check the following before committing:
- YAML Syntax - Ensure proper indentation (2 spaces, not tabs)
- Subject Count - Count the lines under
subjects:- must be ≤ 20 - Scopes Format - Verify subscription IDs and resource group names are correct
- Role Names - Ensure role names match available Azure roles in your organization
Step 5: Commit and Push
git add app-registrations.yaml
git commit -m "Add App Registration for my-repository"
git push origin main
Step 6: Create a Pull Request
Navigate to the repository on GitHub and create a pull request. The infrastructure team will review your configuration for: - Correct syntax and format - Appropriate permissions (following principle of least privilege) - Valid Azure scopes - Compliance with organizational policies
Once approved and merged, the terraform pipeline will automatically create your App Registration, service principal, federated credentials, and role assignments.
Using Your App Registration in GitHub Actions
Once your App Registration is created and deployed, use it in your GitHub Actions workflow with the official Azure Login action:
Example GitHub Actions Workflow
name: Deploy to Azure
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
id-token: write # Required for OIDC token generation
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Azure Login
uses: Azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
allow-no-subscriptions: true
OR
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Deploy Application
run: |
# Your deployment commands here
az deployment group create \
--resource-group my-rg \
--template-file template.bicep
Storing Application Details in GitHub Secrets
After your App Registration is created, you’ll need to store three values as GitHub repository secrets:
- AZURE_CLIENT_ID - The Application ID of the App Registration (found in Azure portal under App registrations)
- AZURE_TENANT_ID - Your Azure Tenant ID
- allow-no-subscription: true - Simply allows a workflow to authenticate to Azure without picking a subscription, this is helps when making multiple environment deployments OR
- AZURE_SUBSCRIPTION_ID - Supply the ID of the subscription to limit the access to only that subscription
To add these secrets: 1. Go to your GitHub repository 2. Settings → Secrets and variables → Actions 3. Click “New repository secret” 4. Add each secret with its corresponding value
Modifying an Existing App Registration
Adding More Subjects
If you need to add more repository contexts to an existing App Registration:
- Edit
app-registrations.yaml - Add new subject lines under the
subjects:section - Important: Ensure the total number of subjects does not exceed 20
- Commit and push changes
Changing Permissions
To modify the roles or scopes:
- Edit the
permissions:section inapp-registrations.yaml - Add or remove roles and scopes as needed
- Ensure changes follow the principle of least privilege
- Commit and push changes
When to Create a New App Registration
If your existing App Registration: - Already has 20 subjects and you need to add more - Has overly broad permissions that should be separated - Serves a different purpose or team
Then create a new App Registration following the “Adding a New App Registration” steps above. Use a naming convention like -1, -2, etc. for related registrations.
Troubleshooting
“Authentication failed in Azure Login action”
Cause: The GitHub Actions workflow doesn’t have the id-token: write permission
Solution: Add permissions: { id-token: write } to your workflow
“Insufficient permissions to perform action”
Cause: The role assigned to your App Registration doesn’t have the required permissions
Solution:
1. Verify the role name is correct in app-registrations.yaml
2. Check that the scope includes the resource you’re trying to access
3. Submit a PR to add the required role/scope
“Subject not found in App Registration”
Cause: The subject in your GitHub Actions workflow context doesn’t match any configured subject
Solution:
1. Verify the subject format matches one of your configured subjects
2. Check branch names, environment names, etc. are correct
3. Add the missing subject to app-registrations.yaml
“Cannot add more subjects - limit reached”
Cause: The App Registration already has 20 federated credentials
Solution: Create a new App Registration and update your workflow to use it instead
Common Use Cases
CI/CD Pipeline to Deploy to Development
- name: Dev Deployment App
subjects:
- 'repo:hmcts/my-app:ref:refs/heads/develop'
- 'repo:hmcts/my-app:pull_request'
permissions:
- role_definition_name: Contributor
scopes:
- /subscriptions/dev-sub-id/resourceGroups/dev-rg
Multi-Environment Deployments
- name: Multi-Env Deployer
subjects:
- 'repo:hmcts/my-app:environment:development'
- 'repo:hmcts/my-app:environment:staging'
- 'repo:hmcts/my-app:environment:production'
permissions:
- role_definition_name: Contributor
scopes:
- /subscriptions/dev-sub-id/resourceGroups/dev-rg
- /subscriptions/staging-sub-id/resourceGroups/staging-rg
- /subscriptions/prod-sub-id/resourceGroups/prod-rg
Monitoring/Reporting Across Multiple Subscriptions
- name: Monitoring Reporter
subjects:
- 'repo:hmcts/monitoring-dashboard:ref:refs/heads/main'
permissions:
- role_definition_name: Reader
scopes:
- /providers/Microsoft.Management/managementGroups/Organization-Root
Best Practices
Principle of Least Privilege: Only grant the minimum permissions needed
- Use
Readerinstead ofContributorif you only need to read resources - Assign scopes to specific resource groups, not entire subscriptions
- Use
Use Specific Subjects: Be precise with subject patterns
- Don’t use broad patterns that could match unintended contexts
- Separate production and non-production with different App Registrations
Naming Convention: Use clear, descriptive names
- Include the repository or service name
- Indicate the purpose (e.g., “Deployer”, “Reader”, “Auditor”)
- For multiple related registrations, use numeric suffixes (-1, -2, etc.)
Documentation: Add comments to complex configurations
- Document why specific permissions are needed
- Note any special requirements or constraints
Regular Reviews: Periodically review your App Registrations
- Remove unused subjects and permissions
- Update documentation if requirements change
Related Documentation
- Microsoft: Configuring OpenID Connect in Azure
- Azure: Connect from Azure using GitHub
- Azure: Federated Identity Credentials API
- GitHub Actions: Azure Login Action
Getting Help
If you encounter issues: 1. Check the Troubleshooting section above 2. Review your configuration against the examples 3. Contact the platform team in Slack (#cloud-native-platform) 4. Open an issue in the azure-github-federation-config repository