Announcing OIDC Support for Pulumi Azure Providers
Posted on
We are happy to announce the delivery of Azure OIDC authentication, one of the most requested features for the Pulumi Azure Native Provider. With the v1.100.0 release, OpenID Connect (OIDC) authentication is now fully supported in both the Azure Native and Azure Classic providers. Let’s dig in to learn what it is, how it works, and why it’s useful.
The What and Why of OIDC
OIDC stands for OpenID Connect, a standardized protocol for federated identity. This means that if you maintain an identity with two service providers, you can tell one of them to trust the other. Then, you can use credentials for the trusted service to get credentials for the other.
As an example, we’ll use a Pulumi program running in CI on GitHub Workflows, creating Azure resources. Without OIDC, this program would need a secret to authenticate with Azure - a client secret or a certificate. With OIDC, your program doesn’t need a secret. It can send its GitHub token to Azure instead. Based on the trust relationship between the two, Azure will exchange the GitHub token for an Azure token.
Secrets need to be safeguarded, rotated, and possibly revoked. If you’ve ever managed a system, whether production or CI/CD, the possibility of managing fewer secrets will be enticing. So how do we go about implementing this?
Enabling OIDC
There are two parts to enabling your Pulumi program to authenticate via OIDC. One is establishing the trust relationship between Azure and the other service that’s involved, like GitHub. This is a one-time operation. The second one is providing your program with the necessary configuration to perform the OIDC token exchange at runtime.
Fortunately, Microsoft has the first step covered with their establishing the trust relationship guide. The idea is that an Active Directory app registration holds the necessary federated credentials. Your program will then use this app’s tenant id to request the token exchange. The setup depends on whether you run on GitHub Actions, Kubernetes, or other providers.
On to the second step, configuring your Pulumi program. First, tell the Pulumi provider to use OIDC by setting the environment variable ARM_USE_OIDC
or the Pulumi configuration azure-native:useOidc
to true. Next, you’re in luck if your program runs on GitHub Actions: you’re done. GitHub exports the necessary values in form of variables that Pulumi understands.
For other providers, you need to provide your Pulumi program with two more settings. Environment variable ARM_OIDC_REQUEST_TOKEN
or configuration azure-native:oidcRequestToken
is your provider’s token to exchange for an Azure token. ARM_OIDC_REQUEST_URL
or azure-native:oidcRequestUrl
is the URL to contact to initiate the token exchange.
A complete example
Let’s take a look at how the CI end-to-end tests of the Pulumi Azure Native provider are set up.
Configuration
First, we have an Active Directory App creatively named oidc-test. We simply copy its client and tenant ids and set them in the CI workflow, along with our subscription:
env:
ARM_CLIENT_ID: 11223344-****-****-****-************ [redacted]
ARM_SUBSCRIPTION_ID: 55667788-****-****-****-************ [redacted]
ARM_TENANT_ID: 99aabbcc-****-****-****-************ [redacted]
ARM_USE_OIDC=true
Note how there is no secret or certificate present.
We also give the required permissions to the workflow step where the authentication happens:
permissions:
id-token: write # required for OIDC auth
In Azure, we added the required credentials to the Active Directory App. You’ll find this under “Certificates & secrets” -> “Federated credentials” on the app’s page. Let’s look at two of the credentials. Both enable OIDC for all pull requests on the repository, one for each Azure provider.
End to End Flow
Now, when our GitHub workflow for CI tests runs, the Azure provider knows to authenticate via OIDC because ARM_USE_OIDC
is set. It performs the following steps:
- Call GitHub’s OIDC provider, found at the URL that GitHub automatically exports as
ACTIONS_ID_TOKEN_REQUEST_URL
. For authentication, it includes a token GitHub generates for each workflow run and exports asACTIONS_ID_TOKEN_REQUEST_TOKEN
. The response is an OIDC token in form of a JSON Web Token (JWT). - Send the OIDC token to Azure’s Active Directory endpoint. Include the client and tenant ids of our Active Directory App that we configured via
ARM_CLIENT_ID
andARM_TENANT_ID
. Active Directory looks up the trust relationship we configured and validates the token, and responds with a valid access token for Azure. - The Pulumi provider can now access Azure resources.
That’s it! Your CI job now runs without maintaining an Azure secret in GitHub, or wherever your Pulumi program runs.
Wrapping up
This article only demonstrated the basics. You can also customize your OIDC token’s claims to control access to Azure in a more fine-grained manner.
I hope this helps you simplify your Azure authentication. Please try it out and let us know if you hit any issues in the Pulumi Community Slack!