Background and Motivation
Aspire.Hosting.Azure.Kubernetes already implements workload-identity federation end to end — but
only on a path a custom compute environment cannot reach.
AzureKubernetesEnvironmentExtensions.cs:835-862 emits the credential — and this is the only
occurrence of FederatedIdentityCredential anywhere in src/:
foreach (var (resourceName, identityResource) in aksResource.WorkloadIdentities)
{
var existingIdentity = UserAssignedIdentity.FromExisting($"identity_{sanitizedName}");
existingIdentity.Name = identityNameParam;
infrastructure.Add(existingIdentity);
var fedCred = new FederatedIdentityCredential($"fedcred_{sanitizedName}")
{
Parent = existingIdentity,
Name = $"{resourceName}-fedcred",
IssuerUri = /* ...aks... oidcIssuerProfile.issuerURL */,
Subject = $"system:serviceaccount:{k8sNamespace}:{saName}",
Audiences = { "api://AzureADTokenExchange" }
};
infrastructure.Add(fedCred);
}
and the matching runtime wiring (annotated ServiceAccountV1, podSpec.ServiceAccountName, the
azure.workload.identity/use label) happens in the AKS environment's prepare-aks-{name} pipeline
step — registered at AzureKubernetesEnvironmentResource.cs:47 and documented at :35 as applying
"node-pool/workload-identity annotations to compute".
Two things lock this away from anyone else:
- It runs only inside
ConfigureAksInfrastructure (AzureKubernetesEnvironmentExtensions.cs:510),
a private static method wired exclusively as
AzureKubernetesEnvironmentResource's infrastructure callback.
aksResource.WorkloadIdentities is internal
(AzureKubernetesEnvironmentResource.cs:153 — internal Dictionary<string, IAppIdentityResource>).
A compute environment that is not the built-in AKS one therefore never runs prepare-aks, and its
generated chart has no ServiceAccount, no workload-identity pod label, and no federated
credential — even though every type involved (FederatedIdentityCredential,
KubernetesServiceCustomizationAnnotation, AppIdentityAnnotation) is public.
Is this the right ask? (division of responsibility)
Yes, with one deliberate boundary. The credential federates an identity the AppHost declares
to a service account the AppHost's own chart creates — both sides are the app's, so this is
app-model work, not platform work.
The boundary: federating a platform-owned identity (say, a shared cluster identity) is a
platform onboarding operation and is explicitly not what this asks for. We do not do that, and an
upstream API should not encourage it either.
We are also not asking Aspire to invent anything — only to make an existing, working emission
reachable.
Proposed API
namespace Aspire.Hosting;
public static class AzureKubernetesEnvironmentExtensions
{
+ /// <summary>
+ /// Emits a federated identity credential linking <paramref name="identity"/> to the
+ /// Kubernetes service account <c>system:serviceaccount:{namespace}:{serviceAccount}</c>.
+ /// </summary>
+ /// <remarks>
+ /// The OIDC issuer is supplied as a value rather than read from a provisioned cluster, so this
+ /// works for a pre-existing cluster and for compute environments that provision no cluster.
+ /// </remarks>
+ public static IResourceBuilder<AzureUserAssignedIdentityResource> WithKubernetesServiceAccountFederation(
+ this IResourceBuilder<AzureUserAssignedIdentityResource> identity,
+ BicepValue<string> oidcIssuerUrl,
+ string kubernetesNamespace,
+ string serviceAccountName);
}
Taking oidcIssuerUrl as a value is the load-bearing detail: a custom compute environment
provisions no cluster template to read oidcIssuerProfile.issuerURL from.
Usage Examples
var identity = builder.AddAzureUserAssignedIdentity("workload-identity");
identity.WithKubernetesServiceAccountFederation(
oidcIssuerUrl: clusterIssuerUrl, // from a parameter or platform metadata
kubernetesNamespace: "my-namespace",
serviceAccountName: "my-workload");
The chart-side half (ServiceAccount + pod label) is already achievable today through the public
KubernetesServiceCustomizationAnnotation, so this one addition completes the scenario.
Alternative Designs
- Make
WorkloadIdentities public and extract the emission into a public static helper. Equally
good, and arguably tidier — it would let the AKS environment and third parties share one code
path rather than two.
- Replicate it downstream. What we do today; it is ~40 lines of stock
Azure.Provisioning, but
it means the federation subject and the service-account name are owned in two different places
and can drift.
Risks
Low — additive, and it emits the same stock FederatedIdentityCredential the AKS environment
already emits. The one judgement call is accepting the issuer as a value, which is strictly more
permissive than reading it from a provisioned cluster.
Background and Motivation
Aspire.Hosting.Azure.Kubernetesalready implements workload-identity federation end to end — butonly on a path a custom compute environment cannot reach.
AzureKubernetesEnvironmentExtensions.cs:835-862emits the credential — and this is the onlyoccurrence of
FederatedIdentityCredentialanywhere insrc/:and the matching runtime wiring (annotated
ServiceAccountV1,podSpec.ServiceAccountName, theazure.workload.identity/uselabel) happens in the AKS environment'sprepare-aks-{name}pipelinestep — registered at
AzureKubernetesEnvironmentResource.cs:47and documented at:35as applying"node-pool/workload-identity annotations to compute".
Two things lock this away from anyone else:
ConfigureAksInfrastructure(AzureKubernetesEnvironmentExtensions.cs:510),a
private staticmethod wired exclusively asAzureKubernetesEnvironmentResource's infrastructure callback.aksResource.WorkloadIdentitiesisinternal(
AzureKubernetesEnvironmentResource.cs:153—internal Dictionary<string, IAppIdentityResource>).A compute environment that is not the built-in AKS one therefore never runs
prepare-aks, and itsgenerated chart has no ServiceAccount, no workload-identity pod label, and no federated
credential — even though every type involved (
FederatedIdentityCredential,KubernetesServiceCustomizationAnnotation,AppIdentityAnnotation) is public.Is this the right ask? (division of responsibility)
Yes, with one deliberate boundary. The credential federates an identity the AppHost declares
to a service account the AppHost's own chart creates — both sides are the app's, so this is
app-model work, not platform work.
The boundary: federating a platform-owned identity (say, a shared cluster identity) is a
platform onboarding operation and is explicitly not what this asks for. We do not do that, and an
upstream API should not encourage it either.
We are also not asking Aspire to invent anything — only to make an existing, working emission
reachable.
Proposed API
namespace Aspire.Hosting; public static class AzureKubernetesEnvironmentExtensions { + /// <summary> + /// Emits a federated identity credential linking <paramref name="identity"/> to the + /// Kubernetes service account <c>system:serviceaccount:{namespace}:{serviceAccount}</c>. + /// </summary> + /// <remarks> + /// The OIDC issuer is supplied as a value rather than read from a provisioned cluster, so this + /// works for a pre-existing cluster and for compute environments that provision no cluster. + /// </remarks> + public static IResourceBuilder<AzureUserAssignedIdentityResource> WithKubernetesServiceAccountFederation( + this IResourceBuilder<AzureUserAssignedIdentityResource> identity, + BicepValue<string> oidcIssuerUrl, + string kubernetesNamespace, + string serviceAccountName); }Taking
oidcIssuerUrlas a value is the load-bearing detail: a custom compute environmentprovisions no cluster template to read
oidcIssuerProfile.issuerURLfrom.Usage Examples
The chart-side half (ServiceAccount + pod label) is already achievable today through the public
KubernetesServiceCustomizationAnnotation, so this one addition completes the scenario.Alternative Designs
WorkloadIdentitiespublic and extract the emission into a public static helper. Equallygood, and arguably tidier — it would let the AKS environment and third parties share one code
path rather than two.
Azure.Provisioning, butit means the federation subject and the service-account name are owned in two different places
and can drift.
Risks
Low — additive, and it emits the same stock
FederatedIdentityCredentialthe AKS environmentalready emits. The one judgement call is accepting the issuer as a value, which is strictly more
permissive than reading it from a provisioned cluster.