Azure Bicep Terraform

Get rid of Client Secrets with OIDC on Github + Bicep & TF

Get rid of Client Secrets with OIDC on Github + Bicep & TF

Authentication to Azure from GitHub has required that you stored sensitive, time constrained secrets in GitHub. I am of course talking about the ClientSecret you get from creating an Azure AD App Registration.

You can now do authentication with a method called OpenID Connect. This can help authentication in several ways:

  • You do not have to store sensitive clientSecret.
  • You do not have to regenerate your clientSecret.
  • Your clientSecret does not expire and cause outages.

When first hearing about OIDC authentication I assumed it would be complex and difficult to set up. Configuring SSO/Federation/authentication has not always been easy.

Imagine my surprise when I managed to configure this within without much hassle at all!

This is the high level task list:

  1. Create an app registration
  2. Create a GitHub repository (if you don’t already have one)
  3. Create a federated identity
  4. Create GitHub secrets for Azure information
  5. Create a simple workflow to test deployment

These steps are more or less the same if you are using ARM, Terraform(soon), or Bicep. I will show you a simple deployment to Azure with Bicep in this post.

Prereqs OIDC

  • Create an Azure AD App Registration.
  • Add federated credentials for the Azure Active Directory application.
  • Create GitHub secrets for storing Azure configuration.

Create Azure AD App Registration

Run this powershell snippet to create app registration, service principal, and assign contributor role on subscription:

$context = get-azcontext $appName = ‘oidc-demo’ $subscriptionId = $context.subscription.id $appId = (az ad app create –display-name $appName | convertFrom-json).appId $objectId = (az ad sp create –id $appId | convertfrom-json).objectId az role assignment create –role contributor ` –subscription $subscriptionId ` –assignee-object-id  $objectId ` –assignee-principal-type ServicePrincipal ` –scope /subscriptions/$subscriptionId Write-Host “These are the “secrets” you need to add to GitHub`n———————————————–`nApplication Id (ClientId/AppId/AAD_APP_ID): $appId`nTenant Id (Directory Id/AAD_TENANT_ID): $($context.tenant.id)`nSubscription Id (AZURE_SUBSCRIPTION_ID): $subscriptionId`n———————————————–”

Please be aware that this service principal will be granted contributor permissions on your entire subscription. If this is not wanted, you can update the role assignment command above before executing.

Also, there is no client secret to note and put into a GitHub secret.

Add federated credentials

Navigate to your app registration’s Certificates & secrets

Choose _Federated credentials and Add credential

_

Choose GitHub Actions deploying Azure resources

Add credential for your branch in your repository and organisation. This info must be correct, as it is used for token issuing. If you enter the exact information from below image, your authentication will not work.

Authentication from a Pull Request did not work with this federated identity alone, so I needed to add Pull Request entity type also:

Create GitHub secrets

You need to create some GitHub secrets for holding App Registration and Azure information. If you change these names, you need to update the deployment workflow accordingly.

  • AAD_APP_ID – Will be the app id/client id from script above
  • AAD_TENANT_ID – The Azure AD tenant Id from script above
  • AZURE_SUBSCRIPTION_ID – Subscription Id from script above

GitHub Workflow

Assuming you have performed the steps correctly, you should now be able to run the workflow for deploying a resource group and a storage account with Bicep, authenticated without client secret stored in GitHub! 🥳

There are a few changes from how you would do it with regular authentication. Previously you would store clientId, clientSecret, subscriptionId, and tenantId in a json secret called AZURE_CREDENTIALS, and use this in the GitHub login action. This is not necessary with OIDC, and the action will instead request a token to use for temporary authentication.

The job or workflow run requires a permissions setting with id-token: write. You won’t be able to request the OIDC JWT ID token if the permissions setting for id-token is set to read or none.

The id-token: write setting allows the JWT to be requested from GitHub’s OIDC provider

permissions: id-token: write contents: read

This is the login action, and it is not supplied with any client secret for authentication.

- name: ‘Az CLI login’ uses: azure/login@v1 with: client-id: ${{ secrets.AAD_APP_ID }} tenant-id: ${{ secrets.AAD_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} enable-AzPSSession: true

This is your OIDC login verification (yours will be somewhat different):

Deployments

Terraform

Terraform has just recently started supporting OpenID Connect as authentication method (since v1.2.0 and AzureRM Provider 3.7.0). This requires some changes to your terraform backend configuration. Terraform OIDC documentation here. I opted for using CLI input backend-config, to avoid putting info in code, but to each their own.

You need to update your existing workflows to support OIDC. 

Note these settings in main.tf:

Bicep

Bicep does not care how you authenticate, as long as you are authenticated. This means that only your workflows need updating.

Success!

Congratulations! You have now authenticated with OpenID Connect from GitHub Actions to Azure. Your first step towards a future free of client secrets 😎

You’ll find a workflow for Bicep deployment here, and one for Terraform deployment here.

Please don’t hesitate to give me a comment if you find something not working or an obvious mistake!