Connect GitHub Actions to Azure using OpenID Connect
Tags: azure, github, openid, devops
Published: 2023-11-10 01:09:08

Using Open ID Connect to connect GitHub Actions to Azure is the new recommended method. This is an updated guide for connecting GitHub Actions to Azure using OpenID Connect, detailing script-based Azure AD app and service principal creation, role assignment, and federated credentials setup for GitHub integration.

### My Environment

I am running this on macOS 14.0, and the following prerequisites are needed to execute the script snippets in this article.

- PowerShell 7.2.4
- Azure CLI 2.53.0
- GitHub CLI 2.38.0

## Step 1: Create an Application Registration in Microsoft Entra ID

***NOTE: Replace the values in the script with your own and be sure you have proper permission to create app registrations/service principals and edit them in Azure AD.***

The App Registration object id will be used to in the request to create federated credentials for GitHub Actions, this part is required.

```powershell
$AppName = "<Name of App Registration>"

$APP_JSON = az ad app create `
    --display-name "${AppName}" `

$APP_DATA = $APP_JSON | ConvertFrom-Json
$APP_ID = $APP_DATA.appId
$APP_OBJECT_ID = $APP_DATA.id
```

## Step 2 (Optional): Create a Service Principal for the Application Registration

While you don't have to create a service principal for the application registration, it is recommended. This will allow you to assign a role to the service principal and limit the scope of the permissions. The service principal object id will be used in the role assignment request in the following step.

```powershell
$SP_JSON = az ad sp create `
    --id $APP_ID

$SP_DATA = $SP_JSON | ConvertFrom-Json
$SP_ASSIGNEE_OBJECT_ID = $SP_DATA.id
```

## Step 3 (Optional): Assign a Role to the Service Principal

This step is optional but recommended. This will allow you to assign a role to the service principal and limit the scope of the permissions. The following assumes you're currently logged into the Azure CLI and have the correct subscription set.

***NOTE: Replace the values in the script with your own and be sure you have proper permission to create app registrations/service principals and edit them in Microsoft Entra ID. Change the Role Array to match your needs.***

```powershell
$SUBSCRIPTION_ID = az account show --query id --output tsv # Returns the current subscription ID

# Create an array of roles with their respective scopes
$RoleArray = @(
    [PSCustomObject]@{Role="Contributor"; Scope="/subscriptions/${SUBSCRIPTION_ID}"},
    [PSCustomObject]@{Role="User Access Administrator"; Scope="/subscriptions/${SUBSCRIPTION_ID}"},
)

# Loop over the array and print the role and scope of each object
foreach ($obj in $objectArray) {
    Write-Host "Role: $($obj.Role), Scope: $($obj.Scope)"
    az role assignment create `
        --role $obj.Role `
        --subscription $SUBSCRIPTION_ID `
        --assignee-object-id  $SP_ASSIGNEE_OBJECT_ID `
        --assignee-principal-type ServicePrincipal `
        --scope $obj.Scope
}
```


## Step 4: Add federated credentials for GitHub Actions

This step is required. The following assumes you're currently logged into the Azure CLI and have the correct subscription set. The following will create a federated credential for the App Registration that will be used to connect to Azure from GitHub Actions.

***NOTE: Replace the variable values in the script with your own.***

```powershell
$CREDENTIAL_NAME = $AppName
$GITHUB_ORG = "<Name of GitHub Org or GitHub Username>"
$GITHUB_REPO = "<Name of GitHub Repo>"
$GITHUB_REPO_BRANCH = "<Name of GitHub Repo Branch>"

$REQUEST = @{
    name = $CREDENTIAL_NAME
    issuer = "https://token.actions.githubusercontent.com"
    subject = "repo:${GITHUB_ORG}/${GITHUB_REPO}:ref:refs/heads/${GITHUB_REPO_BRANCH}"
    description = "Federate GitHub Action"
    audiences = @("api://AzureADTokenExchange")
}

$REQUEST_JSON = ($REQUEST | ConvertTo-Json -Compress) -replace '"', '\"'

az rest `
    --headers "Content-Type=application/json" `
    --method POST `
    --uri "https://graph.microsoft.com/beta/applications/${APP_OBJECT_ID}/federatedIdentityCredentials" `
    --body $REQUEST_JSON
```

## Step 5: Add secrets to GitHub repo via GitHub CLI

Once the federated credentials have been created, you will need to add the following secrets to your GitHub repo. This script will assign the required values from the app registration to the to the GitHub repository through the GitHub CLI. The following assumes you're currently logged into the GitHub CLI. 

***NOTE: The names of the secrets to not have to be exactly as written below, but they do need to match the GitHub Action Workflow in the next step.***

```powershell
$AZURE_CLIENT_ID = az ad app show `
    --id $APP_OBJECT_ID `
    --query appId `
    --output tsv

$AZURE_TENANT_ID = az account show `
    --query "tenantId" `
    --output tsv

# $SUBSCRIPTION_ID = az account show ` # Uncomment if not set in optional Step 3
#     --query "id" `
#     --output tsv

$SecretsArray = @(
    [PSCustomObject]@{Name="AZURE_CLIENT_ID"; Value=$AZURE_CLIENT_ID},
    [PSCustomObject]@{Name="AZURE_TENANT_ID"; Value=$AZURE_TENANT_ID},
    [PSCustomObject]@{Name="AZURE_SUBSCRIPTION_ID"; Value=$SUBSCRIPTION_ID}
)

# Loop over the array and print the role and scope of each object
foreach ($secret in $SecretsArray) {
    Write-Host "Name: $($secret.Name), Repo: ${RepoName}"
    gh secret set $secret.name `
        --repo "${Username}/${RepoName}" `
        --body $secret.Value
}
```

## Step 6: Add GitHub Action and test Azure connection

This is an example of a GitHub Actions workflow that will connect to Azure using the OpenID Connect method. You can find details on the permissions required for this action [here](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure#adding-permissions-settings).

The client id, tenant id and subscription id need to be assigned from the secrets in the repository from step 5.

```yaml
name: Azure Login with OpenID Connect

on:
    push:
        branches: [ main ]
  
permissions:
    id-token: write # This is required for requesting the JWT
    contents: read  # This is required for actions/checkout

jobs:
  login:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - 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: Show Subscription
        run: |
          az account show
```
## Thanks for reading

These scripts are a really helpful way initiate a connection from GitHub to Azure. I use this for all my projects and I hope if helps. If you have questions feel free to reach out.