1/**
2 * Internal composite: a Kubernetes service account (bound onto the cluster)
3 * wired to EKS Pod Identity through an IAM role + `PodIdentityAssociation`.
4 * Un-exported — the workload platforms (`Kubernetes.Deployment`,
5 * `Kubernetes.Job`) provision the same triple through the aws-eks
6 * adapter; this helper remains for composition-style stacks that assemble
7 * the pieces from resources.
8 */
9import * as Effect from "effect/Effect";
10import type { Input } from "../../../Input.ts";
11import * as Namespace from "../../../Namespace.ts";
12import type { PolicyDocument } from "../../IAM/Policy.ts";
13import {
14 Role,
15 type RoleArn,
16 type Role as RoleResource,
17} from "../../IAM/Role.ts";
18import type { Cluster } from "../Cluster.ts";
19import {
20 PodIdentityAssociation,
21 type PodIdentityAssociation as PodIdentityAssociationResource,
22} from "../PodIdentityAssociation.ts";
23import {
24 clusterServiceAccount,
25 namespaceNameOf,
26 type ClusterObjectRef,
27} from "./ClusterObject.ts";
28
29export interface PodIdentityServiceAccountProps {
30 /** Target EKS cluster. */
31 cluster: Cluster;
32 /** Namespace name or namespace helper result. */
33 namespace: string | { name: string } | ClusterObjectRef;
34 /** Optional explicit service account name. Defaults to the logical id. */
35 serviceAccountName?: string;
36 /** Existing IAM role ARN to use for pod identity. */
37 roleArn?: string;
38 /** Optional role name when Alchemy creates the IAM role. */
39 roleName?: string;
40 /** Managed policy ARNs to attach when creating the IAM role. */
41 managedPolicyArns?: string[];
42 /** Inline policies to attach when creating the IAM role. */
43 inlinePolicies?: Record<string, PolicyDocument>;
44 /** Optional role description when Alchemy creates the IAM role. */
45 description?: string;
46 /** Disable session tags for the pod identity association. */
47 disableSessionTags?: boolean;
48 /** Optional target role ARN for chained role assumption. */
49 targetRoleArn?: string;
50 /** Optional inline session policy JSON. */
51 policy?: string;
52 /** Labels applied to the Kubernetes service account. */
53 labels?: Record<string, string>;
54 /** Annotations applied to the Kubernetes service account. */
55 annotations?: Record<string, string>;
56 /** Tags applied to AWS resources. */
57 tags?: Record<string, string>;
58}
59
60export interface PodIdentityServiceAccountResources {
61 /** Reference to the created Kubernetes service account. */
62 serviceAccount: ClusterObjectRef;
63 /** The Pod Identity association binding the role to the service account. */
64 podIdentityAssociation: PodIdentityAssociationResource;
65 /** The generated IAM role, or `undefined` when `roleArn` was supplied. */
66 role: RoleResource | undefined;
67 /** The ARN of the IAM role pods assume (generated or supplied). */
68 roleArn: Input<string> | RoleArn;
69}
70
71export const PodIdentityServiceAccount = (
72 id: string,
73 props: PodIdentityServiceAccountProps,
74) =>
75 Namespace.push(
76 id,
77 Effect.gen(function* () {
78 const serviceAccountName = props.serviceAccountName ?? id;
79
80 const role = props.roleArn
81 ? undefined
82 : yield* Role("Role", {
83 roleName: props.roleName,
84 assumeRolePolicyDocument: {
85 Version: "2012-10-17",
86 Statement: [
87 {
88 Effect: "Allow",
89 Principal: {
90 Service: "pods.eks.amazonaws.com",
91 },
92 Action: ["sts:AssumeRole", "sts:TagSession"],
93 },
94 ],
95 },
96 description:
97 props.description ??
98 `Pod identity role for service account ${serviceAccountName}.`,
99 managedPolicyArns: props.managedPolicyArns,
100 inlinePolicies: props.inlinePolicies,
101 tags: props.tags,
102 });
103
104 const serviceAccount = yield* clusterServiceAccount("ServiceAccount", {
105 cluster: props.cluster,
106 namespace: props.namespace,
107 name: serviceAccountName,
108 labels: props.labels,
109 annotations: props.annotations,
110 });
111
112 const podIdentityAssociation = yield* PodIdentityAssociation(
113 "PodIdentityAssociation",
114 {
115 clusterName: props.cluster.clusterName,
116 namespace: namespaceNameOf(props.namespace),
117 serviceAccount: serviceAccount.name,
118 roleArn: props.roleArn ?? role!.roleArn,
119 disableSessionTags: props.disableSessionTags,
120 targetRoleArn: props.targetRoleArn,
121 policy: props.policy,
122 tags: props.tags,
123 },
124 );
125
126 return {
127 serviceAccount,
128 podIdentityAssociation,
129 role,
130 roleArn: props.roleArn ?? role!.roleArn,
131 } satisfies PodIdentityServiceAccountResources;
132 }),
133 );