diff --git a/packages/@aws-cdk/aws-ec2/lib/vpc.ts b/packages/@aws-cdk/aws-ec2/lib/vpc.ts index 8408073e94a1e..daaba4cd8a05f 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpc.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpc.ts @@ -1107,6 +1107,16 @@ export class Vpc extends VpcBase { public readonly internetConnectivityEstablished: IDependable; + /** + * Indicates if instances launched in this VPC will have public DNS hostnames. + */ + public readonly dnsHostnamesEnabled: boolean; + + /** + * Indicates if DNS support is enabled for this VPC. + */ + public readonly dnsSupportEnabled: boolean; + /** * The VPC resource */ @@ -1147,16 +1157,16 @@ export class Vpc extends VpcBase { this.networkBuilder = new NetworkBuilder(cidrBlock); - const enableDnsHostnames = props.enableDnsHostnames == null ? true : props.enableDnsHostnames; - const enableDnsSupport = props.enableDnsSupport == null ? true : props.enableDnsSupport; + this.dnsHostnamesEnabled = props.enableDnsHostnames == null ? true : props.enableDnsHostnames; + this.dnsSupportEnabled = props.enableDnsSupport == null ? true : props.enableDnsSupport; const instanceTenancy = props.defaultInstanceTenancy || 'default'; this.internetConnectivityEstablished = this._internetConnectivityEstablished; // Define a VPC using the provided CIDR range this.resource = new CfnVPC(this, 'Resource', { cidrBlock, - enableDnsHostnames, - enableDnsSupport, + enableDnsHostnames: this.dnsHostnamesEnabled, + enableDnsSupport: this.dnsSupportEnabled, instanceTenancy, }); diff --git a/packages/@aws-cdk/aws-ec2/test/vpc.test.ts b/packages/@aws-cdk/aws-ec2/test/vpc.test.ts index a2e28a6341297..113a8eb7edd56 100644 --- a/packages/@aws-cdk/aws-ec2/test/vpc.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/vpc.test.ts @@ -61,6 +61,46 @@ nodeunitShim({ test.done(); }, + 'dns getters correspond to CFN properties': (() => { + + const tests: any = { }; + + const inputs = [ + {dnsSupport: false, dnsHostnames: false}, + // {dnsSupport: false, dnsHostnames: true} - this configuration is illegal so its not part of the permutations. + {dnsSupport: true, dnsHostnames: false}, + {dnsSupport: true, dnsHostnames: true}, + ]; + + for (const input of inputs) { + + tests[`[dnsSupport=${input.dnsSupport},dnsHostnames=${input.dnsHostnames}]`] = (test: Test) => { + + const stack = getTestStack(); + const vpc = new Vpc(stack, 'TheVPC', { + cidr: '192.168.0.0/16', + enableDnsHostnames: input.dnsHostnames, + enableDnsSupport: input.dnsSupport, + defaultInstanceTenancy: DefaultInstanceTenancy.DEDICATED, + }); + + expect(stack).to(haveResource('AWS::EC2::VPC', { + CidrBlock: '192.168.0.0/16', + EnableDnsHostnames: input.dnsHostnames, + EnableDnsSupport: input.dnsSupport, + InstanceTenancy: DefaultInstanceTenancy.DEDICATED, + })); + + test.equal(input.dnsSupport, vpc.dnsSupportEnabled); + test.equal(input.dnsHostnames, vpc.dnsHostnamesEnabled); + test.done(); + + }; + } + + return tests; + })(), + 'contains the correct number of subnets'(test: Test) { const stack = getTestStack(); const vpc = new Vpc(stack, 'TheVPC'); diff --git a/packages/@aws-cdk/aws-eks/README.md b/packages/@aws-cdk/aws-eks/README.md index ee73d0bedcba5..776f8e41f6879 100644 --- a/packages/@aws-cdk/aws-eks/README.md +++ b/packages/@aws-cdk/aws-eks/README.md @@ -45,6 +45,20 @@ cluster.addResource('mypod', { }); ``` +### Endpoint Access + +You can configure the [cluster endpoint access](https://docs.aws.amazon.com/eks/latest/userguide/cluster-endpoint.html) by using the `endpointAccess` property: + +```typescript +const cluster = new eks.Cluster(this, 'hello-eks', { + version: eks.KubernetesVersion.V1_16, + endpointAccess: eks.EndpointAccess.PRIVATE // No access outside of your VPC. +}); +``` + +The default value is `eks.EndpointAccess.PUBLIC_AND_PRIVATE`. Which means the cluster endpoint is accessible from outside of your VPC, and worker node traffic to the endpoint will stay within your VPC. + + ### Capacity By default, `eks.Cluster` is created with a managed nodegroup with x2 `m5.large` instances. You must specify the kubernetes version for the cluster with the `version` property. @@ -78,7 +92,7 @@ new eks.Cluster(this, 'cluster', { To disable the default capacity, simply set `defaultCapacity` to `0`: ```ts -new eks.Cluster(this, 'cluster-with-no-capacity', { +new eks.Cluster(this, 'cluster-with-no-capacity', { defaultCapacity: 0, version: eks.KubernetesVersion.V1_16, }); @@ -105,8 +119,8 @@ cluster.addCapacity('frontend-nodes', { ### Managed Node Groups -Amazon EKS managed node groups automate the provisioning and lifecycle management of nodes (Amazon EC2 instances) -for Amazon EKS Kubernetes clusters. By default, `eks.Nodegroup` create a nodegroup with x2 `t3.medium` instances. +Amazon EKS managed node groups automate the provisioning and lifecycle management of nodes (Amazon EC2 instances) +for Amazon EKS Kubernetes clusters. By default, `eks.Nodegroup` create a nodegroup with x2 `t3.medium` instances. ```ts new eks.Nodegroup(stack, 'nodegroup', { cluster }); @@ -128,7 +142,7 @@ AWS Fargate is a technology that provides on-demand, right-sized compute capacity for containers. With AWS Fargate, you no longer have to provision, configure, or scale groups of virtual machines to run containers. This removes the need to choose server types, decide when to scale your node groups, or -optimize cluster packing. +optimize cluster packing. You can control which pods start on Fargate and how they run with Fargate Profiles, which are defined as part of your Amazon EKS cluster. @@ -348,6 +362,20 @@ new KubernetesResource(this, 'hello-kub', { cluster.addResource('hello-kub', service, deployment); ``` +##### Kubectl Environment + +The resources are created in the cluster by running `kubectl apply` from a python lambda function. You can configure the environment of this function by specifying it at cluster instantiation. For example, this can useful in order to configure an http proxy: + +```typescript +const cluster = new eks.Cluster(this, 'hello-eks', { + version: eks.KubernetesVersion.V1_16, + kubectlEnvironment: { + 'http_proxy': 'http://proxy.myproxy.com' + } +}); + +``` + #### Adding resources from a URL The following example will deploy the resource manifest hosting on remote server: diff --git a/packages/@aws-cdk/aws-eks/lib/cluster-resource-handler/cluster.ts b/packages/@aws-cdk/aws-eks/lib/cluster-resource-handler/cluster.ts index d88c8900e3020..48c7baa85ac6a 100644 --- a/packages/@aws-cdk/aws-eks/lib/cluster-resource-handler/cluster.ts +++ b/packages/@aws-cdk/aws-eks/lib/cluster-resource-handler/cluster.ts @@ -261,7 +261,22 @@ export class ClusterResourceHandler extends ResourceHandler { } function parseProps(props: any): aws.EKS.CreateClusterRequest { - return props?.Config ?? { }; + + const parsed = props?.Config ?? { }; + + // this is weird but these boolean properties are passed by CFN as a string, and we need them to be booleanic for the SDK. + // Otherwise it fails with 'Unexpected Parameter: params.resourcesVpcConfig.endpointPrivateAccess is expected to be a boolean' + + if (typeof(parsed.resourcesVpcConfig?.endpointPrivateAccess) === 'string') { + parsed.resourcesVpcConfig.endpointPrivateAccess = parsed.resourcesVpcConfig.endpointPrivateAccess === 'true'; + } + + if (typeof(parsed.resourcesVpcConfig?.endpointPublicAccess) === 'string') { + parsed.resourcesVpcConfig.endpointPublicAccess = parsed.resourcesVpcConfig.endpointPublicAccess === 'true'; + } + + return parsed; + } interface UpdateMap { @@ -280,6 +295,9 @@ function analyzeUpdate(oldProps: Partial, newProps const newVpcProps = newProps.resourcesVpcConfig || { }; const oldVpcProps = oldProps.resourcesVpcConfig || { }; + const oldPublicAccessCidrs = new Set(oldVpcProps.publicAccessCidrs ?? []); + const newPublicAccessCidrs = new Set(newVpcProps.publicAccessCidrs ?? []); + return { replaceName: newProps.name !== oldProps.name, replaceVpc: @@ -287,9 +305,14 @@ function analyzeUpdate(oldProps: Partial, newProps JSON.stringify(newVpcProps.securityGroupIds) !== JSON.stringify(oldVpcProps.securityGroupIds), updateAccess: newVpcProps.endpointPrivateAccess !== oldVpcProps.endpointPrivateAccess || - newVpcProps.endpointPublicAccess !== oldVpcProps.endpointPublicAccess, + newVpcProps.endpointPublicAccess !== oldVpcProps.endpointPublicAccess || + !setsEqual(newPublicAccessCidrs, oldPublicAccessCidrs), replaceRole: newProps.roleArn !== oldProps.roleArn, updateVersion: newProps.version !== oldProps.version, updateLogging: JSON.stringify(newProps.logging) !== JSON.stringify(oldProps.logging), }; } + +function setsEqual(first: Set, second: Set) { + return first.size === second.size || [...first].every((e: string) => second.has(e)); +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-eks/lib/cluster-resource.ts b/packages/@aws-cdk/aws-eks/lib/cluster-resource.ts index 13b493808d0b0..c885feaa476d2 100644 --- a/packages/@aws-cdk/aws-eks/lib/cluster-resource.ts +++ b/packages/@aws-cdk/aws-eks/lib/cluster-resource.ts @@ -2,7 +2,26 @@ import * as iam from '@aws-cdk/aws-iam'; import { ArnComponents, Construct, CustomResource, Lazy, Stack, Token } from '@aws-cdk/core'; import { CLUSTER_RESOURCE_TYPE } from './cluster-resource-handler/consts'; import { ClusterResourceProvider } from './cluster-resource-provider'; -import { CfnClusterProps } from './eks.generated'; +import { CfnClusterProps, CfnCluster } from './eks.generated'; + +export interface ClusterResourceProps extends CfnClusterProps { + + /** + * Enable private endpoint access to the cluster. + */ + readonly endpointPrivateAccess: boolean; + + /** + * Enable public endpoint access to the cluster. + */ + readonly endpointPublicAccess: boolean; + + /** + * Limit public address with CIDR blocks. + */ + readonly publicAccessCidrs?: string[]; + +} /** * A low-level CFN resource Amazon EKS cluster implemented through a custom @@ -32,7 +51,7 @@ export class ClusterResource extends Construct { private readonly trustedPrincipals: string[] = []; - constructor(scope: Construct, id: string, props: CfnClusterProps) { + constructor(scope: Construct, id: string, props: ClusterResourceProps) { super(scope, id); const stack = Stack.of(this); @@ -117,7 +136,21 @@ export class ClusterResource extends Construct { resourceType: CLUSTER_RESOURCE_TYPE, serviceToken: provider.serviceToken, properties: { - Config: props, + // the structure of config needs to be that of 'aws.EKS.CreateClusterRequest' since its passed as is + // to the eks.createCluster sdk invocation. + Config: { + name: props.name, + version: props.version, + roleArn: props.roleArn, + encryptionConfig: props.encryptionConfig, + resourcesVpcConfig: { + subnetIds: (props.resourcesVpcConfig as CfnCluster.ResourcesVpcConfigProperty).subnetIds, + securityGroupIds: (props.resourcesVpcConfig as CfnCluster.ResourcesVpcConfigProperty).securityGroupIds, + endpointPublicAccess: props.endpointPublicAccess, + endpointPrivateAccess: props.endpointPrivateAccess, + publicAccessCidrs: props.publicAccessCidrs, + }, + }, AssumeRoleArn: this.creationRole.roleArn, // IMPORTANT: increment this number when you add new attributes to the diff --git a/packages/@aws-cdk/aws-eks/lib/cluster.ts b/packages/@aws-cdk/aws-eks/lib/cluster.ts index 64338e877075b..3ddee2f54434b 100644 --- a/packages/@aws-cdk/aws-eks/lib/cluster.ts +++ b/packages/@aws-cdk/aws-eks/lib/cluster.ts @@ -13,7 +13,7 @@ import { FargateProfile, FargateProfileOptions } from './fargate-profile'; import { HelmChart, HelmChartOptions } from './helm-chart'; import { KubernetesPatch } from './k8s-patch'; import { KubernetesResource } from './k8s-resource'; -import { KubectlProvider } from './kubectl-provider'; +import { KubectlProvider, KubectlProviderProps } from './kubectl-provider'; import { Nodegroup, NodegroupOptions } from './managed-nodegroup'; import { ServiceAccount, ServiceAccountOptions } from './service-account'; import { LifecycleLabel, renderAmazonLinuxUserData, renderBottlerocketUserData } from './user-data'; @@ -120,7 +120,7 @@ export interface ClusterAttributes { */ export interface ClusterOptions { /** - * The VPC in which to create the Cluster + * The VPC in which to create the Cluster. * * @default - a VPC with default configuration will be created and can be accessed through `cluster.vpc`. */ @@ -216,6 +216,112 @@ export interface ClusterOptions { * @default true */ readonly outputConfigCommand?: boolean; + + /** + * Configure access to the Kubernetes API server endpoint. + * This feature is only available for kubectl enabled clusters, i.e `kubectlEnabled: true`. + * + * @see https://docs.aws.amazon.com/eks/latest/userguide/cluster-endpoint.html + * + * @default EndpointAccess.PUBLIC_AND_PRIVATE + */ + readonly endpointAccess?: EndpointAccess; + + /** + * Environment variables for the kubectl execution. Only relevant for kubectl enabled clusters. + * + * @default - No environment variables. + */ + readonly kubectlEnvironment?: { [key: string]: string }; +} + +/** + * Group access configuration together. + */ +interface EndpointAccessConfig { + + /** + * Indicates if private access is enabled. + */ + readonly privateAccess: boolean; + + /** + * Indicates if public access is enabled. + */ + readonly publicAccess: boolean; + /** + * Public access is allowed only from these CIDR blocks. + * An empty array means access is open to any address. + * + * @default - No restrictions. + */ + readonly publicCidrs?: string[]; + +} + +/** + * Endpoint access characteristics. + */ +export class EndpointAccess { + + /** + * The cluster endpoint is accessible from outside of your VPC. + * Worker node traffic will leave your VPC to connect to the endpoint. + * + * By default, the endpoint is exposed to all adresses. You can optionally limit the CIDR blocks that can access the public endpoint using the `PUBLIC.onlyFrom` method. + * If you limit access to specific CIDR blocks, you must ensure that the CIDR blocks that you + * specify include the addresses that worker nodes and Fargate pods (if you use them) + * access the public endpoint from. + * + * @param cidr The CIDR blocks. + */ + public static readonly PUBLIC = new EndpointAccess({privateAccess: false, publicAccess: true}); + + /** + * The cluster endpoint is only accessible through your VPC. + * Worker node traffic to the endpoint will stay within your VPC. + */ + public static readonly PRIVATE = new EndpointAccess({privateAccess: true, publicAccess: false}); + + /** + * The cluster endpoint is accessible from outside of your VPC. + * Worker node traffic to the endpoint will stay within your VPC. + * + * By default, the endpoint is exposed to all adresses. You can optionally limit the CIDR blocks that can access the public endpoint using the `PUBLIC_AND_PRIVATE.onlyFrom` method. + * If you limit access to specific CIDR blocks, you must ensure that the CIDR blocks that you + * specify include the addresses that worker nodes and Fargate pods (if you use them) + * access the public endpoint from. + * + * @param cidr The CIDR blocks. + */ + public static readonly PUBLIC_AND_PRIVATE = new EndpointAccess({privateAccess: true, publicAccess: true}); + + private constructor( + /** + * Configuration properties. + * + * @internal + */ + public readonly _config: EndpointAccessConfig) { + if (!_config.publicAccess && _config.publicCidrs && _config.publicCidrs.length > 0) { + throw new Error('CIDR blocks can only be configured when public access is enabled'); + } + } + + + /** + * Restrict public access to specific CIDR blocks. + * If public access is disabled, this method will result in an error. + * + * @param cidr CIDR blocks. + */ + public onlyFrom(...cidr: string[]) { + return new EndpointAccess({ + ...this._config, + // override CIDR + publicCidrs: cidr, + }); + } } /** @@ -232,15 +338,17 @@ export interface ClusterProps extends ClusterOptions { * - `addRoleMapping` * - `addUserMapping` * - `addMastersRole` and `props.mastersRole` + * - `endpointAccess` * * If this is disabled, the cluster can only be managed by issuing `kubectl` * commands from a session that uses the IAM role/user that created the * account. * - * _NOTE_: changing this value will destoy the cluster. This is because a + * _NOTE_: changing this value will destroy the cluster. This is because a * managable cluster must be created using an AWS CloudFormation custom * resource which executes with an IAM role owned by the CDK app. * + * * @default true The cluster can be managed by the AWS CDK application. */ readonly kubectlEnabled?: boolean; @@ -383,6 +491,7 @@ export class Cluster extends Resource implements ICluster { /** * Indicates if `kubectl` related operations can be performed on this cluster. + * */ public readonly kubectlEnabled: boolean; @@ -424,6 +533,14 @@ export class Cluster extends Resource implements ICluster { private _neuronDevicePlugin?: KubernetesResource; + private readonly endpointAccess?: EndpointAccess; + + private readonly kubctlProviderSecurityGroup?: ec2.ISecurityGroup; + + private readonly vpcSubnets: ec2.SubnetSelection[]; + + private readonly kubectlProviderEnv?: { [key: string]: string }; + private readonly version: KubernetesVersion; /** @@ -477,9 +594,10 @@ export class Cluster extends Resource implements ICluster { defaultPort: ec2.Port.tcp(443), // Control Plane has an HTTPS API }); + this.vpcSubnets = props.vpcSubnets ?? [{ subnetType: ec2.SubnetType.PUBLIC }, { subnetType: ec2.SubnetType.PRIVATE }]; + // Get subnetIds for all selected subnets - const placements = props.vpcSubnets || [{ subnetType: ec2.SubnetType.PUBLIC }, { subnetType: ec2.SubnetType.PRIVATE }]; - const subnetIds = [...new Set(Array().concat(...placements.map(s => this.vpc.selectSubnets(s).subnetIds)))]; + const subnetIds = [...new Set(Array().concat(...this.vpcSubnets.map(s => this.vpc.selectSubnets(s).subnetIds)))]; const clusterProps: CfnClusterProps = { name: this.physicalName, @@ -494,9 +612,37 @@ export class Cluster extends Resource implements ICluster { let resource; this.kubectlEnabled = props.kubectlEnabled === undefined ? true : props.kubectlEnabled; if (this.kubectlEnabled) { - resource = new ClusterResource(this, 'Resource', clusterProps); + + this.endpointAccess = props.endpointAccess ?? EndpointAccess.PUBLIC_AND_PRIVATE; + this.kubectlProviderEnv = props.kubectlEnvironment; + + if (this.endpointAccess._config.privateAccess && this.vpc instanceof ec2.Vpc) { + // validate VPC properties according to: https://docs.aws.amazon.com/eks/latest/userguide/cluster-endpoint.html + if (!this.vpc.dnsHostnamesEnabled || !this.vpc.dnsSupportEnabled) { + throw new Error('Private endpoint access requires the VPC to have DNS support and DNS hostnames enabled. Use `enableDnsHostnames: true` and `enableDnsSupport: true` when creating the VPC.'); + } + } + + this.kubctlProviderSecurityGroup = new ec2.SecurityGroup(this, 'KubectlProviderSecurityGroup', { + vpc: this.vpc, + description: 'Comminication between KubectlProvider and EKS Control Plane', + }); + + // grant the kubectl provider access to the cluster control plane. + this.connections.allowFrom(this.kubctlProviderSecurityGroup, this.connections.defaultPort!); + + resource = new ClusterResource(this, 'Resource', { + ...clusterProps, + endpointPrivateAccess: this.endpointAccess._config.privateAccess, + endpointPublicAccess: this.endpointAccess._config.publicAccess, + publicAccessCidrs: this.endpointAccess._config.publicCidrs, + }); this._clusterResource = resource; + // the security group and vpc must exist in order to properly delete the cluster (since we run `kubectl delete`). + // this ensures that. + this._clusterResource.node.addDependency(this.kubctlProviderSecurityGroup, this.vpc); + // see https://github.com/aws/aws-cdk/issues/9027 this._clusterResource.creationRole.addToPolicy(new iam.PolicyStatement({ actions: ['ec2:DescribeVpcs'], @@ -519,6 +665,11 @@ export class Cluster extends Resource implements ICluster { // add the cluster resource itself as a dependency of the barrier this._kubectlReadyBarrier.node.addDependency(this._clusterResource); } else { + + if (props.endpointAccess) { + throw new Error("'endpointAccess' is not supported for clusters without kubectl enabled."); + } + resource = new CfnCluster(this, 'Resource', clusterProps); } @@ -904,7 +1055,29 @@ export class Cluster extends Resource implements ICluster { let provider = this.stack.node.tryFindChild(uid) as KubectlProvider; if (!provider) { // create the provider. - provider = new KubectlProvider(this.stack, uid); + + let providerProps: KubectlProviderProps = { + env: this.kubectlProviderEnv, + }; + + if (!this.endpointAccess) { + // this should have been set on cluster instantiation for kubectl enabled clusters + throw new Error("Expected 'endpointAccess' to be defined for kubectl enabled clusters"); + } + + if (!this.endpointAccess._config.publicAccess) { + // endpoint access is private only, we need to attach the + // provider to the VPC so that it can access the cluster. + providerProps = { + ...providerProps, + vpc: this.vpc, + // lambda can only be accociated with max 16 subnets and they all need to be private. + vpcSubnets: {subnets: this.selectPrivateSubnets().slice(0, 16)}, + securityGroups: [this.kubctlProviderSecurityGroup!], + }; + } + + provider = new KubectlProvider(this.stack, uid, providerProps); } // allow the kubectl provider to assume the cluster creation role. @@ -919,6 +1092,17 @@ export class Cluster extends Resource implements ICluster { return provider; } + private selectPrivateSubnets(): ec2.ISubnet[] { + + const privateSubnets: ec2.ISubnet[] = []; + + for (const placement of this.vpcSubnets) { + privateSubnets.push(...this.vpc.selectSubnets(placement).subnets.filter(s => s instanceof ec2.PrivateSubnet)); + } + + return privateSubnets; + } + /** * Installs the AWS spot instance interrupt handler on the cluster if it's not * already added. diff --git a/packages/@aws-cdk/aws-eks/lib/kubectl-handler/apply/__init__.py b/packages/@aws-cdk/aws-eks/lib/kubectl-handler/apply/__init__.py index c892d9bc4e7ff..e5c9d712758fd 100644 --- a/packages/@aws-cdk/aws-eks/lib/kubectl-handler/apply/__init__.py +++ b/packages/@aws-cdk/aws-eks/lib/kubectl-handler/apply/__init__.py @@ -49,18 +49,21 @@ def apply_handler(event, context): def kubectl(verb, file): - retry = 3 + maxAttempts = 3 + retry = maxAttempts while retry > 0: try: cmd = ['kubectl', verb, '--kubeconfig', kubeconfig, '-f', file] + logger.info(f'Running command: {cmd}') output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as exc: output = exc.output if b'i/o timeout' in output and retry > 0: - logger.info("kubectl timed out, retries left: %s" % retry) - retry = retry - 1 + retry = retry - 1 + logger.info("kubectl timed out, retries left: %s" % retry) else: raise Exception(output) else: logger.info(output) return + raise Exception(f'Operation failed after {maxAttempts} attempts: {output}') diff --git a/packages/@aws-cdk/aws-eks/lib/kubectl-handler/helm/__init__.py b/packages/@aws-cdk/aws-eks/lib/kubectl-handler/helm/__init__.py index 6e740b1cdaca8..67171b11aeede 100644 --- a/packages/@aws-cdk/aws-eks/lib/kubectl-handler/helm/__init__.py +++ b/packages/@aws-cdk/aws-eks/lib/kubectl-handler/helm/__init__.py @@ -75,10 +75,11 @@ def helm(verb, release, chart = None, repo = None, file = None, namespace = None if wait: cmnd.append('--wait') if not timeout is None: - cmnd.extend(['--timeout', timeout]) + cmnd.extend(['--timeout', timeout]) cmnd.extend(['--kubeconfig', kubeconfig]) - retry = 3 + maxAttempts = 3 + retry = maxAttempts while retry > 0: try: output = subprocess.check_output(cmnd, stderr=subprocess.STDOUT, cwd=outdir) @@ -87,7 +88,8 @@ def helm(verb, release, chart = None, repo = None, file = None, namespace = None except subprocess.CalledProcessError as exc: output = exc.output if b'Broken pipe' in output: - logger.info("Broken pipe, retries left: %s" % retry) retry = retry - 1 + logger.info("Broken pipe, retries left: %s" % retry) else: raise Exception(output) + raise Exception(f'Operation failed after {maxAttempts} attempts: {output}') diff --git a/packages/@aws-cdk/aws-eks/lib/kubectl-handler/patch/__init__.py b/packages/@aws-cdk/aws-eks/lib/kubectl-handler/patch/__init__.py index d6211b9348e1e..6597341a4806d 100644 --- a/packages/@aws-cdk/aws-eks/lib/kubectl-handler/patch/__init__.py +++ b/packages/@aws-cdk/aws-eks/lib/kubectl-handler/patch/__init__.py @@ -36,9 +36,9 @@ def patch_handler(event, context): patch_type = props['PatchType'] patch_json = None - if request_type == 'Create' or request_type == 'Update': + if request_type == 'Create' or request_type == 'Update': patch_json = apply_patch_json - elif request_type == 'Delete': + elif request_type == 'Delete': patch_json = restore_patch_json else: raise Exception("invalid request type %s" % request_type) @@ -47,7 +47,8 @@ def patch_handler(event, context): def kubectl(args): - retry = 3 + maxAttempts = 3 + retry = maxAttempts while retry > 0: try: cmd = [ 'kubectl', '--kubeconfig', kubeconfig ] + args @@ -55,10 +56,11 @@ def kubectl(args): except subprocess.CalledProcessError as exc: output = exc.output if b'i/o timeout' in output and retry > 0: - logger.info("kubectl timed out, retries left: %s" % retry) retry = retry - 1 + logger.info("kubectl timed out, retries left: %s" % retry) else: raise Exception(output) else: logger.info(output) return + raise Exception(f'Operation failed after {maxAttempts} attempts: {output}') \ No newline at end of file diff --git a/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts b/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts index 9fe12c4b6169b..c7c845fefcb9b 100644 --- a/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts +++ b/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts @@ -1,10 +1,41 @@ +import * as path from 'path'; +import { IVpc, ISecurityGroup, SubnetSelection } from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import { Construct, Duration, NestedStack } from '@aws-cdk/core'; import * as cr from '@aws-cdk/custom-resources'; -import * as path from 'path'; import { KubectlLayer } from './kubectl-layer'; +export interface KubectlProviderProps { + + /** + * Connect the provider to a VPC. + * + * @default - no vpc attachement. + */ + readonly vpc?: IVpc; + + /** + * Select the Vpc subnets to attach to the provider. + * + * @default - no subnets. + */ + readonly vpcSubnets?: SubnetSelection; + + /** + * Attach security groups to the provider. + * + * @default - no security groups. + */ + readonly securityGroups?: ISecurityGroup[]; + + /** + * Environment variables to inject to the provider function. + */ + readonly env?: { [key: string]: string }; + +} + export class KubectlProvider extends NestedStack { /** * The custom resource provider. @@ -16,7 +47,7 @@ export class KubectlProvider extends NestedStack { */ public readonly role: iam.IRole; - public constructor(scope: Construct, id: string) { + public constructor(scope: Construct, id: string, props: KubectlProviderProps = { }) { super(scope, id); const handler = new lambda.Function(this, 'Handler', { @@ -27,6 +58,10 @@ export class KubectlProvider extends NestedStack { description: 'onEvent handler for EKS kubectl resource provider', layers: [ KubectlLayer.getOrCreate(this, { version: '2.0.0' }) ], memorySize: 256, + vpc: props.vpc, + securityGroups: props.securityGroups, + vpcSubnets: props.vpcSubnets, + environment: props.env, }); this.provider = new cr.Provider(this, 'Provider', { diff --git a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-private-endpoint.expected.json b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-private-endpoint.expected.json new file mode 100644 index 0000000000000..0889cc63a0237 --- /dev/null +++ b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-private-endpoint.expected.json @@ -0,0 +1,1346 @@ +{ + "Resources": { + "AdminRole38563C57": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "Vpc8378EB38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-eks-cluster-private-endpoint-test/Vpc" + } + ] + } + }, + "VpcPublicSubnet1Subnet5C2D37C4": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/19", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "kubernetes.io/role/elb", + "Value": "1" + }, + { + "Key": "Name", + "Value": "aws-cdk-eks-cluster-private-endpoint-test/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTable6C95E38E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "kubernetes.io/role/elb", + "Value": "1" + }, + { + "Key": "Name", + "Value": "aws-cdk-eks-cluster-private-endpoint-test/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTableAssociation97140677": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "VpcPublicSubnet1DefaultRoute3DA9E72A": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet1EIPD7E02669": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "kubernetes.io/role/elb", + "Value": "1" + }, + { + "Key": "Name", + "Value": "aws-cdk-eks-cluster-private-endpoint-test/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1NATGateway4D7517AA": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "Tags": [ + { + "Key": "kubernetes.io/role/elb", + "Value": "1" + }, + { + "Key": "Name", + "Value": "aws-cdk-eks-cluster-private-endpoint-test/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet2Subnet691E08A3": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.32.0/19", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "kubernetes.io/role/elb", + "Value": "1" + }, + { + "Key": "Name", + "Value": "aws-cdk-eks-cluster-private-endpoint-test/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTable94F7E489": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "kubernetes.io/role/elb", + "Value": "1" + }, + { + "Key": "Name", + "Value": "aws-cdk-eks-cluster-private-endpoint-test/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTableAssociationDD5762D8": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + } + } + }, + "VpcPublicSubnet2DefaultRoute97F91067": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet3SubnetBE12F0B6": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.64.0/19", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1c", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "kubernetes.io/role/elb", + "Value": "1" + }, + { + "Key": "Name", + "Value": "aws-cdk-eks-cluster-private-endpoint-test/Vpc/PublicSubnet3" + } + ] + } + }, + "VpcPublicSubnet3RouteTable93458DBB": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "kubernetes.io/role/elb", + "Value": "1" + }, + { + "Key": "Name", + "Value": "aws-cdk-eks-cluster-private-endpoint-test/Vpc/PublicSubnet3" + } + ] + } + }, + "VpcPublicSubnet3RouteTableAssociation1F1EDF02": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet3RouteTable93458DBB" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet3SubnetBE12F0B6" + } + } + }, + "VpcPublicSubnet3DefaultRoute4697774F": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet3RouteTable93458DBB" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPrivateSubnet1Subnet536B997A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.96.0/19", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "kubernetes.io/role/internal-elb", + "Value": "1" + }, + { + "Key": "Name", + "Value": "aws-cdk-eks-cluster-private-endpoint-test/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableB2C5B500": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "kubernetes.io/role/internal-elb", + "Value": "1" + }, + { + "Key": "Name", + "Value": "aws-cdk-eks-cluster-private-endpoint-test/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableAssociation70C59FA6": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "VpcPrivateSubnet1DefaultRouteBE02A9ED": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + } + } + }, + "VpcPrivateSubnet2Subnet3788AAA1": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.128.0/19", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "kubernetes.io/role/internal-elb", + "Value": "1" + }, + { + "Key": "Name", + "Value": "aws-cdk-eks-cluster-private-endpoint-test/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableA678073B": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "kubernetes.io/role/internal-elb", + "Value": "1" + }, + { + "Key": "Name", + "Value": "aws-cdk-eks-cluster-private-endpoint-test/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableAssociationA89CAD56": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + } + }, + "VpcPrivateSubnet2DefaultRoute060D2087": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + } + } + }, + "VpcPrivateSubnet3SubnetF258B56E": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.160.0/19", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1c", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "kubernetes.io/role/internal-elb", + "Value": "1" + }, + { + "Key": "Name", + "Value": "aws-cdk-eks-cluster-private-endpoint-test/Vpc/PrivateSubnet3" + } + ] + } + }, + "VpcPrivateSubnet3RouteTableD98824C7": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "kubernetes.io/role/internal-elb", + "Value": "1" + }, + { + "Key": "Name", + "Value": "aws-cdk-eks-cluster-private-endpoint-test/Vpc/PrivateSubnet3" + } + ] + } + }, + "VpcPrivateSubnet3RouteTableAssociation16BDDC43": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet3RouteTableD98824C7" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet3SubnetF258B56E" + } + } + }, + "VpcPrivateSubnet3DefaultRoute94B74F0D": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet3RouteTableD98824C7" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + } + } + }, + "VpcIGWD7BA715C": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-eks-cluster-private-endpoint-test/Vpc" + } + ] + } + }, + "VpcVPCGWBF912B6E": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "InternetGatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "ClusterRoleFA261979": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "eks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/AmazonEKSClusterPolicy" + ] + ] + } + ] + } + }, + "ClusterControlPlaneSecurityGroupD274242C": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "EKS Control Plane Security Group", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "ClusterControlPlaneSecurityGroupfromawscdkeksclusterprivateendpointtestClusterKubectlProviderSecurityGroup6A0B729C443DF3A2707": { + "Type": "AWS::EC2::SecurityGroupIngress", + "Properties": { + "IpProtocol": "tcp", + "Description": "from awscdkeksclusterprivateendpointtestClusterKubectlProviderSecurityGroup6A0B729C:443", + "FromPort": 443, + "GroupId": { + "Fn::GetAtt": [ + "ClusterControlPlaneSecurityGroupD274242C", + "GroupId" + ] + }, + "SourceSecurityGroupId": { + "Fn::GetAtt": [ + "ClusterKubectlProviderSecurityGroup2D90691C", + "GroupId" + ] + }, + "ToPort": 443 + } + }, + "ClusterKubectlProviderSecurityGroup2D90691C": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "Comminication between KubectlProvider and EKS Control Plane", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "ClusterCreationRole360249B6": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": [ + { + "Fn::GetAtt": [ + "awscdkawseksClusterResourceProviderNestedStackawscdkawseksClusterResourceProviderNestedStackResource9827C454", + "Outputs.awscdkeksclusterprivateendpointtestawscdkawseksClusterResourceProviderOnEventHandlerServiceRole4392FD6EArn" + ] + }, + { + "Fn::GetAtt": [ + "awscdkawseksClusterResourceProviderNestedStackawscdkawseksClusterResourceProviderNestedStackResource9827C454", + "Outputs.awscdkeksclusterprivateendpointtestawscdkawseksClusterResourceProviderIsCompleteHandlerServiceRole956A78E2Arn" + ] + } + ] + } + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "awscdkawseksKubectlProviderNestedStackawscdkawseksKubectlProviderNestedStackResourceA7AEBA6B", + "Outputs.awscdkeksclusterprivateendpointtestawscdkawseksKubectlProviderHandlerServiceRole5505C312Arn" + ] + } + } + } + ], + "Version": "2012-10-17" + } + }, + "DependsOn": [ + "ClusterKubectlProviderSecurityGroup2D90691C", + "VpcIGWD7BA715C", + "VpcPrivateSubnet1DefaultRouteBE02A9ED", + "VpcPrivateSubnet1RouteTableB2C5B500", + "VpcPrivateSubnet1RouteTableAssociation70C59FA6", + "VpcPrivateSubnet1Subnet536B997A", + "VpcPrivateSubnet2DefaultRoute060D2087", + "VpcPrivateSubnet2RouteTableA678073B", + "VpcPrivateSubnet2RouteTableAssociationA89CAD56", + "VpcPrivateSubnet2Subnet3788AAA1", + "VpcPrivateSubnet3DefaultRoute94B74F0D", + "VpcPrivateSubnet3RouteTableD98824C7", + "VpcPrivateSubnet3RouteTableAssociation16BDDC43", + "VpcPrivateSubnet3SubnetF258B56E", + "VpcPublicSubnet1DefaultRoute3DA9E72A", + "VpcPublicSubnet1EIPD7E02669", + "VpcPublicSubnet1NATGateway4D7517AA", + "VpcPublicSubnet1RouteTable6C95E38E", + "VpcPublicSubnet1RouteTableAssociation97140677", + "VpcPublicSubnet1Subnet5C2D37C4", + "VpcPublicSubnet2DefaultRoute97F91067", + "VpcPublicSubnet2RouteTable94F7E489", + "VpcPublicSubnet2RouteTableAssociationDD5762D8", + "VpcPublicSubnet2Subnet691E08A3", + "VpcPublicSubnet3DefaultRoute4697774F", + "VpcPublicSubnet3RouteTable93458DBB", + "VpcPublicSubnet3RouteTableAssociation1F1EDF02", + "VpcPublicSubnet3SubnetBE12F0B6", + "Vpc8378EB38", + "VpcVPCGWBF912B6E" + ] + }, + "ClusterCreationRoleDefaultPolicyE8BDFC7B": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "iam:PassRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "ClusterRoleFA261979", + "Arn" + ] + } + }, + { + "Action": [ + "ec2:DescribeSubnets", + "ec2:DescribeRouteTables" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "eks:CreateCluster", + "eks:DescribeCluster", + "eks:DescribeUpdate", + "eks:DeleteCluster", + "eks:UpdateClusterVersion", + "eks:UpdateClusterConfig", + "eks:CreateFargateProfile", + "eks:TagResource", + "eks:UntagResource" + ], + "Effect": "Allow", + "Resource": [ + "*" + ] + }, + { + "Action": [ + "eks:DescribeFargateProfile", + "eks:DeleteFargateProfile" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "iam:GetRole", + "iam:listAttachedRolePolicies" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "iam:CreateServiceLinkedRole", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "ec2:DescribeVpcs", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:test-region:12345678:vpc/", + { + "Ref": "Vpc8378EB38" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "ClusterCreationRoleDefaultPolicyE8BDFC7B", + "Roles": [ + { + "Ref": "ClusterCreationRole360249B6" + } + ] + }, + "DependsOn": [ + "ClusterKubectlProviderSecurityGroup2D90691C", + "VpcIGWD7BA715C", + "VpcPrivateSubnet1DefaultRouteBE02A9ED", + "VpcPrivateSubnet1RouteTableB2C5B500", + "VpcPrivateSubnet1RouteTableAssociation70C59FA6", + "VpcPrivateSubnet1Subnet536B997A", + "VpcPrivateSubnet2DefaultRoute060D2087", + "VpcPrivateSubnet2RouteTableA678073B", + "VpcPrivateSubnet2RouteTableAssociationA89CAD56", + "VpcPrivateSubnet2Subnet3788AAA1", + "VpcPrivateSubnet3DefaultRoute94B74F0D", + "VpcPrivateSubnet3RouteTableD98824C7", + "VpcPrivateSubnet3RouteTableAssociation16BDDC43", + "VpcPrivateSubnet3SubnetF258B56E", + "VpcPublicSubnet1DefaultRoute3DA9E72A", + "VpcPublicSubnet1EIPD7E02669", + "VpcPublicSubnet1NATGateway4D7517AA", + "VpcPublicSubnet1RouteTable6C95E38E", + "VpcPublicSubnet1RouteTableAssociation97140677", + "VpcPublicSubnet1Subnet5C2D37C4", + "VpcPublicSubnet2DefaultRoute97F91067", + "VpcPublicSubnet2RouteTable94F7E489", + "VpcPublicSubnet2RouteTableAssociationDD5762D8", + "VpcPublicSubnet2Subnet691E08A3", + "VpcPublicSubnet3DefaultRoute4697774F", + "VpcPublicSubnet3RouteTable93458DBB", + "VpcPublicSubnet3RouteTableAssociation1F1EDF02", + "VpcPublicSubnet3SubnetBE12F0B6", + "Vpc8378EB38", + "VpcVPCGWBF912B6E" + ] + }, + "Cluster9EE0221C": { + "Type": "Custom::AWSCDK-EKS-Cluster", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "awscdkawseksClusterResourceProviderNestedStackawscdkawseksClusterResourceProviderNestedStackResource9827C454", + "Outputs.awscdkeksclusterprivateendpointtestawscdkawseksClusterResourceProviderframeworkonEvent080B290CArn" + ] + }, + "Config": { + "version": "1.16", + "roleArn": { + "Fn::GetAtt": [ + "ClusterRoleFA261979", + "Arn" + ] + }, + "resourcesVpcConfig": { + "subnetIds": [ + { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, + { + "Ref": "VpcPublicSubnet3SubnetBE12F0B6" + }, + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + }, + { + "Ref": "VpcPrivateSubnet3SubnetF258B56E" + } + ], + "securityGroupIds": [ + { + "Fn::GetAtt": [ + "ClusterControlPlaneSecurityGroupD274242C", + "GroupId" + ] + } + ], + "endpointPublicAccess": false, + "endpointPrivateAccess": true + } + }, + "AssumeRoleArn": { + "Fn::GetAtt": [ + "ClusterCreationRole360249B6", + "Arn" + ] + }, + "AttributesRevision": 2 + }, + "DependsOn": [ + "ClusterKubectlProviderSecurityGroup2D90691C", + "ClusterCreationRoleDefaultPolicyE8BDFC7B", + "ClusterCreationRole360249B6", + "VpcIGWD7BA715C", + "VpcPrivateSubnet1DefaultRouteBE02A9ED", + "VpcPrivateSubnet1RouteTableB2C5B500", + "VpcPrivateSubnet1RouteTableAssociation70C59FA6", + "VpcPrivateSubnet1Subnet536B997A", + "VpcPrivateSubnet2DefaultRoute060D2087", + "VpcPrivateSubnet2RouteTableA678073B", + "VpcPrivateSubnet2RouteTableAssociationA89CAD56", + "VpcPrivateSubnet2Subnet3788AAA1", + "VpcPrivateSubnet3DefaultRoute94B74F0D", + "VpcPrivateSubnet3RouteTableD98824C7", + "VpcPrivateSubnet3RouteTableAssociation16BDDC43", + "VpcPrivateSubnet3SubnetF258B56E", + "VpcPublicSubnet1DefaultRoute3DA9E72A", + "VpcPublicSubnet1EIPD7E02669", + "VpcPublicSubnet1NATGateway4D7517AA", + "VpcPublicSubnet1RouteTable6C95E38E", + "VpcPublicSubnet1RouteTableAssociation97140677", + "VpcPublicSubnet1Subnet5C2D37C4", + "VpcPublicSubnet2DefaultRoute97F91067", + "VpcPublicSubnet2RouteTable94F7E489", + "VpcPublicSubnet2RouteTableAssociationDD5762D8", + "VpcPublicSubnet2Subnet691E08A3", + "VpcPublicSubnet3DefaultRoute4697774F", + "VpcPublicSubnet3RouteTable93458DBB", + "VpcPublicSubnet3RouteTableAssociation1F1EDF02", + "VpcPublicSubnet3SubnetBE12F0B6", + "Vpc8378EB38", + "VpcVPCGWBF912B6E" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "ClusterKubectlReadyBarrier200052AF": { + "Type": "AWS::SSM::Parameter", + "Properties": { + "Type": "String", + "Value": "aws:cdk:eks:kubectl-ready" + }, + "DependsOn": [ + "ClusterCreationRoleDefaultPolicyE8BDFC7B", + "ClusterCreationRole360249B6", + "Cluster9EE0221C" + ] + }, + "ClusterAwsAuthmanifestFE51F8AE": { + "Type": "Custom::AWSCDK-EKS-KubernetesResource", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "awscdkawseksKubectlProviderNestedStackawscdkawseksKubectlProviderNestedStackResourceA7AEBA6B", + "Outputs.awscdkeksclusterprivateendpointtestawscdkawseksKubectlProviderframeworkonEventC2C76E2FArn" + ] + }, + "Manifest": { + "Fn::Join": [ + "", + [ + "[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"aws-auth\",\"namespace\":\"kube-system\"},\"data\":{\"mapRoles\":\"[{\\\"rolearn\\\":\\\"", + { + "Fn::GetAtt": [ + "AdminRole38563C57", + "Arn" + ] + }, + "\\\",\\\"username\\\":\\\"", + { + "Fn::GetAtt": [ + "AdminRole38563C57", + "Arn" + ] + }, + "\\\",\\\"groups\\\":[\\\"system:masters\\\"]},{\\\"rolearn\\\":\\\"", + { + "Fn::GetAtt": [ + "ClusterNodegroupDefaultCapacityNodeGroupRole55953B04", + "Arn" + ] + }, + "\\\",\\\"username\\\":\\\"system:node:{{EC2PrivateDNSName}}\\\",\\\"groups\\\":[\\\"system:bootstrappers\\\",\\\"system:nodes\\\"]}]\",\"mapUsers\":\"[]\",\"mapAccounts\":\"[]\"}}]" + ] + ] + }, + "ClusterName": { + "Ref": "Cluster9EE0221C" + }, + "RoleArn": { + "Fn::GetAtt": [ + "ClusterCreationRole360249B6", + "Arn" + ] + } + }, + "DependsOn": [ + "ClusterKubectlReadyBarrier200052AF" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "ClusterNodegroupDefaultCapacityNodeGroupRole55953B04": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": { + "Fn::Join": [ + "", + [ + "ec2.", + { + "Ref": "AWS::URLSuffix" + } + ] + ] + } + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/AmazonEKSWorkerNodePolicy" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/AmazonEKS_CNI_Policy" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/AmazonEC2ContainerRegistryReadOnly" + ] + ] + } + ] + } + }, + "ClusterNodegroupDefaultCapacityDA0920A3": { + "Type": "AWS::EKS::Nodegroup", + "Properties": { + "ClusterName": { + "Ref": "Cluster9EE0221C" + }, + "NodeRole": { + "Fn::GetAtt": [ + "ClusterNodegroupDefaultCapacityNodeGroupRole55953B04", + "Arn" + ] + }, + "Subnets": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + }, + { + "Ref": "VpcPrivateSubnet3SubnetF258B56E" + } + ], + "ForceUpdateEnabled": true, + "InstanceTypes": [ + "m5.large" + ], + "ScalingConfig": { + "DesiredSize": 2, + "MaxSize": 2, + "MinSize": 2 + } + } + }, + "Clustermanifestconfigmap3F180550": { + "Type": "Custom::AWSCDK-EKS-KubernetesResource", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "awscdkawseksKubectlProviderNestedStackawscdkawseksKubectlProviderNestedStackResourceA7AEBA6B", + "Outputs.awscdkeksclusterprivateendpointtestawscdkawseksKubectlProviderframeworkonEventC2C76E2FArn" + ] + }, + "Manifest": "[{\"kind\":\"ConfigMap\",\"apiVersion\":\"v1\",\"data\":{\"hello\":\"world\"},\"metadata\":{\"name\":\"config-map\"}}]", + "ClusterName": { + "Ref": "Cluster9EE0221C" + }, + "RoleArn": { + "Fn::GetAtt": [ + "ClusterCreationRole360249B6", + "Arn" + ] + } + }, + "DependsOn": [ + "ClusterKubectlReadyBarrier200052AF" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "awscdkawseksClusterResourceProviderNestedStackawscdkawseksClusterResourceProviderNestedStackResource9827C454": { + "Type": "AWS::CloudFormation::Stack", + "Properties": { + "TemplateURL": { + "Fn::Join": [ + "", + [ + "https://s3.test-region.", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "AssetParameters3c8e15207108696f26eb3900c56b9ed4a81535ed7d0fdb4477972f1741ad9789S3BucketBC18629C" + }, + "/", + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters3c8e15207108696f26eb3900c56b9ed4a81535ed7d0fdb4477972f1741ad9789S3VersionKeyE68C888F" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters3c8e15207108696f26eb3900c56b9ed4a81535ed7d0fdb4477972f1741ad9789S3VersionKeyE68C888F" + } + ] + } + ] + } + ] + ] + }, + "Parameters": { + "referencetoawscdkeksclusterprivateendpointtestAssetParameters00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791S3Bucket87F4EA82Ref": { + "Ref": "AssetParameters00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791S3Bucket26C90BA0" + }, + "referencetoawscdkeksclusterprivateendpointtestAssetParameters00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791S3VersionKey3BEF8ACDRef": { + "Ref": "AssetParameters00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791S3VersionKeyD269C675" + }, + "referencetoawscdkeksclusterprivateendpointtestAssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3Bucket7CB66361Ref": { + "Ref": "AssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3BucketF1BD2256" + }, + "referencetoawscdkeksclusterprivateendpointtestAssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3VersionKeyF78CAD23Ref": { + "Ref": "AssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3VersionKeyF47FA401" + } + } + } + }, + "awscdkawseksKubectlProviderNestedStackawscdkawseksKubectlProviderNestedStackResourceA7AEBA6B": { + "Type": "AWS::CloudFormation::Stack", + "Properties": { + "TemplateURL": { + "Fn::Join": [ + "", + [ + "https://s3.test-region.", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "AssetParametersf2ad7629f5f54ad293dccc2fb60891424f9149f12d84f2f12728543b145962a0S3BucketACD6057C" + }, + "/", + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersf2ad7629f5f54ad293dccc2fb60891424f9149f12d84f2f12728543b145962a0S3VersionKey20D7AC7B" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersf2ad7629f5f54ad293dccc2fb60891424f9149f12d84f2f12728543b145962a0S3VersionKey20D7AC7B" + } + ] + } + ] + } + ] + ] + }, + "Parameters": { + "referencetoawscdkeksclusterprivateendpointtestAssetParameters649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502S3Bucket5848D8F5Ref": { + "Ref": "AssetParameters649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502S3BucketE7D09A6B" + }, + "referencetoawscdkeksclusterprivateendpointtestAssetParameters649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502S3VersionKeyD69255C2Ref": { + "Ref": "AssetParameters649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502S3VersionKey1DA734B2" + }, + "referencetoawscdkeksclusterprivateendpointtestVpcPrivateSubnet1Subnet94DAD769Ref": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + "referencetoawscdkeksclusterprivateendpointtestVpcPrivateSubnet2Subnet04963C08Ref": { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + }, + "referencetoawscdkeksclusterprivateendpointtestVpcPrivateSubnet3SubnetC47FD39ARef": { + "Ref": "VpcPrivateSubnet3SubnetF258B56E" + }, + "referencetoawscdkeksclusterprivateendpointtestClusterKubectlProviderSecurityGroup67FA4325GroupId": { + "Fn::GetAtt": [ + "ClusterKubectlProviderSecurityGroup2D90691C", + "GroupId" + ] + }, + "referencetoawscdkeksclusterprivateendpointtestAssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3Bucket7CB66361Ref": { + "Ref": "AssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3BucketF1BD2256" + }, + "referencetoawscdkeksclusterprivateendpointtestAssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3VersionKeyF78CAD23Ref": { + "Ref": "AssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3VersionKeyF47FA401" + } + } + } + } + }, + "Outputs": { + "ClusterConfigCommand43AAE40F": { + "Value": { + "Fn::Join": [ + "", + [ + "aws eks update-kubeconfig --name ", + { + "Ref": "Cluster9EE0221C" + }, + " --region test-region --role-arn ", + { + "Fn::GetAtt": [ + "AdminRole38563C57", + "Arn" + ] + } + ] + ] + } + }, + "ClusterGetTokenCommand06AE992E": { + "Value": { + "Fn::Join": [ + "", + [ + "aws eks get-token --cluster-name ", + { + "Ref": "Cluster9EE0221C" + }, + " --region test-region --role-arn ", + { + "Fn::GetAtt": [ + "AdminRole38563C57", + "Arn" + ] + } + ] + ] + } + } + }, + "Parameters": { + "AssetParameters00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791S3Bucket26C90BA0": { + "Type": "String", + "Description": "S3 bucket for asset \"00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791\"" + }, + "AssetParameters00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791S3VersionKeyD269C675": { + "Type": "String", + "Description": "S3 key for asset version \"00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791\"" + }, + "AssetParameters00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791ArtifactHashAADC8B03": { + "Type": "String", + "Description": "Artifact hash for asset \"00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791\"" + }, + "AssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3BucketF1BD2256": { + "Type": "String", + "Description": "S3 bucket for asset \"974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74c\"" + }, + "AssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3VersionKeyF47FA401": { + "Type": "String", + "Description": "S3 key for asset version \"974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74c\"" + }, + "AssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cArtifactHash5C0B1EA0": { + "Type": "String", + "Description": "Artifact hash for asset \"974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74c\"" + }, + "AssetParameters649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502S3BucketE7D09A6B": { + "Type": "String", + "Description": "S3 bucket for asset \"649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502\"" + }, + "AssetParameters649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502S3VersionKey1DA734B2": { + "Type": "String", + "Description": "S3 key for asset version \"649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502\"" + }, + "AssetParameters649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502ArtifactHash815E1969": { + "Type": "String", + "Description": "Artifact hash for asset \"649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502\"" + }, + "AssetParameters3c8e15207108696f26eb3900c56b9ed4a81535ed7d0fdb4477972f1741ad9789S3BucketBC18629C": { + "Type": "String", + "Description": "S3 bucket for asset \"3c8e15207108696f26eb3900c56b9ed4a81535ed7d0fdb4477972f1741ad9789\"" + }, + "AssetParameters3c8e15207108696f26eb3900c56b9ed4a81535ed7d0fdb4477972f1741ad9789S3VersionKeyE68C888F": { + "Type": "String", + "Description": "S3 key for asset version \"3c8e15207108696f26eb3900c56b9ed4a81535ed7d0fdb4477972f1741ad9789\"" + }, + "AssetParameters3c8e15207108696f26eb3900c56b9ed4a81535ed7d0fdb4477972f1741ad9789ArtifactHash026B7D88": { + "Type": "String", + "Description": "Artifact hash for asset \"3c8e15207108696f26eb3900c56b9ed4a81535ed7d0fdb4477972f1741ad9789\"" + }, + "AssetParametersf2ad7629f5f54ad293dccc2fb60891424f9149f12d84f2f12728543b145962a0S3BucketACD6057C": { + "Type": "String", + "Description": "S3 bucket for asset \"f2ad7629f5f54ad293dccc2fb60891424f9149f12d84f2f12728543b145962a0\"" + }, + "AssetParametersf2ad7629f5f54ad293dccc2fb60891424f9149f12d84f2f12728543b145962a0S3VersionKey20D7AC7B": { + "Type": "String", + "Description": "S3 key for asset version \"f2ad7629f5f54ad293dccc2fb60891424f9149f12d84f2f12728543b145962a0\"" + }, + "AssetParametersf2ad7629f5f54ad293dccc2fb60891424f9149f12d84f2f12728543b145962a0ArtifactHash05CD8D10": { + "Type": "String", + "Description": "Artifact hash for asset \"f2ad7629f5f54ad293dccc2fb60891424f9149f12d84f2f12728543b145962a0\"" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-private-endpoint.ts b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-private-endpoint.ts new file mode 100644 index 0000000000000..c8bcd43bdebfa --- /dev/null +++ b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-private-endpoint.ts @@ -0,0 +1,48 @@ +/// !cdk-integ pragma:ignore-assets +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as iam from '@aws-cdk/aws-iam'; +import { App } from '@aws-cdk/core'; +import * as eks from '../lib'; +import { TestStack } from './util'; + +class EksClusterStack extends TestStack { + constructor(scope: App, id: string) { + super(scope, id); + + // allow all account users to assume this role in order to admin the cluster + const mastersRole = new iam.Role(this, 'AdminRole', { + assumedBy: new iam.AccountRootPrincipal(), + }); + + // just need one nat gateway to simplify the test + const vpc = new ec2.Vpc(this, 'Vpc', { maxAzs: 3, natGateways: 1 }); + + const cluster = new eks.Cluster(this, 'Cluster', { + vpc, + mastersRole, + defaultCapacity: 2, + version: eks.KubernetesVersion.V1_16, + endpointAccess: eks.EndpointAccess.PRIVATE, + }); + + // this is the valdiation. it won't work if the private access is not setup properly. + cluster.addResource('config-map', { + kind: 'ConfigMap', + apiVersion: 'v1', + data: { + hello: 'world', + }, + metadata: { + name: 'config-map', + }, + }); + + } +} + + +const app = new App(); + +new EksClusterStack(app, 'aws-cdk-eks-cluster-private-endpoint-test'); + +app.synth(); diff --git a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.expected.json b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.expected.json index 1aa1b2d54725d..2be0396673fb6 100644 --- a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.expected.json +++ b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.expected.json @@ -581,6 +581,27 @@ } } }, + "ClusterControlPlaneSecurityGroupfromawscdkeksclustertestClusterKubectlProviderSecurityGroup0285626644359187EDA": { + "Type": "AWS::EC2::SecurityGroupIngress", + "Properties": { + "IpProtocol": "tcp", + "Description": "from awscdkeksclustertestClusterKubectlProviderSecurityGroup02856266:443", + "FromPort": 443, + "GroupId": { + "Fn::GetAtt": [ + "ClusterControlPlaneSecurityGroupD274242C", + "GroupId" + ] + }, + "SourceSecurityGroupId": { + "Fn::GetAtt": [ + "ClusterKubectlProviderSecurityGroup2D90691C", + "GroupId" + ] + }, + "ToPort": 443 + } + }, "ClusterControlPlaneSecurityGroupfromawscdkeksclustertestClusterNodesInstanceSecurityGroupD0B64C54443795AF111": { "Type": "AWS::EC2::SecurityGroupIngress", "Properties": { @@ -665,6 +686,22 @@ "ToPort": 443 } }, + "ClusterKubectlProviderSecurityGroup2D90691C": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "Comminication between KubectlProvider and EKS Control Plane", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, "ClusterCreationRole360249B6": { "Type": "AWS::IAM::Role", "Properties": { @@ -705,7 +742,39 @@ ], "Version": "2012-10-17" } - } + }, + "DependsOn": [ + "ClusterKubectlProviderSecurityGroup2D90691C", + "VpcIGWD7BA715C", + "VpcPrivateSubnet1DefaultRouteBE02A9ED", + "VpcPrivateSubnet1RouteTableB2C5B500", + "VpcPrivateSubnet1RouteTableAssociation70C59FA6", + "VpcPrivateSubnet1Subnet536B997A", + "VpcPrivateSubnet2DefaultRoute060D2087", + "VpcPrivateSubnet2RouteTableA678073B", + "VpcPrivateSubnet2RouteTableAssociationA89CAD56", + "VpcPrivateSubnet2Subnet3788AAA1", + "VpcPrivateSubnet3DefaultRoute94B74F0D", + "VpcPrivateSubnet3RouteTableD98824C7", + "VpcPrivateSubnet3RouteTableAssociation16BDDC43", + "VpcPrivateSubnet3SubnetF258B56E", + "VpcPublicSubnet1DefaultRoute3DA9E72A", + "VpcPublicSubnet1EIPD7E02669", + "VpcPublicSubnet1NATGateway4D7517AA", + "VpcPublicSubnet1RouteTable6C95E38E", + "VpcPublicSubnet1RouteTableAssociation97140677", + "VpcPublicSubnet1Subnet5C2D37C4", + "VpcPublicSubnet2DefaultRoute97F91067", + "VpcPublicSubnet2RouteTable94F7E489", + "VpcPublicSubnet2RouteTableAssociationDD5762D8", + "VpcPublicSubnet2Subnet691E08A3", + "VpcPublicSubnet3DefaultRoute4697774F", + "VpcPublicSubnet3RouteTable93458DBB", + "VpcPublicSubnet3RouteTableAssociation1F1EDF02", + "VpcPublicSubnet3SubnetBE12F0B6", + "Vpc8378EB38", + "VpcVPCGWBF912B6E" + ] }, "ClusterCreationRoleDefaultPolicyE8BDFC7B": { "Type": "AWS::IAM::Policy", @@ -806,7 +875,39 @@ "Ref": "ClusterCreationRole360249B6" } ] - } + }, + "DependsOn": [ + "ClusterKubectlProviderSecurityGroup2D90691C", + "VpcIGWD7BA715C", + "VpcPrivateSubnet1DefaultRouteBE02A9ED", + "VpcPrivateSubnet1RouteTableB2C5B500", + "VpcPrivateSubnet1RouteTableAssociation70C59FA6", + "VpcPrivateSubnet1Subnet536B997A", + "VpcPrivateSubnet2DefaultRoute060D2087", + "VpcPrivateSubnet2RouteTableA678073B", + "VpcPrivateSubnet2RouteTableAssociationA89CAD56", + "VpcPrivateSubnet2Subnet3788AAA1", + "VpcPrivateSubnet3DefaultRoute94B74F0D", + "VpcPrivateSubnet3RouteTableD98824C7", + "VpcPrivateSubnet3RouteTableAssociation16BDDC43", + "VpcPrivateSubnet3SubnetF258B56E", + "VpcPublicSubnet1DefaultRoute3DA9E72A", + "VpcPublicSubnet1EIPD7E02669", + "VpcPublicSubnet1NATGateway4D7517AA", + "VpcPublicSubnet1RouteTable6C95E38E", + "VpcPublicSubnet1RouteTableAssociation97140677", + "VpcPublicSubnet1Subnet5C2D37C4", + "VpcPublicSubnet2DefaultRoute97F91067", + "VpcPublicSubnet2RouteTable94F7E489", + "VpcPublicSubnet2RouteTableAssociationDD5762D8", + "VpcPublicSubnet2Subnet691E08A3", + "VpcPublicSubnet3DefaultRoute4697774F", + "VpcPublicSubnet3RouteTable93458DBB", + "VpcPublicSubnet3RouteTableAssociation1F1EDF02", + "VpcPublicSubnet3SubnetBE12F0B6", + "Vpc8378EB38", + "VpcVPCGWBF912B6E" + ] }, "Cluster9EE0221C": { "Type": "Custom::AWSCDK-EKS-Cluster", @@ -818,22 +919,14 @@ ] }, "Config": { + "version": "1.16", "roleArn": { "Fn::GetAtt": [ "ClusterRoleFA261979", "Arn" ] }, - "version": "1.16", "resourcesVpcConfig": { - "securityGroupIds": [ - { - "Fn::GetAtt": [ - "ClusterControlPlaneSecurityGroupD274242C", - "GroupId" - ] - } - ], "subnetIds": [ { "Ref": "VpcPublicSubnet1Subnet5C2D37C4" @@ -853,7 +946,17 @@ { "Ref": "VpcPrivateSubnet3SubnetF258B56E" } - ] + ], + "securityGroupIds": [ + { + "Fn::GetAtt": [ + "ClusterControlPlaneSecurityGroupD274242C", + "GroupId" + ] + } + ], + "endpointPublicAccess": true, + "endpointPrivateAccess": true } }, "AssumeRoleArn": { @@ -865,8 +968,38 @@ "AttributesRevision": 2 }, "DependsOn": [ + "ClusterKubectlProviderSecurityGroup2D90691C", "ClusterCreationRoleDefaultPolicyE8BDFC7B", - "ClusterCreationRole360249B6" + "ClusterCreationRole360249B6", + "VpcIGWD7BA715C", + "VpcPrivateSubnet1DefaultRouteBE02A9ED", + "VpcPrivateSubnet1RouteTableB2C5B500", + "VpcPrivateSubnet1RouteTableAssociation70C59FA6", + "VpcPrivateSubnet1Subnet536B997A", + "VpcPrivateSubnet2DefaultRoute060D2087", + "VpcPrivateSubnet2RouteTableA678073B", + "VpcPrivateSubnet2RouteTableAssociationA89CAD56", + "VpcPrivateSubnet2Subnet3788AAA1", + "VpcPrivateSubnet3DefaultRoute94B74F0D", + "VpcPrivateSubnet3RouteTableD98824C7", + "VpcPrivateSubnet3RouteTableAssociation16BDDC43", + "VpcPrivateSubnet3SubnetF258B56E", + "VpcPublicSubnet1DefaultRoute3DA9E72A", + "VpcPublicSubnet1EIPD7E02669", + "VpcPublicSubnet1NATGateway4D7517AA", + "VpcPublicSubnet1RouteTable6C95E38E", + "VpcPublicSubnet1RouteTableAssociation97140677", + "VpcPublicSubnet1Subnet5C2D37C4", + "VpcPublicSubnet2DefaultRoute97F91067", + "VpcPublicSubnet2RouteTable94F7E489", + "VpcPublicSubnet2RouteTableAssociationDD5762D8", + "VpcPublicSubnet2Subnet691E08A3", + "VpcPublicSubnet3DefaultRoute4697774F", + "VpcPublicSubnet3RouteTable93458DBB", + "VpcPublicSubnet3RouteTableAssociation1F1EDF02", + "VpcPublicSubnet3SubnetBE12F0B6", + "Vpc8378EB38", + "VpcVPCGWBF912B6E" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -2724,7 +2857,7 @@ }, "/", { - "Ref": "AssetParameters5215f685494c7a295ec1b06b713a041a82e7ac216473965711a88e32405e9053S3Bucket50B33A86" + "Ref": "AssetParameterse8f5d2a182613ad64e98c81d59e2ad3ecb46c92c5b51c3612a5c614a0715e57bS3Bucket393DA96E" }, "/", { @@ -2734,7 +2867,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters5215f685494c7a295ec1b06b713a041a82e7ac216473965711a88e32405e9053S3VersionKey1FB82B13" + "Ref": "AssetParameterse8f5d2a182613ad64e98c81d59e2ad3ecb46c92c5b51c3612a5c614a0715e57bS3VersionKey0633C6DF" } ] } @@ -2747,7 +2880,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters5215f685494c7a295ec1b06b713a041a82e7ac216473965711a88e32405e9053S3VersionKey1FB82B13" + "Ref": "AssetParameterse8f5d2a182613ad64e98c81d59e2ad3ecb46c92c5b51c3612a5c614a0715e57bS3VersionKey0633C6DF" } ] } @@ -2757,17 +2890,17 @@ ] }, "Parameters": { - "referencetoawscdkeksclustertestAssetParametersc23ce59a47ffb1e28812148fb83f7dcb0d94f1f0286e122a2f1aa189c0b35d03S3BucketD5010C93Ref": { - "Ref": "AssetParametersc23ce59a47ffb1e28812148fb83f7dcb0d94f1f0286e122a2f1aa189c0b35d03S3Bucket2F8CA18B" + "referencetoawscdkeksclustertestAssetParameters00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791S3Bucket363F6F79Ref": { + "Ref": "AssetParameters00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791S3Bucket26C90BA0" }, - "referencetoawscdkeksclustertestAssetParametersc23ce59a47ffb1e28812148fb83f7dcb0d94f1f0286e122a2f1aa189c0b35d03S3VersionKeyAC8DDB71Ref": { - "Ref": "AssetParametersc23ce59a47ffb1e28812148fb83f7dcb0d94f1f0286e122a2f1aa189c0b35d03S3VersionKeyEFEE8BE5" + "referencetoawscdkeksclustertestAssetParameters00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791S3VersionKeyDC22C51CRef": { + "Ref": "AssetParameters00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791S3VersionKeyD269C675" }, - "referencetoawscdkeksclustertestAssetParameters956c2f92ddbde06f551fdf914445c679dcadb21c6e8d1ee9c9632144ef5a2ad3S3Bucket8E231383Ref": { - "Ref": "AssetParameters956c2f92ddbde06f551fdf914445c679dcadb21c6e8d1ee9c9632144ef5a2ad3S3Bucket0EEA1C2E" + "referencetoawscdkeksclustertestAssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3BucketA9A24CF5Ref": { + "Ref": "AssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3BucketF1BD2256" }, - "referencetoawscdkeksclustertestAssetParameters956c2f92ddbde06f551fdf914445c679dcadb21c6e8d1ee9c9632144ef5a2ad3S3VersionKey33D81F32Ref": { - "Ref": "AssetParameters956c2f92ddbde06f551fdf914445c679dcadb21c6e8d1ee9c9632144ef5a2ad3S3VersionKey7BCE18C9" + "referencetoawscdkeksclustertestAssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3VersionKey6036F880Ref": { + "Ref": "AssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3VersionKeyF47FA401" } } } @@ -2785,7 +2918,7 @@ }, "/", { - "Ref": "AssetParameters2181e1ea22ea11a566260dec2f26c5f66ac77bb1b73812ba467b9c3bc564e42bS3BucketBEF9DA08" + "Ref": "AssetParameters5db67dc64d67f3574c3c3e10970910e121e77f67974ab320c4dc47af2f88d2feS3Bucket864A12C7" }, "/", { @@ -2795,7 +2928,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters2181e1ea22ea11a566260dec2f26c5f66ac77bb1b73812ba467b9c3bc564e42bS3VersionKey8B401BBD" + "Ref": "AssetParameters5db67dc64d67f3574c3c3e10970910e121e77f67974ab320c4dc47af2f88d2feS3VersionKeyD0F4176F" } ] } @@ -2808,7 +2941,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters2181e1ea22ea11a566260dec2f26c5f66ac77bb1b73812ba467b9c3bc564e42bS3VersionKey8B401BBD" + "Ref": "AssetParameters5db67dc64d67f3574c3c3e10970910e121e77f67974ab320c4dc47af2f88d2feS3VersionKeyD0F4176F" } ] } @@ -2818,17 +2951,17 @@ ] }, "Parameters": { - "referencetoawscdkeksclustertestAssetParameters2d65340a9414c04d1844e421bd328aa3b80015d6a02e74afe9a222168b2ba050S3BucketA41B2C70Ref": { - "Ref": "AssetParameters2d65340a9414c04d1844e421bd328aa3b80015d6a02e74afe9a222168b2ba050S3Bucket0EAA682D" + "referencetoawscdkeksclustertestAssetParameters649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502S3Bucket8095B011Ref": { + "Ref": "AssetParameters649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502S3BucketE7D09A6B" }, - "referencetoawscdkeksclustertestAssetParameters2d65340a9414c04d1844e421bd328aa3b80015d6a02e74afe9a222168b2ba050S3VersionKey4E1E47F7Ref": { - "Ref": "AssetParameters2d65340a9414c04d1844e421bd328aa3b80015d6a02e74afe9a222168b2ba050S3VersionKeyF3400812" + "referencetoawscdkeksclustertestAssetParameters649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502S3VersionKeyFE6DC258Ref": { + "Ref": "AssetParameters649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502S3VersionKey1DA734B2" }, - "referencetoawscdkeksclustertestAssetParameters956c2f92ddbde06f551fdf914445c679dcadb21c6e8d1ee9c9632144ef5a2ad3S3Bucket8E231383Ref": { - "Ref": "AssetParameters956c2f92ddbde06f551fdf914445c679dcadb21c6e8d1ee9c9632144ef5a2ad3S3Bucket0EEA1C2E" + "referencetoawscdkeksclustertestAssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3BucketA9A24CF5Ref": { + "Ref": "AssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3BucketF1BD2256" }, - "referencetoawscdkeksclustertestAssetParameters956c2f92ddbde06f551fdf914445c679dcadb21c6e8d1ee9c9632144ef5a2ad3S3VersionKey33D81F32Ref": { - "Ref": "AssetParameters956c2f92ddbde06f551fdf914445c679dcadb21c6e8d1ee9c9632144ef5a2ad3S3VersionKey7BCE18C9" + "referencetoawscdkeksclustertestAssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3VersionKey6036F880Ref": { + "Ref": "AssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3VersionKeyF47FA401" } } } @@ -2860,7 +2993,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters3b28f4ee261986c158a160900e3042a61238f644fe502199d60bcea592128086S3Bucket57C0655B" + "Ref": "AssetParameters952bd1c03e8201c4c1c67d6de0f3fdaaf88fda05f89a1232c3f6364343cd5344S3Bucket055DC235" }, "S3Key": { "Fn::Join": [ @@ -2873,7 +3006,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters3b28f4ee261986c158a160900e3042a61238f644fe502199d60bcea592128086S3VersionKey4BC65AD6" + "Ref": "AssetParameters952bd1c03e8201c4c1c67d6de0f3fdaaf88fda05f89a1232c3f6364343cd5344S3VersionKey2FFFA299" } ] } @@ -2886,7 +3019,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters3b28f4ee261986c158a160900e3042a61238f644fe502199d60bcea592128086S3VersionKey4BC65AD6" + "Ref": "AssetParameters952bd1c03e8201c4c1c67d6de0f3fdaaf88fda05f89a1232c3f6364343cd5344S3VersionKey2FFFA299" } ] } @@ -2959,7 +3092,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersea46702e1c05b2735e48e826d630f7bf6acdf7e55d6fa8d9fa8df858d5542161S3Bucket0C424907" + "Ref": "AssetParameterseb7a9b73a02dcd848325fc3abc22c1923c364d7480e06bd68a337dc3f33143d3S3BucketB6A9971A" }, "S3Key": { "Fn::Join": [ @@ -2972,7 +3105,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersea46702e1c05b2735e48e826d630f7bf6acdf7e55d6fa8d9fa8df858d5542161S3VersionKey6841F1F8" + "Ref": "AssetParameterseb7a9b73a02dcd848325fc3abc22c1923c364d7480e06bd68a337dc3f33143d3S3VersionKey08BBD845" } ] } @@ -2985,7 +3118,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersea46702e1c05b2735e48e826d630f7bf6acdf7e55d6fa8d9fa8df858d5542161S3VersionKey6841F1F8" + "Ref": "AssetParameterseb7a9b73a02dcd848325fc3abc22c1923c364d7480e06bd68a337dc3f33143d3S3VersionKey08BBD845" } ] } @@ -3099,89 +3232,89 @@ } }, "Parameters": { - "AssetParametersc23ce59a47ffb1e28812148fb83f7dcb0d94f1f0286e122a2f1aa189c0b35d03S3Bucket2F8CA18B": { + "AssetParameters00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791S3Bucket26C90BA0": { "Type": "String", - "Description": "S3 bucket for asset \"c23ce59a47ffb1e28812148fb83f7dcb0d94f1f0286e122a2f1aa189c0b35d03\"" + "Description": "S3 bucket for asset \"00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791\"" }, - "AssetParametersc23ce59a47ffb1e28812148fb83f7dcb0d94f1f0286e122a2f1aa189c0b35d03S3VersionKeyEFEE8BE5": { + "AssetParameters00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791S3VersionKeyD269C675": { "Type": "String", - "Description": "S3 key for asset version \"c23ce59a47ffb1e28812148fb83f7dcb0d94f1f0286e122a2f1aa189c0b35d03\"" + "Description": "S3 key for asset version \"00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791\"" }, - "AssetParametersc23ce59a47ffb1e28812148fb83f7dcb0d94f1f0286e122a2f1aa189c0b35d03ArtifactHashC187523A": { + "AssetParameters00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791ArtifactHashAADC8B03": { "Type": "String", - "Description": "Artifact hash for asset \"c23ce59a47ffb1e28812148fb83f7dcb0d94f1f0286e122a2f1aa189c0b35d03\"" + "Description": "Artifact hash for asset \"00ba02e613a29439c93f9aef4e82e253763eb70cd32026df071449485c692791\"" }, - "AssetParameters956c2f92ddbde06f551fdf914445c679dcadb21c6e8d1ee9c9632144ef5a2ad3S3Bucket0EEA1C2E": { + "AssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3BucketF1BD2256": { "Type": "String", - "Description": "S3 bucket for asset \"956c2f92ddbde06f551fdf914445c679dcadb21c6e8d1ee9c9632144ef5a2ad3\"" + "Description": "S3 bucket for asset \"974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74c\"" }, - "AssetParameters956c2f92ddbde06f551fdf914445c679dcadb21c6e8d1ee9c9632144ef5a2ad3S3VersionKey7BCE18C9": { + "AssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cS3VersionKeyF47FA401": { "Type": "String", - "Description": "S3 key for asset version \"956c2f92ddbde06f551fdf914445c679dcadb21c6e8d1ee9c9632144ef5a2ad3\"" + "Description": "S3 key for asset version \"974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74c\"" }, - "AssetParameters956c2f92ddbde06f551fdf914445c679dcadb21c6e8d1ee9c9632144ef5a2ad3ArtifactHash2CBB11D2": { + "AssetParameters974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74cArtifactHash5C0B1EA0": { "Type": "String", - "Description": "Artifact hash for asset \"956c2f92ddbde06f551fdf914445c679dcadb21c6e8d1ee9c9632144ef5a2ad3\"" + "Description": "Artifact hash for asset \"974a6fb29abbd1d98fce56346da3743e79277f0f52e0e2cdf3f1867ac5b1e74c\"" }, - "AssetParameters2d65340a9414c04d1844e421bd328aa3b80015d6a02e74afe9a222168b2ba050S3Bucket0EAA682D": { + "AssetParameters649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502S3BucketE7D09A6B": { "Type": "String", - "Description": "S3 bucket for asset \"2d65340a9414c04d1844e421bd328aa3b80015d6a02e74afe9a222168b2ba050\"" + "Description": "S3 bucket for asset \"649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502\"" }, - "AssetParameters2d65340a9414c04d1844e421bd328aa3b80015d6a02e74afe9a222168b2ba050S3VersionKeyF3400812": { + "AssetParameters649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502S3VersionKey1DA734B2": { "Type": "String", - "Description": "S3 key for asset version \"2d65340a9414c04d1844e421bd328aa3b80015d6a02e74afe9a222168b2ba050\"" + "Description": "S3 key for asset version \"649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502\"" }, - "AssetParameters2d65340a9414c04d1844e421bd328aa3b80015d6a02e74afe9a222168b2ba050ArtifactHashF4CEE19F": { + "AssetParameters649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502ArtifactHash815E1969": { "Type": "String", - "Description": "Artifact hash for asset \"2d65340a9414c04d1844e421bd328aa3b80015d6a02e74afe9a222168b2ba050\"" + "Description": "Artifact hash for asset \"649b09403c8414e624c965d6c2f0e41c341c2afa5d8e7bae4ac5746fe230f502\"" }, - "AssetParameters3b28f4ee261986c158a160900e3042a61238f644fe502199d60bcea592128086S3Bucket57C0655B": { + "AssetParameters952bd1c03e8201c4c1c67d6de0f3fdaaf88fda05f89a1232c3f6364343cd5344S3Bucket055DC235": { "Type": "String", - "Description": "S3 bucket for asset \"3b28f4ee261986c158a160900e3042a61238f644fe502199d60bcea592128086\"" + "Description": "S3 bucket for asset \"952bd1c03e8201c4c1c67d6de0f3fdaaf88fda05f89a1232c3f6364343cd5344\"" }, - "AssetParameters3b28f4ee261986c158a160900e3042a61238f644fe502199d60bcea592128086S3VersionKey4BC65AD6": { + "AssetParameters952bd1c03e8201c4c1c67d6de0f3fdaaf88fda05f89a1232c3f6364343cd5344S3VersionKey2FFFA299": { "Type": "String", - "Description": "S3 key for asset version \"3b28f4ee261986c158a160900e3042a61238f644fe502199d60bcea592128086\"" + "Description": "S3 key for asset version \"952bd1c03e8201c4c1c67d6de0f3fdaaf88fda05f89a1232c3f6364343cd5344\"" }, - "AssetParameters3b28f4ee261986c158a160900e3042a61238f644fe502199d60bcea592128086ArtifactHashD8D99435": { + "AssetParameters952bd1c03e8201c4c1c67d6de0f3fdaaf88fda05f89a1232c3f6364343cd5344ArtifactHash1AB042BC": { "Type": "String", - "Description": "Artifact hash for asset \"3b28f4ee261986c158a160900e3042a61238f644fe502199d60bcea592128086\"" + "Description": "Artifact hash for asset \"952bd1c03e8201c4c1c67d6de0f3fdaaf88fda05f89a1232c3f6364343cd5344\"" }, - "AssetParametersea46702e1c05b2735e48e826d630f7bf6acdf7e55d6fa8d9fa8df858d5542161S3Bucket0C424907": { + "AssetParameterseb7a9b73a02dcd848325fc3abc22c1923c364d7480e06bd68a337dc3f33143d3S3BucketB6A9971A": { "Type": "String", - "Description": "S3 bucket for asset \"ea46702e1c05b2735e48e826d630f7bf6acdf7e55d6fa8d9fa8df858d5542161\"" + "Description": "S3 bucket for asset \"eb7a9b73a02dcd848325fc3abc22c1923c364d7480e06bd68a337dc3f33143d3\"" }, - "AssetParametersea46702e1c05b2735e48e826d630f7bf6acdf7e55d6fa8d9fa8df858d5542161S3VersionKey6841F1F8": { + "AssetParameterseb7a9b73a02dcd848325fc3abc22c1923c364d7480e06bd68a337dc3f33143d3S3VersionKey08BBD845": { "Type": "String", - "Description": "S3 key for asset version \"ea46702e1c05b2735e48e826d630f7bf6acdf7e55d6fa8d9fa8df858d5542161\"" + "Description": "S3 key for asset version \"eb7a9b73a02dcd848325fc3abc22c1923c364d7480e06bd68a337dc3f33143d3\"" }, - "AssetParametersea46702e1c05b2735e48e826d630f7bf6acdf7e55d6fa8d9fa8df858d5542161ArtifactHash67B22EF2": { + "AssetParameterseb7a9b73a02dcd848325fc3abc22c1923c364d7480e06bd68a337dc3f33143d3ArtifactHashADF25EB1": { "Type": "String", - "Description": "Artifact hash for asset \"ea46702e1c05b2735e48e826d630f7bf6acdf7e55d6fa8d9fa8df858d5542161\"" + "Description": "Artifact hash for asset \"eb7a9b73a02dcd848325fc3abc22c1923c364d7480e06bd68a337dc3f33143d3\"" }, - "AssetParameters5215f685494c7a295ec1b06b713a041a82e7ac216473965711a88e32405e9053S3Bucket50B33A86": { + "AssetParameterse8f5d2a182613ad64e98c81d59e2ad3ecb46c92c5b51c3612a5c614a0715e57bS3Bucket393DA96E": { "Type": "String", - "Description": "S3 bucket for asset \"5215f685494c7a295ec1b06b713a041a82e7ac216473965711a88e32405e9053\"" + "Description": "S3 bucket for asset \"e8f5d2a182613ad64e98c81d59e2ad3ecb46c92c5b51c3612a5c614a0715e57b\"" }, - "AssetParameters5215f685494c7a295ec1b06b713a041a82e7ac216473965711a88e32405e9053S3VersionKey1FB82B13": { + "AssetParameterse8f5d2a182613ad64e98c81d59e2ad3ecb46c92c5b51c3612a5c614a0715e57bS3VersionKey0633C6DF": { "Type": "String", - "Description": "S3 key for asset version \"5215f685494c7a295ec1b06b713a041a82e7ac216473965711a88e32405e9053\"" + "Description": "S3 key for asset version \"e8f5d2a182613ad64e98c81d59e2ad3ecb46c92c5b51c3612a5c614a0715e57b\"" }, - "AssetParameters5215f685494c7a295ec1b06b713a041a82e7ac216473965711a88e32405e9053ArtifactHash599411DD": { + "AssetParameterse8f5d2a182613ad64e98c81d59e2ad3ecb46c92c5b51c3612a5c614a0715e57bArtifactHashA64B37F7": { "Type": "String", - "Description": "Artifact hash for asset \"5215f685494c7a295ec1b06b713a041a82e7ac216473965711a88e32405e9053\"" + "Description": "Artifact hash for asset \"e8f5d2a182613ad64e98c81d59e2ad3ecb46c92c5b51c3612a5c614a0715e57b\"" }, - "AssetParameters2181e1ea22ea11a566260dec2f26c5f66ac77bb1b73812ba467b9c3bc564e42bS3BucketBEF9DA08": { + "AssetParameters5db67dc64d67f3574c3c3e10970910e121e77f67974ab320c4dc47af2f88d2feS3Bucket864A12C7": { "Type": "String", - "Description": "S3 bucket for asset \"2181e1ea22ea11a566260dec2f26c5f66ac77bb1b73812ba467b9c3bc564e42b\"" + "Description": "S3 bucket for asset \"5db67dc64d67f3574c3c3e10970910e121e77f67974ab320c4dc47af2f88d2fe\"" }, - "AssetParameters2181e1ea22ea11a566260dec2f26c5f66ac77bb1b73812ba467b9c3bc564e42bS3VersionKey8B401BBD": { + "AssetParameters5db67dc64d67f3574c3c3e10970910e121e77f67974ab320c4dc47af2f88d2feS3VersionKeyD0F4176F": { "Type": "String", - "Description": "S3 key for asset version \"2181e1ea22ea11a566260dec2f26c5f66ac77bb1b73812ba467b9c3bc564e42b\"" + "Description": "S3 key for asset version \"5db67dc64d67f3574c3c3e10970910e121e77f67974ab320c4dc47af2f88d2fe\"" }, - "AssetParameters2181e1ea22ea11a566260dec2f26c5f66ac77bb1b73812ba467b9c3bc564e42bArtifactHash87F44C09": { + "AssetParameters5db67dc64d67f3574c3c3e10970910e121e77f67974ab320c4dc47af2f88d2feArtifactHash2B9F340F": { "Type": "String", - "Description": "Artifact hash for asset \"2181e1ea22ea11a566260dec2f26c5f66ac77bb1b73812ba467b9c3bc564e42b\"" + "Description": "Artifact hash for asset \"5db67dc64d67f3574c3c3e10970910e121e77f67974ab320c4dc47af2f88d2fe\"" }, "SsmParameterValueawsserviceeksoptimizedami116amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { "Type": "AWS::SSM::Parameter::Value", diff --git a/packages/@aws-cdk/aws-eks/test/test.cluster.ts b/packages/@aws-cdk/aws-eks/test/test.cluster.ts index 1b87a8d94a152..58863772311c1 100644 --- a/packages/@aws-cdk/aws-eks/test/test.cluster.ts +++ b/packages/@aws-cdk/aws-eks/test/test.cluster.ts @@ -895,6 +895,8 @@ export = { { Ref: 'MyClusterDefaultVpcPrivateSubnet1SubnetE1D0DCDB' }, { Ref: 'MyClusterDefaultVpcPrivateSubnet2Subnet11FEA8D0' }, ], + endpointPrivateAccess: true, + endpointPublicAccess: true, }, }, })); @@ -1378,5 +1380,313 @@ export = { } test.done(); }, + + }, + + 'kubectl provider passes environment to lambda'(test: Test) { + + const { stack } = testFixture(); + + const cluster = new eks.Cluster(stack, 'Cluster1', { + version: CLUSTER_VERSION, endpointAccess: eks.EndpointAccess.PRIVATE, + kubectlEnvironment: { + Foo: 'Bar', + }, + }); + + cluster.addResource('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).to(haveResource('AWS::Lambda::Function', { + Environment: { + Variables: { + Foo: 'Bar', + }, + }, + })); + + test.done(); + }, + + 'endpoint access': { + + 'can configure private endpoint access'(test: Test) { + // GIVEN + const { stack } = testFixture(); + new eks.Cluster(stack, 'Cluster1', { version: CLUSTER_VERSION, endpointAccess: eks.EndpointAccess.PRIVATE }); + + expect(stack).to(haveResource('Custom::AWSCDK-EKS-Cluster', { + Config: { + roleArn: { 'Fn::GetAtt': ['Cluster1RoleE88C32AD', 'Arn'] }, + version: '1.16', + resourcesVpcConfig: { + securityGroupIds: [{ 'Fn::GetAtt': ['Cluster1ControlPlaneSecurityGroupF9C67C32', 'GroupId'] }], + subnetIds: [ + { Ref: 'Cluster1DefaultVpcPublicSubnet1SubnetBEABA6ED' }, + { Ref: 'Cluster1DefaultVpcPublicSubnet2Subnet947A5158' }, + { Ref: 'Cluster1DefaultVpcPrivateSubnet1Subnet4E30ECA1' }, + { Ref: 'Cluster1DefaultVpcPrivateSubnet2Subnet707FCD37' }, + ], + endpointPrivateAccess: true, + endpointPublicAccess: false, + }, + }, + })); + + test.done(); + }, + + 'can configure cidr blocks in public endpoint access'(test: Test) { + // GIVEN + const { stack } = testFixture(); + new eks.Cluster(stack, 'Cluster1', { version: CLUSTER_VERSION, endpointAccess: eks.EndpointAccess.PUBLIC.onlyFrom('1.2.3.4/5') }); + + expect(stack).to(haveResource('Custom::AWSCDK-EKS-Cluster', { + Config: { + roleArn: { 'Fn::GetAtt': ['Cluster1RoleE88C32AD', 'Arn'] }, + version: '1.16', + resourcesVpcConfig: { + securityGroupIds: [{ 'Fn::GetAtt': ['Cluster1ControlPlaneSecurityGroupF9C67C32', 'GroupId'] }], + subnetIds: [ + { Ref: 'Cluster1DefaultVpcPublicSubnet1SubnetBEABA6ED' }, + { Ref: 'Cluster1DefaultVpcPublicSubnet2Subnet947A5158' }, + { Ref: 'Cluster1DefaultVpcPrivateSubnet1Subnet4E30ECA1' }, + { Ref: 'Cluster1DefaultVpcPrivateSubnet2Subnet707FCD37' }, + ], + endpointPrivateAccess: false, + endpointPublicAccess: true, + publicAccessCidrs: ['1.2.3.4/5'], + }, + }, + })); + + test.done(); + }, + + 'kubectl provider chooses only private subnets'(test: Test) { + + const { stack } = testFixture(); + + const vpc = new ec2.Vpc(stack, 'Vpc', { + maxAzs: 2, + natGateways: 1, + subnetConfiguration: [ + { + subnetType: ec2.SubnetType.PRIVATE, + name: 'Private1', + }, + { + subnetType: ec2.SubnetType.PUBLIC, + name: 'Public1', + }, + ], + }); + + const cluster = new eks.Cluster(stack, 'Cluster1', { + version: CLUSTER_VERSION, endpointAccess: eks.EndpointAccess.PRIVATE, + vpc, + }); + + cluster.addResource('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).to(haveResource('AWS::Lambda::Function', { + VpcConfig: { + SecurityGroupIds: [ + { + Ref: 'referencetoStackCluster1KubectlProviderSecurityGroupDF05D03AGroupId', + }, + ], + SubnetIds: [ + { + Ref: 'referencetoStackVpcPrivate1Subnet1Subnet6764A0F6Ref', + }, + { + Ref: 'referencetoStackVpcPrivate1Subnet2SubnetDFD49645Ref', + }, + ], + }, + })); + + test.done(); + }, + + 'kubectl provider limits number of subnets to 16'(test: Test) { + + const { stack } = testFixture(); + + const subnetConfiguration: ec2.SubnetConfiguration[] = []; + + for (let i = 0; i < 20; i++) { + subnetConfiguration.push( { + subnetType: ec2.SubnetType.PRIVATE, + name: `Private${i}`, + }, + ); + } + + subnetConfiguration.push( { + subnetType: ec2.SubnetType.PUBLIC, + name: 'Public1', + }); + + const vpc2 = new ec2.Vpc(stack, 'Vpc', { + maxAzs: 2, + natGateways: 1, + subnetConfiguration, + }); + + const cluster = new eks.Cluster(stack, 'Cluster1', { + version: CLUSTER_VERSION, endpointAccess: eks.EndpointAccess.PRIVATE, + vpc: vpc2, + }); + + cluster.addResource('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; + test.equal(16, expect(nested).value.Resources.Handler886CB40B.Properties.VpcConfig.SubnetIds.length); + + test.done(); + }, + + 'kubectl provider considers vpc subnet selection'(test: Test) { + + const { stack } = testFixture(); + + const subnetConfiguration: ec2.SubnetConfiguration[] = []; + + for (let i = 0; i < 20; i++) { + subnetConfiguration.push( { + subnetType: ec2.SubnetType.PRIVATE, + name: `Private${i}`, + }, + ); + } + + subnetConfiguration.push( { + subnetType: ec2.SubnetType.PUBLIC, + name: 'Public1', + }); + + const vpc2 = new ec2.Vpc(stack, 'Vpc', { + maxAzs: 2, + natGateways: 1, + subnetConfiguration, + }); + + const cluster = new eks.Cluster(stack, 'Cluster1', { + version: CLUSTER_VERSION, endpointAccess: eks.EndpointAccess.PRIVATE, + vpc: vpc2, + vpcSubnets: [{subnetGroupName: 'Private1'}, {subnetGroupName: 'Private2'}], + }); + + cluster.addResource('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).to(haveResource('AWS::Lambda::Function', { + VpcConfig: { + SecurityGroupIds: [ + { + Ref: 'referencetoStackCluster1KubectlProviderSecurityGroupDF05D03AGroupId', + }, + ], + SubnetIds: [ + { + Ref: 'referencetoStackVpcPrivate1Subnet1Subnet6764A0F6Ref', + }, + { + Ref: 'referencetoStackVpcPrivate1Subnet2SubnetDFD49645Ref', + }, + { + Ref: 'referencetoStackVpcPrivate2Subnet1Subnet586AD392Ref', + }, + { + Ref: 'referencetoStackVpcPrivate2Subnet2SubnetE42148C0Ref', + }, + ], + }, + })); + + test.done(); + }, + + 'throw when private access is configured without dns support enabled for the VPC'(test: Test) { + + const { stack } = testFixture(); + + test.throws(() => { + new eks.Cluster(stack, 'Cluster', { + vpc: new ec2.Vpc(stack, 'Vpc', { + enableDnsSupport: false, + }), + version: CLUSTER_VERSION, + }); + }, /Private endpoint access requires the VPC to have DNS support and DNS hostnames enabled/); + test.done(); + }, + + 'throw when private access is configured without dns hostnames enabled for the VPC'(test: Test) { + + const { stack } = testFixture(); + + test.throws(() => { + new eks.Cluster(stack, 'Cluster', { + vpc: new ec2.Vpc(stack, 'Vpc', { + enableDnsHostnames: false, + }), + version: CLUSTER_VERSION, + }); + }, /Private endpoint access requires the VPC to have DNS support and DNS hostnames enabled/); + test.done(); + }, + + 'throw when cidrs are configured without public access endpoint'(test: Test) { + + test.throws(() => { + eks.EndpointAccess.PRIVATE.onlyFrom('1.2.3.4/5'); + }, /CIDR blocks can only be configured when public access is enabled/); + test.done(); + }, + }, };