OpenID Connect (OIDC) authentication for GitHub Actions and Azure deployments is a real game-changer in the world of security. It significantly enhances security when deploying to your Azure environment. In this blog post, I’ll explain why to use OIDC, walk through setting up an Azure AD application with federated credentials using Azure CLI, and demonstrate it in action within a GitHub repository.
Why use OIDC?
Let’s face it, managing secrets for cloud deployments can be a real headache. But what if I told you there’s a way to deploy to Azure without storing any long-lived secrets in your GitHub repos? Sounds too good to be true, right? Enter OIDC authentication!
OIDC authentication eliminates the need for storing long-lived cloud secrets in your GitHub repositories. Instead, it uses short-lived tokens, enhancing security and simplifying secret management (No need to remember any secrets or keep them stored securely – awesome!).
OpenID Connect (OIDC) allows your GitHub Actions workflows to access resources in Azure, without needing to store the Azure credentials as long-lived GitHub secrets. – https://docs.github.com
Setting up OIDC Authentication
Now, I know what you’re thinking. “Great, another complicated setup process.” But trust me, it’s not as daunting as it seems. Let’s break it down using Azure CLI:
1. Create a Microsoft Entra application with a service principal
# Create Azure AD application registration
az ad app create --display-name "GitHub-To-Azure-Example" --query appId -o tsv
# Store the app ID in a variable
APP_ID=$(az ad app list --display-name "GitHub-To-Azure-Example" --query [].appId -o tsv)
# Create service principal
az ad sp create --id $APP_ID
2. Configure a federated identity credential on the application
# Add OIDC federation credentials
az ad app federated-credential create \
--id $APP_ID \
--parameters '{
"name": "github-oidc-branch",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:thomast1906/azure-oidc-example:ref:refs/heads/main",
"description": "GitHub Actions OIDC - Branch Workflows (main)",
"audiences": ["api://AzureADTokenExchange"]
The subject
field can be configured in different ways:
- For workflows triggered by pull requests:
repo:
/ :pull-request - For jobs tied to an environment:
repo:
/ :environment:
A quick view in Azure, we can see the service principal and OIDC federation creation above has been successful:
3. Assign appropriate roles to your service principal
# Create role assignment reader for Service Principal
az role assignment create --assignee $APP_ID --role Reader --scope /subscriptions/
In this example, I have provided the service principal with reader access to my Azure Subscription
GitHub Workflow example
Now that we have created the required OIDC setup in Azure, lets look at setting up the GitHub workflow
Add the required secrets
Firstly, lets get the required secret values:
1. Azure Subscription ID:
AZURE_SUBSCRIPTION_ID=$(az account show --query id -o tsv)
2. Azure Tenant ID
AZURE_TENANT_ID=$(az account show --query tenantId -o tsv)
3. Service Principal Client ID
AZURE_CLIENT_ID=$(az ad sp list --display-name "GitHub-To-Azure-Example" --query [].appId -o tsv)
After running the above commands, you can use the variables $AZURE_SUBSCRIPTION_ID, $AZURE_TENANT_ID, and $AZURE_CLIENT_ID to access the respective values
To use these values in your GitHub Actions workflow, you’ll need to add them as secrets in your GitHub repository:
- Go to your GitHub repository
- Navigate to Settings > Secrets and variables > Actions
- Click on “New repository secret”
- Add each secret separately:
- Name:
AZURE_SUBSCRIPTION_ID
, Value: (output of the first command) - Name:
AZURE_TENANT_ID
, Value: (output of the second command) - Name:
AZURE_CLIENT_ID
, Value: (output of the third command)
- Name:
Example workflow
Now that the required GitHub repository secrets have been added, lets add an example workflow:
name: Example OIDC Workflow for Azure
on: [push]
permissions:
id-token: write
contents: read
jobs:
login-to-azure:
runs-on: ubuntu-latest
steps:
- name: 'Az CLI login'
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: 'Run az commands to verify login'
run: |
az account show
az group list
This workflow does the following:
- Sets the necessary permissions for OIDC token generation
- Uses the
azure/login@v2
action to authenticate - Runs some basic Azure CLI commands to verify the connection
Some notes to point out from the workflow:
Permissions
permissions:
id-token: write
contents: read
Crucial for OIDC authentication. It’s telling GitHub that this workflow needs permission to write ID tokens (that’s the OIDC token) and read repository contents. Without these permissions, the OIDC authentication won’t work
Authentication to the service principal (using OIDC)
steps:
- name: 'Az CLI login'
uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
Using the azure/login@v1
action to authenticate with Azure. The cool part? We’re not using any long-lived secrets here. Instead, we’re providing the client ID, tenant ID, and subscription ID as GitHub secrets
OIDC Security Considerations
When implementing OIDC, keep these security best practices in mind:
- Always set conditions for token issuance to prevent unauthorized access
- Regularly review access permissions
- Use the principle of least privilege when assigning roles
Implementing OIDC authentication in GitHub Actions for Azure deployments significantly enhances your security posture. It eliminates the need for long-lived secrets, simplifies credential management, and provides more granular control over authentication and authorisation
Awesome, by following the above, you will have successfully connected to your Azure subscription within a GitHub workflow using OIDC, a more secure and CI/CD pipeline
Have you tried implementing OIDC in your workflows yet? I’d love to hear about your experiences! Drop a comment below and let’s geek out together. And hey, if you found this helpful, why not share it with your dev buddies? After all, sharing is caring in the world of tech!
GitHub repository containing the above example code