microsoft / microsoft/aspire

[API Proposal] Make workload-identity federation reachable from a custom compute environment

Open
#19,833 1 comment 0 reactions 0 assignees View on GitHub
area-deployment area-integrations triage:bot-seen
Dominant language
C#
Stars
6.3k
Forks
991
Avg merge
2d 15h
Merged PRs (30d)
196

Description

## 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/`:

```csharp
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:

1. It runs only inside `ConfigureAksInfrastructure` (`AzureKubernetesEnvironmentExtensions.cs:510`),
a `private static` method wired exclusively as
`AzureKubernetesEnvironmentResource`'s infrastructure callback.
2. `aksResource.WorkloadIdentities` is `internal`
(`AzureKubernetesEnvironmentResource.cs:153` — `internal Dictionary`).

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

```diff
namespace Aspire.Hosting;

public static class AzureKubernetesEnvironmentExtensions
{
+ ///
+ /// Emits a federated identity credential linking to the
+ /// Kubernetes service account system:serviceaccount:{namespace}:{serviceAccount}.
+ ///
+ ///
+ /// 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.
+ ///
+ public static IResourceBuilder WithKubernetesServiceAccountFederation(
+ this IResourceBuilder identity,
+ BicepValue 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

```csharp
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.

Contributor guide

Open the contributing guide

Research direction

Start with AzureKubernetesEnvironmentExtensions.cs:510 and :835-862, then inspect AzureKubernetesEnvironmentResource.cs:35, :47, and :153 to understand the existing AKS-only path and internal workload-identity state. Compare the proposed identity extension with the public KubernetesServiceCustomizationAnnotation; done means the API design is agreed, custom compute environments can reach equivalent federation emission, and existing AKS behavior remains intact.

Written by the indexing model from the issue text.

Assessment

Tech stack
azure, csharp, kubernetes
Domain
api, cloud, infrastructure
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.