eks: ServiceAccount (IdentityType.POD_IDENTITY) should accept an existing IAM role via optional `role` prop
- Dominant language
- TypeScript
- Stars
- 12.9k
- Forks
- 4.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 83
Description
### Describe the feature
When using `ServiceAccount` with `IdentityType.POD_IDENTITY`, the CDK automatically
creates an IAM role internally. There is currently no way to pass a pre-existing IAM
role (e.g., created via `iam.Role`, `iam.Role.fromRoleArn()`, or `iam.Role.fromLookup()`)
to be used in the underlying `PodIdentityAssociation`.
### Use Case
### 1. Centralized IAM role management
Projects that consolidate all IAM role definitions in a dedicated construct
(e.g., `IamConstruct`) cannot use `ServiceAccount` with `IdentityType.POD_IDENTITY`,
because the auto-generated role falls outside that construct's control.
This makes it impossible to call `grantRead()` or attach policies in one place.
### 2. Reusing an existing IAM role
When an IAM role already exists (imported via `Role.fromRoleArn()` or
`Role.fromLookup()`), there is no L2 way to associate it with a Pod Identity.
Developers are forced to fall back to the L1 construct `CfnPodIdentityAssociation`.
### Proposed Solution
Add an optional `role` property to `ServiceAccountOptions`:
```typescript
export interface ServiceAccountOptions {
// ... existing props ...
/**
* An existing IAM role to associate with this service account via Pod Identity.
* Only valid when `identityType` is `IdentityType.POD_IDENTITY`.
*
* When specified, the provided role is used instead of auto-generating one.
* When omitted, the current behavior (auto-generating an IAM role) is preserved.
*
* @default - a new IAM role is created automatically
*/
readonly role?: iam.IRole;
}
Example usage:
// IAM role defined and managed separately
const appRole = new iam.Role(this, 'AppRole', {
assumedBy: new iam.SessionTagsPrincipal(
new iam.ServicePrincipal('pods.eks.amazonaws.com'),
),
});
appRole.addManagedPolicy(...);
// Pass the existing role to ServiceAccount
new eks.ServiceAccount(this, 'AppSA', {
cluster: this.cluster,
name: 'app-sa',
namespace: 'production',
identityType: IdentityType.POD_IDENTITY,
role: appRole, // use pre-existing role instead of auto-generating
});
Backward Compatibility
This change is fully backward compatible:
- If role is not specified, behavior remains unchanged (IAM role is auto-generated).
- If role is specified, the provided role is used instead.
- A validation error should be thrown if role is specified when
identityType is not IdentityType.POD_IDENTITY.
Current Workaround
Using the L1 construct directly:
const pia = new CfnPodIdentityAssociation(this, 'AppPIA', {
clusterName: cluster.clusterName,
namespace: 'production',
serviceAccount: 'app-sa',
roleArn: appRole.roleArn,
});
This works but loses the ergonomics and CDK-level dependency management
that the L2 ServiceAccount construct provides.
### Other Information
Related
- #30519 — original Pod Identity L2 support issue (closed)
### Acknowledgements
- [x] I may be able to implement this feature request
- [ ] This feature might incur a breaking change
### AWS CDK Library version (aws-cdk-lib)
2.243.0
### AWS CDK CLI version
2.1111.0 (build 69089a7)
### Environment details (OS name and version, etc.)
Ubuntu 24.04
Contributor guide
Research direction
Start at the ServiceAccount and ServiceAccountOptions entry points, then trace the existing IdentityType.POD_IDENTITY path and its PodIdentityAssociation setup. Done means an optional existing IAM role is accepted for POD_IDENTITY, omitted roles retain current behavior, and non-POD_IDENTITY usage is rejected; add or update focused tests for these cases.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, kubernetes, typescript
- Domain
- cloud, infrastructure
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100