Configuring GitHub Actions to Azure authentication with OIDC

Security is one of the top priorities for any company working with cloud. Any intrusion can cost thousands of dollars due to the easiness of compute spin-ups to happen. Which is why we should limit the use of credentials anywhere in our code!

One such risk factor is running your CI/CD pipelines with Azure credentials hardcoded as a GitHub secret. Luckily for us, GitHub added Azure OIDC (OpenID Connect) that allows us to connect GitHub to Azure as a managed identity.

But how do we configure this? Well, that's what I will explain in this article!

Prerequisites

  • Azure CLI Installed
  • Logged in on the Azure CLI (az login)
  • Subscription set (az account set --subscription <YOUR_ID>)

Create an AD Application

First off, we need to configure our GitHub Actions workspace as an Azure AD application. Once it is created, we fetch the `APP_ID`

az ad app create --display-name "GitHub OIDC"
APP_ID=$(az ad app list --display-name "GitHub OIDC" --query '[].appId' -o tsv)

Create a Service Principal for the Application

Once the app exists we configure a service principal for it. This allows us to assign fine-grained permissions to the application.

az ad sp create --id $APP_ID
SP_ID=$(az ad sp list --display-name "GitHub OIDC" --query '[].id' -o tsv)

Now assign it the proper rights

az role assignment create --assignee-object-id $SP_ID --assignee-principal-type ServicePrincipal --role contributor

Configure access from GitHub to Azure

Finally, we configure the created application to create a federation to GitHub on our specific repository with the below:

GH_USERNAME="YOUR_USER"
GH_REPO="YOUR_REPO"
GH_BRANCH=main

az ad app federated-credential create --id $APP_ID --parameters "{\"name\":\"FederatedCredential\", \"issuer\":\"https://token.actions.githubusercontent.com\", \"subject\":\"repo:${GH_USERNAME}/${GH_REPO}:ref:refs/heads/${GH_BRANCH}\", \"description\":\"Allow GitHub Actions to access Azure Resources\", \"audiences\":[\"api://AzureADTokenExchange\"]}"

Configuring GitHub Actions

To run a workflow, we now configure GitHub Secrets to point to the correct Azure subscription:

echo "AZURE_CLIENT_ID: '$APP_ID'"
echo "AZURE_TENANT_ID: '$(az account show --query tenantId -o tsv)'"
echo "AZURE_SUBSCRIPTION_ID: '$(az account show --query id -o tsv)'"

Now in your GitHub workflow, simply configure the Azure Login action to utilize the secrets configured:

name: Run Azure Login with OIDC
on: [push]

permissions:
      id-token: write
      contents: read

jobs: 
  build-and-deploy:
    runs-on: ubuntu-latest
    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 }}
  
      - name: 'Run az commands'
        run: |
          az account show
          az group list
          pwd 

Script

As a shortcut I included the script below that you can easily run:

GH_USERNAME="YOUR_USER"
GH_REPO="YOUR_REPO"
GH_BRANCH=main

# 1. Create an Azure AD Application: 
az ad app create --display-name "GitHub OIDC"

# 2. Get the Azure AD Application ID: 
APP_ID=$(az ad app list --display-name "GitHub OIDC" --query '[].appId' -o tsv)

# 3. Create a Service Principal for the Application: 
az ad sp create --id $APP_ID

# 4. Get the Service Principal Object ID: 
SP_ID=$(az ad sp list --display-name "GitHub OIDC" --query '[].id' -o tsv)

# 5. Assign the Service Principal to the Contributor Role: 
az role assignment create --assignee-object-id $SP_ID --assignee-principal-type ServicePrincipal --role contributor

# 6. Add the federated identity credential to GitHub:
# -> findable under https://entra.microsoft.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade/
# under certificates & secrets -> federated credentials
az ad app federated-credential create --id $APP_ID --parameters "{\"name\":\"FederatedCredential\", \"issuer\":\"https://token.actions.githubusercontent.com\", \"subject\":\"repo:${GH_USERNAME}/${GH_REPO}:ref:refs/heads/${GH_BRANCH}\", \"description\":\"Allow GitHub Actions to access Azure Resources\", \"audiences\":[\"api://AzureADTokenExchange\"]}"

# Set the following in GitHub as secrets
echo "Configure the below as GitHub Secrets"
echo "AZURE_CLIENT_ID: '$APP_ID'"
echo "AZURE_TENANT_ID: '$(az account show --query tenantId -o tsv)'"
echo "AZURE_SUBSCRIPTION_ID: '$(az account show --query id -o tsv)'"