Azure Active Directory

GitHub actions federated identity with Azure AD

A long-awaited feature for many is the ability to authenticate with Azure in a GitHub workflow. For a long time, this has been an integrated feature in Azure DevOps, and for some time GitHub actions have had the ability to authenticate using Azure Application registrations and secrets. But now, we can use federated credentials which utilize OIDC- This will completely remove the cumbersome task of rotating app secrets.

Configure an Azure Application with federated identity

At my company, I have decided we should look into moving pipelines from Azure DevOps to GitHub. Federated identity is now Generally Available (GA) which means we are allowed to work with a production solution in mind. So how do you set it up? my suggestion is to follow the official guide. Below is a script that will configure an Azure AD application with GitHub authentication enabled. The script assumes you use a branching strategy. However, you can change this to be any of the other entity types, altering the federationSubcject variable.

# creates an appregistration in Azure AD and connects it with a github repo
# use as an example only
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]
$gitHubRepoName,
[Parameter(Mandatory)]
[string]
$teamName,
[Parameter(Mandatory)]
[string]
$branchName = "main"
)
$organization = "myorgltd"
$appRegistrationName = "gh-$teamName-action-sp"
$appRegistrationDescription = "Used by $teamName for enabeling GitHub actions with Azure AD OIDC authentication"
$audience = "api://AzureADTokenExchange"
$ghIssuer = "https://token.actions.githubusercontent.com/"
$federationName = "action-$gitHubRepoName"
$federationSubject = "repo:$($organization)/$($gitHubRepoName):ref:refs/heads/$($branchName)"
# check if app exists
$GhAdApp = $null
$GhAdApp = Get-AzADApplication -DisplayName $appRegistrationName
if ($GhAdApp) {
Write-Output "App registration already exist. Adding new credential for specified repo $gitHubRepoName"
$federatedCred = New-AzADAppFederatedCredential -ApplicationObjectId $GhAdApp.Id `
-Audience $audience -Issuer $ghIssuer -Subject $federationSubject `
-Name $federationName -Description "Used to authenticate pipeline in $gitHubRepoName"
}
# if app does not eist
else {
Write-Output "adding new app registration for $teamName with credentials for $gitHubRepoName"
$GhAdApp = New-AzADApplication -DisplayName $appRegistrationName -Description $appRegistrationDescription
$federatedCred = New-AzADAppFederatedCredential -ApplicationObjectId $GhAdApp.Id `
-Audience $audience -Issuer $ghIssuer -Subject $federationSubject `
-Name $federationName -Description "Used to authenticate pipeline in $gitHubRepoName"
}
$credentialObject = [PSCustomObject]@{
ApplicationName = "$($GhAdApp.DisplayName)"
ApplicationObjectId = "$($GhAdApp.Id)"
}
Write-Output $credentialObject | ConvertTo-Json

Configure repository secrets in GitHub using PowerShell

As you probably understand, I need to automate this entire process. If our developers are moving from DevOps to Github workflows, it has to be as easy as it can be. No one will move unless they see some benefits, and onboarding is just as easy as the current DevOps setup. I am pretty familiar with Azure as an identity platform, especially with app registrations and enterprise apps. So the above script was done quickly. On the other hand, I am not that familiar with GitHub APIs. I know there’s a CLI, but for various reasons, I need to look into the API, more specifically the repository secrets API. What you can see from the first part of the documentation is that you need to encrypt the secret before you make the put request.

Creates or updates an organization secret with an encrypted value. Encrypt your secret using LibSodium

GitHub api docs

Now I was in trouble. Should I write everything in .NET, or is it possible to solve this using PowerShell? For sure I would be better off using PowerShell, at least in the PoC phase. First I tried to load the .NET sodium DLLs in my script, but it did not work, and the approach is not that delightful. Luckily with some search engine work, I found a module that wraps Sodium, created specifically for creating secrets in GitHub. Thanks, Tyler! See my example script below.

# add secrets to repo
import-module PSSodium
$ghToken = "ghp_"
$headers = @{Authorization = "token " + $ghToken}
$secret = "fb4ca569-c690-4391-96d9-928e7a8fd7ff"
$repoName = "myrepo"
$organization = "myorgltd"
Invoke-RestMethod -Method get -Uri "https://api.github.com/repos/$($organization)/$($repoName)/actions/secrets" -Headers $headers
$publicKey = (Invoke-RestMethod -Method get -Uri "https://api.github.com/repos/$($organization)/$($repoName)/actions/secrets/public-key" -Headers $headers)
$encryptedSecret = ConvertTo-SodiumEncryptedString -Text $secret -PublicKey $($publicKey.key)
$secretBody = @"
{
"encrypted_value": "$encryptedSecret",
"key_id": "$($publicKey.key_id)"
}
"@
Invoke-RestMethod -Method Put -Uri "https://api.github.com/repos/$($organization)/$($repoName)/actions/secrets/AZURE_TENANT_ID" -Headers $headers -body $secretBody

What about the pipeline?

Next, you need a working pipeline. Lucky for you the documentation is way better when I first looked at this (before GA), so the example works just fine as it is. Anyway, there are a couple of things to point out. Namely the permissions and the setting for login without a subscription ID.

name: 'Federated identity test'
on:
push:
branches:
main
permissions:
id-token: write
contents: read
jobs:
build-and-publish:
runs-on: ubuntu-latest
steps:
name: checkout
uses: actions/checkout@main
name: azure login
uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
allow-no-subscriptions: true
view raw workflow.yaml hosted with ❤ by GitHub

Summary

I hope this short post on how to configure Azure and GitHub with federated identity helps you, and that it provided some more information than the official documentation does. If anything please reach out. I now have my scripts in place, and can start the full automation and migration from Azure DevOps pipelines.

6 COMMENTS
  • Pink Withney

    Pink Withney Nice post. I learn something totally new and challenging on websites

  • startup talky

    startup talky There is definately a lot to find out about this subject. I like all the points you made

  • youtube to mp3 320

    Your passion for your subject matter shines through in every post. It’s clear that you genuinely care about sharing knowledge and making a positive impact on your readers. Kudos to you!

  • Healthstay

    very informative articles or reviews at this time.

  • Migrate from Azure DevOps to GitHub - what you need to know - adatum

    […] workflow we can add references to our container registries, use organization secrets, use and pre-populate connection to our Azure environment. As I mentioned above with the user accounts. We want both, freedom and […]

  • nimabi

    Thank you very much for sharing, I learned a lot from your article. Very cool. Thanks. nimabi

Comments are closed.