Setting Up Microsoft Entra for a Federated Service Account

This guide shows you how to create a Microsoft Entra workload identity for a federated service account, allow it to request the audience Axual expects, and find the object id your tenant admin binds it to.

Type

How-to guide

Goal

Prepare an Entra workload identity that a federated service account authenticates as, and collect the values Axual needs.

Audience

A Microsoft Entra administrator, or an engineer who owns the Azure workload that will call the Platform Manager API.

When to use

Use this guide before a tenant admin creates a federated service account, once your Axual operator has prepared the realm.

This is the Azure side of federated authentication. The tenant admin creates the service account in Federated Service Accounts, and an operator prepares the realm in How to Prepare a Keycloak Realm for Federated Service Accounts.

Prerequisites

Federated authentication trusts a token that your Entra workload presents, so the workload and the audience it targets must exist in your Entra tenant first.

Access and permissions
Tools and versions
  • The Azure portal or the az CLI, to create the identity.

  • curl, to verify the token request.

Resources that must exist first
  • The audience the realm accepts. A tenant admin sees it as the Audience on the create service account form, and, for Entra, the Resource of the form api://<AUDIENCE>. See Service Account and Federation Reference.

Values in angle brackets, such as <AUDIENCE>, are placeholders. Replace each with your own value.

Create the workload identity

The workload identity is what your automation runs as. Choose one kind: an app registration with a secret, or a secretless identity (an AKS workload identity or an Azure managed identity) that holds no secret and is the safer choice for a high-privilege account. Each gives the workload an object id, which the tenant admin binds the service account to.

Use an app registration with a client secret

This is the simplest option. The workload holds a secret and presents it to Entra.

  1. In Entra, register an application for the workload and note its object id (the service principal object id).

  2. Add a client secret to the application and store it in your workload’s secret store.

The workload authenticates to Entra with its client id and secret, and the token’s sub and oid are the service principal object id. Prefer a secretless option below for a high-privilege service account.

Use AKS workload identity (secretless)

On Azure Kubernetes Service (AKS), a pod authenticates with a projected Kubernetes token instead of a secret. This needs the cluster’s OpenID Connect (OIDC) issuer and the workload-identity webhook, which az aks update -g <RESOURCE_GROUP> -n <CLUSTER> --enable-oidc-issuer --enable-workload-identity enable.

  1. On the workload’s Entra application, add a federated credential for the AKS scenario: the cluster’s OIDC issuer URL, the pod’s namespace, and its service-account name. Entra builds the subject system:serviceaccount:<AKS_NAMESPACE>:<AKS_SERVICE_ACCOUNT> with audience api://AzureADTokenExchange.

  2. Annotate the Kubernetes service account with the application’s client id, and label the pod so the webhook injects the token:

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: <AKS_SERVICE_ACCOUNT>
      namespace: <AKS_NAMESPACE>
      annotations:
        azure.workload.identity/client-id: <WORKLOAD_CLIENT_ID>
    ---
    # on the Pod (or the Deployment pod template):
    metadata:
      labels:
        azure.workload.identity/use: "true"
    spec:
      serviceAccountName: <AKS_SERVICE_ACCOUNT>

    At pod creation the webhook injects AZURE_CLIENT_ID, AZURE_TENANT_ID, and AZURE_AUTHORITY_HOST, and mounts the projected token at AZURE_FEDERATED_TOKEN_FILE. The workload reads that file as its assertion, which the Azure SDK’s WorkloadIdentityCredential does for you. The webhook runs once, at admission; it mints no tokens itself.

The workload’s object id is the service principal object id of the application the federated credential is attached to.

Use an Azure managed identity (secretless)

A user-assigned managed identity holds no secret and needs no federated credential of its own: the identity requests a token for the audience directly, from the Azure Instance Metadata Service (IMDS) or through the Azure SDK. The token has the same shape as the other options, with sub and oid equal to the managed identity’s principal object id.

  1. Assign the managed identity to the workload, for example a virtual machine or an App Service.

  2. Note the managed identity’s principal object id.

Azure hands out cached managed-identity tokens that are already minutes old, so the realm’s fedClientAssertionMaxExp must be raised for this path, unlike the AKS path, which mints a fresh token per call. Your operator sets this; see How to Prepare a Keycloak Realm for Federated Service Accounts.

Allow the workload to request the audience

The audience is a resource application in your tenant, separate from the workload (the caller): the resource’s application (client) ID is the <AUDIENCE> the realm accepts, and the caller requests a token for it. Entra only issues that token when the resource resolves and the caller is allowed to request it, and the token must be a v2 token so its aud claim is the bare application GUID.

  1. On the resource application, expose the audience as an Application ID URI of the form api://<AUDIENCE>, so the caller can request the resource api://<AUDIENCE>. The bare GUID form <AUDIENCE>/.default also works.

  2. Configure the resource application to issue v2 access tokens, so aud is the bare GUID and the issuer ends in /v2.0.

  3. If the resource application requires assignment, define an app role on it (allowed member type Applications, enabled), grant that role to the caller, and grant admin consent. Without this, the token request is refused with AADSTS501051.

The <AUDIENCE> must match the client id your operator set on the realm identity provider. Confirm it against the Audience shown on the create service account form before you begin.

Give the object id to your tenant admin

The service account binds to exactly one workload, by its object id.

  1. Copy the workload’s object id from its Entra application or managed identity.

  2. Give it to the tenant admin to enter as the Subject (sub) when they create the federated service account in Federated Service Accounts.

Verify with a token request

Confirm the workload can obtain a token for the audience before the tenant admin relies on it.

curl -s -X POST "https://login.microsoftonline.com/<ENTRA_TENANT_ID>/oauth2/v2.0/token" \
 --data grant_type=client_credentials \
 --data client_id=<WORKLOAD_CLIENT_ID> \
 --data client_secret=<CLIENT_SECRET> \
 --data scope=<AUDIENCE>/.default

To confirm the token is usable, decode it and check that aud is the bare audience GUID, iss ends in /v2.0, and sub (equal to oid) matches the object id you gave the tenant admin. The consumer then presents this token to Axual as described in Authenticating as a Service Account.

For an AKS workload, the webhook has already set the environment in the pod (example values):

AZURE_AUTHORITY_HOST=https://login.microsoftonline.com/
AZURE_TENANT_ID=<ENTRA_TENANT_ID>
AZURE_CLIENT_ID=<WORKLOAD_CLIENT_ID>
AZURE_FEDERATED_TOKEN_FILE=/var/run/secrets/azure/tokens/azure-identity-token

The projected token file is the assertion, so the request carries no secret:

curl -s -X POST "${AZURE_AUTHORITY_HOST}${AZURE_TENANT_ID}/oauth2/v2.0/token" \
 --data grant_type=client_credentials \
 --data client_id="$AZURE_CLIENT_ID" \
 --data scope=<AUDIENCE>/.default \
 --data client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
 --data-urlencode "client_assertion=$(cat "$AZURE_FEDERATED_TOKEN_FILE")"

A real application uses the Azure SDK’s WorkloadIdentityCredential rather than this raw call. The resulting token has the same shape as the client-secret token, so everything downstream is identical.

Common errors

These errors surface while obtaining the Entra token, before Axual is involved.

Error Cause

AADSTS700016

The tenant in the token URL is wrong. Use the tenant that holds the workload.

AADSTS500011

The requested resource does not resolve. Expose api://<AUDIENCE> on the resource application, or request the bare GUID <AUDIENCE>/.default.

AADSTS501051

The caller lacks its app-role assignment on a resource application that requires assignment. Grant the role and admin consent.

AADSTS70021

AKS only: no matching federated identity record. The credential’s subject, issuer, or audience does not match the pod’s projected token.