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/[email protected]
        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)'"