Connect GitHub Actions to Azure using OpenID Connect
Tags: azure, github, oidc, beginners
Published: 2023-01-14 21:14:22

Until recently it was recommended to connect to Azure from a GitHub Action using a service principal, you can find documentation on that [here](https://learn.microsoft.com/en-us/azure/developer/github/connect-from-azure?tabs=azure-portal%2Clinux#use-the-azure-login-action-with-a-service-principal-secret). However, the new way is a connection through OpenID Connect. This did give me some trouble in the beginning but through a little trial and error I was able to get it working with the script below:

## Step 1: Connect to Azure using OpenID Connect

***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.***

```powershell
Param(
    [Parameter(Mandatory = $true)]
    [String]
    $AppName
)

# -------------------------------------------------
# Create an Azure AD application
# -------------------------------------------------

$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

# -------------------------------------------------
# Create a service principal for above app
# -------------------------------------------------

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

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

# -------------------------------------------------
# Assign role to the service principal
# -------------------------------------------------

$ROLE_NAME = "Contributor"
$SUBSCRIPTION_ID = az account show --query id --output tsv # Returns the current subscription ID

az role assignment create `
    --role $ROLE_NAME `
    --subscription $SUBSCRIPTION_ID `
    --assignee-object-id  $SP_ASSIGNEE_OBJECT_ID `
    --assignee-principal-type ServicePrincipal `
    --scope "/subscriptions/${SUBSCRIPTION_ID}"

# -------------------------------------------------
# Add federated credentials for GitHub Actions
# -------------------------------------------------

$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
```

The above script will create the required App Registration and Service Principal in Azure Active Directory with a contributor role assignment. It will then create a federated credential for the App Registration that will be used to connect to Azure from GitHub Actions.

## Step 2: Manually :( add secrets to GitHub repo 

After running the above script, you will need to add the following secrets to your GitHub repo:

|GitHub Secret Name|Azure Active Directory Application|
|---|---|
|AZURE_CLIENT_ID|Application (client) ID|
|AZURE_TENANT_ID|Directory (tenant) ID|
|AZURE_SUBSCRIPTION_ID|Subscription ID|

## Step 3: Connect to Azure from GitHub Actions

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).

```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
```

I hope this saves you some time and frustration. If you have any questions or comments, please feel free to reach out to me.