LinuxBlog.xyz January 4, 2026 - Patrick Kerwood
Workload Identity Federation is rapidly becoming the modern standard for authenticating workloads. In this post, I will configure the Crossplane AzureAD provider to use a Kubernetes-issued service account token to authenticate to Azure, eliminating the need for long-lived secrets.
More and more software vendors are adopting identity federation because it improves both security and operational simplicity by removing the need for static credentials and secret rotation.
Crossplane is not alone in this shift, projects like Argo CD are also moving in this direction, embracing federated identity as the preferred authentication model for cloud-native workloads.
You will need a Kubernetes cluster with a publicly accessible /.well-known/openid-configuration endpoint so that Azure can retrieve the JSON Web Keys.
I’m using Google Kubernetes Engine, which provides this by default.
If you’re using an on-premises cluster and want to enable a public issuer URL, you can use a tool like k8s-apiserver-oidc-reverse-proxy (opens new window) to securely expose the endpoints. Keep in mind that the URL must exactly match the issuer URL configured for your cluster.
And of course you will also need an Azure tenant where you can create the App Registration.
You can retrieve your cluster’s issuer URL by running the command below. You’ll need this URL when creating the Federated Credential on the App Registration.
kubectl get --raw /.well-known/openid-configuration | jq -r '.issuer'
In the Azure Portal, create an App Registration for Crossplane to use. Assign it the required permissions to manage the Azure resources you intend Crossplane to provision and control.
After creation, navigate to Certificates & secrets, switch to the Federated credentials tab, and click Add credential. In the Federated credential scenario dropdown, select Kubernetes accessing Azure resources, then fill in the required fields.
crossplane-system.azuread-provider.crossplane.See the example screenshot below.

Click Add and that’s it! Your App Registration is now ready to use.
Before deploying the Provider, we need to create and deploy a DeploymentRuntimeConfig to apply some additional configuration to the provider’s deployment.
The serviceAccountTemplate block overrides the default service account name used by the provider.
This name must match the service account specified when creating the Federated Credential on the App Registration.
In the deploymentTemplate block, we add a volume named azure-token.
This volume is defined as a serviceAccountToken and is configured with an audience of api://AzureADTokenExchange.
Within the package-runtime container, the service account token volume is mounted at /var/run/secrets/tokens/,
allowing Crossplane to automatically discover and use it for authentication.
Finally, the service account name azuread-provider is explicitly referenced in the pod template spec.
apiVersion: pkg.crossplane.io/v1beta1
kind: DeploymentRuntimeConfig
metadata:
name: provider-azuread
spec:
serviceAccountTemplate:
metadata:
name: azuread-provider
deploymentTemplate:
spec:
selector: {}
template:
spec:
serviceAccountName: azuread-provider
containers:
- name: package-runtime
volumeMounts:
- name: azure-token
mountPath: "/var/run/secrets/tokens/"
readOnly: true
volumes:
- name: azure-token
projected:
sources:
- serviceAccountToken:
audience: api://AzureADTokenExchange
expirationSeconds: 3600
path: azure-token
Deploy the provider with a reference to the previously created DeploymentRuntimeConfig.
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-azuread
spec:
package: xpkg.upbound.io/upbound/provider-azuread:v2.2.0
runtimeConfigRef:
name: provider-azuread
Then we add the ClusterProviderConfig resources which provides the information needed for Crossplane to know how to
authticate.
Replace tenantID and clientID with your own values. The oidcTokenFilePath property tells Crossplane where to locate the Kubernetes service account token, as specified in the DeploymentRuntimeConfig resource.
apiVersion: azuread.m.upbound.io/v1beta1
kind: ClusterProviderConfig
metadata:
name: azuread
spec:
tenantID: "e3db0bd0-8a61-4eca-afda-5a2e32527b4b"
clientID: "d699cb75-6907-439d-9c77-352c64cf6de4"
oidcTokenFilePath: /var/run/secrets/tokens/azure-token
credentials:
source: OIDCTokenFile
You can now manage Azure Entra ID resources using the Crossplane azuread provider.
Below is an example showing how to create an App Registration with a Federated Credential configured, allowing an application to use the associated Enterprise Application for single sign-on.
apiVersion: applications.azuread.m.upbound.io/v1beta1
kind: Application
metadata:
name: crossplane-example
spec:
forProvider:
displayName: "Crossplane Example"
web:
redirectUris:
- "http://localhost/auth/redirect"
providerConfigRef:
kind: ClusterProviderConfig
name: azuread
---
apiVersion: applications.azuread.m.upbound.io/v1beta1
kind: FederatedIdentityCredential
metadata:
name: crossplane-example
spec:
forProvider:
displayName: crossplane-example
issuer: https://container.googleapis.com/v1/projects/<project-id>/locations/europe-west3/clusters/<cluster-name>
subject: system:serviceaccount:<k8s-namespace>:<k8s-sa-name>
applicationIdRef:
name: crossplane-example
audiences:
- api://AzureADTokenExchange
---
apiVersion: serviceprincipals.azuread.m.upbound.io/v1beta1
kind: Principal
metadata:
name: crossplane-example
spec:
forProvider:
featureTags:
- enterprise: true
useExisting: true
clientIdRef:
name: crossplane-example
providerConfigRef:
kind: ClusterProviderConfig
name: azuread