Skip to content

Connect GitHub with Azure using OIDC

Using OIDC to log in to Azure within a GitHub Action offers multiple advantages:

  • It avoids complex secrets, such as:
  • No need for periodic secret renewal
  • Use of managed identity, and gain fine control over role assignment and scope

Summary:

  1. Create a user-assigned managed identity
  2. Assign roles to the user-assigned managed identity
  3. Configure a federated identity credential
  4. Configure the GitHub repository

The aleternative is to create a Service Principal, with a secret, assigning the roles to the Service Principal, and create a secret in GitHub with the following format:

json
{
    "tenantId":  "<ID of the tenant>",
    "subscriptionId": "<ID of the subscription>",
    "clientId": "<ID of the SPN (Application (client) ID)>",
    "clientSecret": "SPN secret" 
}

In the GitHub action, use the creds parameter in the azure/login@v2 step:

yaml
      - name: 'Login via Azure CLI'
        uses: azure/login@v2
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}

User-assigned managed identity

Azure portal

In the Azure Portal, create a user-assigned managed identity

001-ConnectGitHubWithAzureUsingOIDC--CreateUserAssignedManagedIdentity

Set the propreties of the identity:

  • Select or create a group
  • Select the region for the resources
  • Set the name (Azure resource name abbreviations)
  • Click on Next to set the tags, otherwise, click on Review + Create:

002-ConnectGitHubWithAzureUsingOIDC--DefineUserAssignedManagedIdentity

Set the tags, eventually, and click on Next.

Review the information, and click on Create.

003-ConnectGitHubWithAzureUsingOIDC--LaunchUserAssignedManagedIdentityCreation

Azure CLI

Login to Azure:

powershell
az login

Run the following commands:

powershell
az identity create --name <identity name> --resource-group <group name> [--location <location>] [--tags tagname=tagvalue tagname=tagvalue]

az identity list [--resource-group <group name>]

Managed identity roles

To deploy Azure resources, the Managed Identity should be:

  • Contributor
  • [Opion] Role Based Access Control Administrator, if the MI is used to configure roles

The scope may a a resource group, a set of resource groups, a subscription, depending on the orgnization's governance.

Note: for Terraform deployments, the Managed Identity can be assigned the Storage Blob Data Contributor role for the storage account where the Terraform states are stored.

Azure Portal

In the Azure Portal, click on Access Control (IAM) from the object on which the MI's role is applied:

004-ConnectGitHubWithAzureUsingOIDC--OpenAccessControl

Click on Add, and the Add role assignment:

005-ConnectGitHubWithAzureUsingOIDC--OpenAddRoleAssignmentPage

Select the Privileged administrator roles, and the select the role:

  1. Contributor
  2. Role Based Access Control Administrator

Click on Next

006-ConnectGitHubWithAzureUsingOIDC--SelectRole

Click on Managed identity radio button, the click on + Select members. Select the MI, and the click on Next:

007-ConnectGitHubWithAzureUsingOIDC--SelectMembers

Select the condition, if asked. The Allow user to assign all roles except privileged administrator roles Owner, UAA, RBAC is recommanded for the Role Based Access Control Administrator role. Click on Next or Review + assign:

008-ConnectGitHubWithAzureUsingOIDC--SetCondition

Check the information, and click on Review + assign:

009-ConnectGitHubWithAzureUsingOIDC--ReviewAndAssignRole

Azure CLI

Login to Azure:

powershell
az login

Run the following commands:

powershell
az role assignment create --role <role name> --assignee <assignee name>| --assignee-principal-type ServicePrincipal ServicePrincipal --assignee-object-id <assignee object id, principal id> --scope <resource id. e.g.: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MyResourceGroup/providers/Microsoft.Compute/virtualMachines/MyVm> [--condition <condition> --description <description>]

az role assignment list --assignee-object-id --assignee <assignee name>|--assignee-object-id <assignee object id, principal id>

Roles:

  • Contributor
  • Role Based Access Administrator

Configure a federated identity credential

Azure Portal

In the Azure Portal, open the Federated Credential pane from the Settings menu under the Managed Identity object:

010-ConnectGitHubWithAzureUsingOIDC--OpenMIFederatedCredentials

Click on + Add credential:

011-ConnectGitHubWithAzureUsingOIDC--AddMIFederatedCredential

Select GitHub Actions deploying Azure resources scenario.

Set:

  • The GiHub organization name,
  • The GitHub repository name,
  • The enity type, between Environment, Branch, Pull Request or Tag,
  • The name of the entity,
  • The name of the credential

011-ConnectGitHubWithAzureUsingOIDC--ConfigureMIFederatedCredential

Azure CLI

Login to Azure:

powershell
az login

Run the following commands:

powershell
az identity federated-credential create --name <name of the federated credential> --identity-name <identity name> --resource-group <resource group name> --issuer https://token.actions.githubusercontent.com --subject repo:https://github.com/<organization name>/<repository name>:environment:<environment name> --audiences api://AzureADTokenExchange

az identity federated-credential list --identity-name <identity name> --resource-group <resource group name>

Configure the GitHub repository

In the GitHub repository, ensure the entity exists. For the environment, check the repository settings and the environment pane, under the code and automation section.

Create the following secrets in the GitHub repository, eventually within the environment:

  • AZURE_CLIENT_ID: the ID (client ID) of the user-assigned managed identity,
  • AZURE_SUBSCRIPTION_ID: the ID of the subscription
  • AZURE_TENANT_ID: the ID of the tenant

In your GitHub action, add the permissions section at the top, and reference the client-id, subscription-id and tenant-id paramters / secrets in the azure/login@v2 step:

yaml
# ...

permissions:
  id-token: write
  contents: read

# ...

jobs:

# ...

    steps:

      #...
      
      - name: Azure login
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

Sources