Kubernetes service accounts with IAM role binding (IRSA) providing secure AWS service access from pods through IAM roles for service accounts.
Kubernetes service account with associated IAM role for secure AWS API access.
/**
* Kubernetes service account with IAM role (IRSA)
*/
class ServiceAccount extends Construct implements iam.IPrincipal {
constructor(scope: Construct, id: string, props: ServiceAccountProps);
/** Associated IAM role */
readonly role: iam.IRole;
/** Service account name */
readonly serviceAccountName: string;
/** Service account namespace */
readonly serviceAccountNamespace: string;
/** Assume role action */
readonly assumeRoleAction: string;
/** Grant principal */
readonly grantPrincipal: iam.IPrincipal;
/** Principal ARN */
readonly principalArn: string;
/** Add policy statement to the service account's IAM role */
addToPrincipalPolicy(statement: iam.PolicyStatement): iam.AddToPrincipalPolicyResult;
/** @deprecated Use addToPrincipalPolicy instead */
addToPolicy(statement: iam.PolicyStatement): boolean;
}
/**
* Service account properties
*/
interface ServiceAccountProps extends ServiceAccountOptions {
/** EKS cluster */
readonly cluster: ICluster;
}
/**
* Service account configuration options
*/
interface ServiceAccountOptions {
/** Service account name */
readonly name?: string;
/** Kubernetes namespace */
readonly namespace?: string;
/** Service account annotations */
readonly annotations?: { [key: string]: string };
/** Service account labels */
readonly labels?: { [key: string]: string };
/** IAM role to associate */
readonly role?: iam.IRole;
}Usage Examples:
import * as eks from "@aws-cdk/aws-eks";
import * as iam from "@aws-cdk/aws-iam";
import * as s3 from "@aws-cdk/aws-s3";
// Basic service account
const serviceAccount = cluster.addServiceAccount("MyServiceAccount", {
name: "my-service-account",
namespace: "default",
});
// Grant S3 access to the service account
const bucket = new s3.Bucket(this, "MyBucket");
bucket.grantReadWrite(serviceAccount);
// Service account with custom IAM role
const customRole = new iam.Role(this, "CustomRole", {
assumedBy: new iam.WebIdentityPrincipal(
cluster.openIdConnectProvider.openIdConnectProviderArn,
{
"StringEquals": {
[`${cluster.clusterOpenIdConnectIssuer}:sub`]: "system:serviceaccount:default:custom-sa",
[`${cluster.clusterOpenIdConnectIssuer}:aud`]: "sts.amazonaws.com",
},
}
),
});
const customServiceAccount = cluster.addServiceAccount("CustomServiceAccount", {
name: "custom-sa",
namespace: "default",
role: customRole,
});
// Service account with specific permissions
const s3ServiceAccount = cluster.addServiceAccount("S3ServiceAccount", {
name: "s3-reader",
namespace: "app",
labels: {
"app": "data-processor",
"component": "reader",
},
});
s3ServiceAccount.addToPrincipalPolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
"s3:GetObject",
"s3:ListBucket",
],
resources: [
"arn:aws:s3:::my-data-bucket",
"arn:aws:s3:::my-data-bucket/*",
],
}));To use the service account in a pod:
apiVersion: v1
kind: Pod
metadata:
name: aws-cli-pod
namespace: default
spec:
serviceAccountName: my-service-account # Links to the created service account
containers:
- name: aws-cli
image: amazon/aws-cli:latest
command: ["aws", "s3", "ls"] # Will use the service account's IAM roleIRSA provides the following benefits:
IRSA Architecture:
/**
* IRSA trust relationship configuration
*/
interface IRSATrustPolicy {
/** OIDC provider ARN */
readonly openIdConnectProviderArn: string;
/** Service account subject */
readonly subject: string;
/** OIDC audience */
readonly audience: string;
}The service account is automatically annotated with the IAM role ARN:
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-service-account
namespace: default
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/MyServiceAccountRole// Example: Service account for different environments
const prodServiceAccount = cluster.addServiceAccount("ProdServiceAccount", {
name: "app-service-account",
namespace: "production",
labels: {
"environment": "production",
"app": "web-api",
},
});
const stagingServiceAccount = cluster.addServiceAccount("StagingServiceAccount", {
name: "app-service-account",
namespace: "staging",
labels: {
"environment": "staging",
"app": "web-api",
},
});
// Grant different permissions based on environment
prodBucket.grantRead(prodServiceAccount);
stagingBucket.grantReadWrite(stagingServiceAccount);