diff --git a/packages/@aws-cdk/aws-eks/README.md b/packages/@aws-cdk/aws-eks/README.md index e1d78b774450d..493168fa2689a 100644 --- a/packages/@aws-cdk/aws-eks/README.md +++ b/packages/@aws-cdk/aws-eks/README.md @@ -537,6 +537,8 @@ Breaking this down, it means that if the endpoint exposes private access (via `E If the endpoint does not expose private access (via `EndpointAccess.PUBLIC`) **or** the VPC does not contain private subnets, the function will not be provisioned within the VPC. +If your use-case requires control over the IAM role that the KubeCtl Handler assumes, a custom role can be passed through the ClusterProps (as `kubectlLambdaRole`) of the EKS Cluster construct. + #### Cluster Handler The `ClusterHandler` is a set of Lambda functions (`onEventHandler`, `isCompleteHandler`) responsible for interacting with the EKS API in order to control the cluster lifecycle. To provision these functions inside the VPC, set the `placeClusterHandlerInVpc` property to `true`. This will place the functions inside the private subnets of the VPC based on the selection strategy specified in the [`vpcSubnets`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-eks.Cluster.html#vpcsubnetsspan-classapi-icon-api-icon-experimental-titlethis-api-element-is-experimental-it-may-change-without-noticespan) property. diff --git a/packages/@aws-cdk/aws-eks/lib/cluster.ts b/packages/@aws-cdk/aws-eks/lib/cluster.ts index 2c762ab666327..3f78399b6a8e8 100644 --- a/packages/@aws-cdk/aws-eks/lib/cluster.ts +++ b/packages/@aws-cdk/aws-eks/lib/cluster.ts @@ -117,6 +117,15 @@ export interface ICluster extends IResource, ec2.IConnectable { */ readonly kubectlPrivateSubnets?: ec2.ISubnet[]; + /** + * An IAM role that can perform kubectl operations against this cluster. + * + * The role should be mapped to the `system:masters` Kubernetes RBAC role. + * + * This role is directly passed to the lambda handler that sends Kube Ctl commands to the cluster. + */ + readonly kubectlLambdaRole?: iam.IRole; + /** * An AWS Lambda layer that includes `kubectl`, `helm` and the `aws` CLI. * @@ -271,6 +280,18 @@ export interface ClusterAttributes { */ readonly kubectlRoleArn?: string; + /** + * An IAM role that can perform kubectl operations against this cluster. + * + * The role should be mapped to the `system:masters` Kubernetes RBAC role. + * + * This role is directly passed to the lambda handler that sends Kube Ctl commands + * to the cluster. + * @default - if not specified, the default role created by a lambda function will + * be used. + */ + readonly kubectlLambdaRole?: iam.IRole; + /** * Environment variables to use when running `kubectl` against this cluster. * @default - no additional variables @@ -702,6 +723,14 @@ export interface ClusterProps extends ClusterOptions { * @default NODEGROUP */ readonly defaultCapacityType?: DefaultCapacityType; + + + /** + * The IAM role to pass to the Kubectl Lambda Handler. + * + * @default - Default Lambda IAM Execution Role + */ + readonly kubectlLambdaRole?: iam.IRole; } /** @@ -771,6 +800,7 @@ abstract class ClusterBase extends Resource implements ICluster { public abstract readonly clusterSecurityGroup: ec2.ISecurityGroup; public abstract readonly clusterEncryptionConfigKeyArn: string; public abstract readonly kubectlRole?: iam.IRole; + public abstract readonly kubectlLambdaRole?: iam.IRole; public abstract readonly kubectlEnvironment?: { [key: string]: string }; public abstract readonly kubectlSecurityGroup?: ec2.ISecurityGroup; public abstract readonly kubectlPrivateSubnets?: ec2.ISubnet[]; @@ -1077,6 +1107,18 @@ export class Cluster extends ClusterBase { */ public readonly kubectlRole?: iam.IRole; + /** + * An IAM role that can perform kubectl operations against this cluster. + * + * The role should be mapped to the `system:masters` Kubernetes RBAC role. + * + * This role is directly passed to the lambda handler that sends Kube Ctl commands to the cluster. + * @default - if not specified, the default role created by a lambda function will + * be used. + */ + + public readonly kubectlLambdaRole?: iam.IRole; + /** * Custom environment variables when running `kubectl` against this cluster. */ @@ -1195,6 +1237,7 @@ export class Cluster extends ClusterBase { this.prune = props.prune ?? true; this.vpc = props.vpc || new ec2.Vpc(this, 'DefaultVpc'); this.version = props.version; + this.kubectlLambdaRole = props.kubectlLambdaRole ? props.kubectlLambdaRole : undefined; this.tagSubnets(); @@ -1867,6 +1910,7 @@ class ImportedCluster extends ClusterBase { public readonly clusterArn: string; public readonly connections = new ec2.Connections(); public readonly kubectlRole?: iam.IRole; + public readonly kubectlLambdaRole?: iam.IRole; public readonly kubectlEnvironment?: { [key: string]: string; } | undefined; public readonly kubectlSecurityGroup?: ec2.ISecurityGroup | undefined; public readonly kubectlPrivateSubnets?: ec2.ISubnet[] | undefined; diff --git a/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts b/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts index b5bd8ed51b876..0e5db3c6a51e3 100644 --- a/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts +++ b/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts @@ -77,6 +77,7 @@ export class KubectlProvider extends NestedStack { description: 'onEvent handler for EKS kubectl resource provider', memorySize, environment: cluster.kubectlEnvironment, + role: cluster.kubectlLambdaRole ? cluster.kubectlLambdaRole : undefined, // defined only when using private access vpc: cluster.kubectlPrivateSubnets ? cluster.vpc : undefined, diff --git a/packages/@aws-cdk/aws-eks/test/cluster.test.ts b/packages/@aws-cdk/aws-eks/test/cluster.test.ts index 63a17d9c5d64d..b8b9c91042f32 100644 --- a/packages/@aws-cdk/aws-eks/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-eks/test/cluster.test.ts @@ -2202,6 +2202,42 @@ describe('cluster', () => { }, }); + }); + + test('kubectl provider passes iam role environment to kube ctl lambda', () => { + + const { stack } = testFixture(); + + const kubectlRole = new iam.Role(stack, 'KubectlIamRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), + }); + + // using _ syntax to silence warning about _cluster not being used, when it is + const cluster = new eks.Cluster(stack, 'Cluster1', { + version: CLUSTER_VERSION, + prune: false, + endpointAccess: eks.EndpointAccess.PRIVATE, + kubectlLambdaRole: kubectlRole, + }); + + cluster.addManifest('resource', { + kind: 'ConfigMap', + apiVersion: 'v1', + data: { + hello: 'world', + }, + metadata: { + name: 'config-map', + }, + }); + + // the kubectl provider is inside a nested stack. + const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; + expect(nested).toHaveResourceLike('AWS::Lambda::Function', { + Role: { + Ref: 'referencetoStackKubectlIamRole02F8947EArn', + }, + }); });