Skip to content
v2.273.0

Amazon EKS Construct Library ​

This construct library allows you to define Amazon Elastic Container Service for Kubernetes (EKS) clusters. In addition, the library also supports defining Kubernetes resource manifests within EKS clusters.

Quick Start ​

This example defines an Amazon EKS cluster with the following configuration:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

// provisioning a cluster
const cluster = new eks.Cluster(this, 'hello-eks', {
  version: eks.KubernetesVersion.V1_36,
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});

// apply a kubernetes manifest to the cluster
cluster.addManifest('mypod', {
  apiVersion: 'v1',
  kind: 'Pod',
  metadata: { name: 'mypod' },
  spec: {
    containers: [
      {
        name: 'hello',
        image: 'paulbouwer/hello-kubernetes:1.5',
        ports: [ { containerPort: 8080 } ],
      },
    ],
  },
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


# provisioning a cluster
cluster = eks.Cluster(self, "hello-eks",
    version=eks.KubernetesVersion.V1_36,
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)

# apply a kubernetes manifest to the cluster
cluster.add_manifest("mypod", {
    "api_version": "v1",
    "kind": "Pod",
    "metadata": {"name": "mypod"},
    "spec": {
        "containers": [{
            "name": "hello",
            "image": "paulbouwer/hello-kubernetes:1.5",
            "ports": [{"container_port": 8080}]
        }
        ]
    }
})
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


// provisioning a cluster
Cluster cluster = Cluster.Builder.create(this, "hello-eks")
        .version(KubernetesVersion.V1_36)
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();

// apply a kubernetes manifest to the cluster
cluster.addManifest("mypod", Map.of(
        "apiVersion", "v1",
        "kind", "Pod",
        "metadata", Map.of("name", "mypod"),
        "spec", Map.of(
                "containers", List.of(Map.of(
                        "name", "hello",
                        "image", "paulbouwer/hello-kubernetes:1.5",
                        "ports", List.of(Map.of("containerPort", 8080)))))));
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


// provisioning a cluster
var cluster = new Cluster(this, "hello-eks", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});

// apply a kubernetes manifest to the cluster
cluster.AddManifest("mypod", new Dictionary<string, object> {
    { "apiVersion", "v1" },
    { "kind", "Pod" },
    { "metadata", new Dictionary<string, string> { { "name", "mypod" } } },
    { "spec", new Dictionary<string, IDictionary<string, object>[]> {
        { "containers", new [] { new Struct {
            Name = "hello",
            Image = "paulbouwer/hello-kubernetes:1.5",
            Ports = new [] { new Struct { ContainerPort = 8080 } }
        } } }
    } }
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


// provisioning a cluster
cluster := eks.NewCluster(this, jsii.String("hello-eks"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

// apply a kubernetes manifest to the cluster
cluster.addManifest(jsii.String("mypod"), map[string]interface{}{
	"apiVersion": jsii.String("v1"),
	"kind": jsii.String("Pod"),
	"metadata": map[string]*string{
		"name": jsii.String("mypod"),
	},
	"spec": map[string][]map[string]interface{}{
		"containers": []map[string]interface{}{
			map[string]interface{}{
				"name": jsii.String("hello"),
				"image": jsii.String("paulbouwer/hello-kubernetes:1.5"),
				"ports": []map[string]*f64{
					map[string]*f64{
						"containerPort": jsii.Number(8080),
					},
				},
			},
		},
	},
})

Architectural Overview ​

The following is a qualitative diagram of the various possible components involved in the cluster deployment.

text
 +-----------------------------------------------+               +-----------------+
 | EKS Cluster | kubectl |  |
 | ----------- |<-------------+| Kubectl Handler |
 |                                               |               |                 |
 |                                               |               +-----------------+
 | +--------------------+    +-----------------+ |
 | |                    |    |                 | |
 | | Managed Node Group |    | Fargate Profile | |               +-----------------+
 | |                    |    |                 | |               |                 |
 | +--------------------+    +-----------------+ |               | Cluster Handler |
 |                                               |               |                 |
 +-----------------------------------------------+               +-----------------+
    ^                                   ^                          +
    |                                   |                          |
    | connect self managed capacity     |                          | aws-sdk
    |                                   | create/update/delete     |
    +                                   |                          v
 +--------------------+                 +              +-------------------+
 |                    |                 --------------+| eks.amazonaws.com |
 | Auto Scaling Group |                                +-------------------+
 |                    |
 +--------------------+

In a nutshell:

  • EKS Cluster - The cluster endpoint created by EKS.
  • Managed Node Group - EC2 worker nodes managed by EKS.
  • Fargate Profile - Fargate worker nodes managed by EKS.
  • Auto Scaling Group - EC2 worker nodes managed by the user.
  • KubectlHandler - Lambda function for invoking kubectl commands on the cluster - created by CDK.
  • ClusterHandler - Lambda function for interacting with EKS API to manage the cluster lifecycle - created by CDK.

A more detailed breakdown of each is provided further down this README.

Provisioning clusters ​

Creating a new cluster is done using the Cluster or FargateCluster constructs. The only required properties are the kubernetes version and kubectlLayer.

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

new eks.Cluster(this, 'HelloEKS', {
  version: eks.KubernetesVersion.V1_36,
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


eks.Cluster(self, "HelloEKS",
    version=eks.KubernetesVersion.V1_36,
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


Cluster.Builder.create(this, "HelloEKS")
        .version(KubernetesVersion.V1_36)
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


new Cluster(this, "HelloEKS", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

You can control what happens to the resources created by the cluster construct when they are no longer managed by CloudFormation by specifying a removalPolicy.

This can happen in one of three situations:

  • The resource is removed from the template, so CloudFormation stops managing it;
  • A change to the resource is made that requires it to be replaced, so CloudFormation stops managing it;
  • The stack is deleted, so CloudFormation stops managing all resources in it.

This affects the EKS cluster itself, the custom resource that created the cluster, associated IAM roles, node groups, security groups, VPC and any other CloudFormation resources managed by this construct.

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';
import * as core from 'aws-cdk-lib/core';

new eks.Cluster(this, 'HelloEKS', {
  version: eks.KubernetesVersion.V1_36,
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
  removalPolicy: core.RemovalPolicy.RETAIN, // Keep all resources created by the construct.
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer
import aws_cdk as core


eks.Cluster(self, "HelloEKS",
    version=eks.KubernetesVersion.V1_36,
    kubectl_layer=KubectlV36Layer(self, "kubectl"),
    removal_policy=core.RemovalPolicy.RETAIN
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;
import software.amazon.awscdk.*;


Cluster.Builder.create(this, "HelloEKS")
        .version(KubernetesVersion.V1_36)
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .removalPolicy(RemovalPolicy.RETAIN)
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;
using Amazon.CDK;


new Cluster(this, "HelloEKS", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    KubectlLayer = new KubectlV36Layer(this, "kubectl"),
    RemovalPolicy = RemovalPolicy.RETAIN
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"
import core "github.com/aws/aws-cdk-go/awscdk"


eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
	RemovalPolicy: core.RemovalPolicy_RETAIN,
})

You can also use FargateCluster to provision a cluster that uses only fargate workers.

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

new eks.FargateCluster(this, 'HelloEKS', {
  version: eks.KubernetesVersion.V1_36,
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


eks.FargateCluster(self, "HelloEKS",
    version=eks.KubernetesVersion.V1_36,
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


FargateCluster.Builder.create(this, "HelloEKS")
        .version(KubernetesVersion.V1_36)
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


new FargateCluster(this, "HelloEKS", new FargateClusterProps {
    Version = KubernetesVersion.V1_36,
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


eks.NewFargateCluster(this, jsii.String("HelloEKS"), &FargateClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

You can enable deletion protection for your cluster to prevent accidental deletion. When deletion protection is enabled, the cluster cannot be deleted until protection is disabled. This setting only applies to clusters in an active state.

For more details visit Deletion protection.

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

new eks.Cluster(this, 'HelloEKS', {
  version: eks.KubernetesVersion.V1_36,
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
  deletionProtection: true,
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


eks.Cluster(self, "HelloEKS",
    version=eks.KubernetesVersion.V1_36,
    kubectl_layer=KubectlV36Layer(self, "kubectl"),
    deletion_protection=True
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


Cluster.Builder.create(this, "HelloEKS")
        .version(KubernetesVersion.V1_36)
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .deletionProtection(true)
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


new Cluster(this, "HelloEKS", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    KubectlLayer = new KubectlV36Layer(this, "kubectl"),
    DeletionProtection = true
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
	DeletionProtection: jsii.Boolean(true),
})

Provisioned Control Plane ​

Amazon EKS Provisioned Control Plane allows you to select a scaling tier to ensure high and predictable performance for demanding workloads such as AI training/inference, high-performance computing, or large-scale data processing.

ts
import { KubectlV35Layer } from '@aws-cdk/lambda-layer-kubectl-v35';

new eks.Cluster(this, 'HighPerformanceCluster', {
  version: eks.KubernetesVersion.V1_35,
  kubectlLayer: new KubectlV35Layer(this, 'kubectl'),
  controlPlaneScalingTier: eks.ControlPlaneScalingTier.TIER_XL,
});
python
from aws_cdk.lambda_layer_kubectl_v35 import KubectlV35Layer


eks.Cluster(self, "HighPerformanceCluster",
    version=eks.KubernetesVersion.V1_35,
    kubectl_layer=KubectlV35Layer(self, "kubectl"),
    control_plane_scaling_tier=eks.ControlPlaneScalingTier.TIER_XL
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v35.KubectlV35Layer;


Cluster.Builder.create(this, "HighPerformanceCluster")
        .version(KubernetesVersion.V1_35)
        .kubectlLayer(new KubectlV35Layer(this, "kubectl"))
        .controlPlaneScalingTier(ControlPlaneScalingTier.TIER_XL)
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV35;


new Cluster(this, "HighPerformanceCluster", new ClusterProps {
    Version = KubernetesVersion.V1_35,
    KubectlLayer = new KubectlV35Layer(this, "kubectl"),
    ControlPlaneScalingTier = ControlPlaneScalingTier.TIER_XL
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv35"


eks.NewCluster(this, jsii.String("HighPerformanceCluster"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_35(),
	KubectlLayer: kubectlv35.NewKubectlV35Layer(this, jsii.String("kubectl")),
	ControlPlaneScalingTier: eks.ControlPlaneScalingTier_TIER_XL,
})

Available scaling tiers:

  • STANDARD - Standard control plane (default, no additional cost)
  • TIER_XL - Extra-large provisioned tier
  • TIER_2XL - 2x extra-large provisioned tier
  • TIER_4XL - 4x extra-large provisioned tier
  • TIER_8XL - 8x extra-large provisioned tier

For more details visit Amazon EKS Provisioned Control Plane.

NOTE: Only 1 cluster per stack is supported. If you have a use-case for multiple clusters per stack, or would like to understand more about this limitation, see <https://github.com/aws/aws-cdk/issues/10073>.

Below you'll find a few important cluster configuration options. First of which is Capacity. Capacity is the amount and the type of worker nodes that are available to the cluster for deploying resources. Amazon EKS offers 3 ways of configuring capacity, which you can combine as you like:

Managed node groups ​

Amazon EKS managed node groups automate the provisioning and lifecycle management of nodes (Amazon EC2 instances) for Amazon EKS Kubernetes clusters. With Amazon EKS managed node groups, you don't need to separately provision or register the Amazon EC2 instances that provide compute capacity to run your Kubernetes applications. You can create, update, or terminate nodes for your cluster with a single operation. Nodes run using the latest Amazon EKS optimized AMIs in your AWS account while node updates and terminations gracefully drain nodes to ensure that your applications stay available.

For more details visit Amazon EKS Managed Node Groups.

Managed Node Groups are the recommended way to allocate cluster capacity.

By default, this library will allocate a managed node group with 2 m5.large instances (this instance type suits most common use-cases, and is good value for money).

At cluster instantiation time, you can customize the number of instances and their type:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

new eks.Cluster(this, 'HelloEKS', {
  version: eks.KubernetesVersion.V1_36,
  defaultCapacity: 5,
  defaultCapacityInstance: ec2.InstanceType.of(ec2.InstanceClass.M5, ec2.InstanceSize.SMALL),
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


eks.Cluster(self, "HelloEKS",
    version=eks.KubernetesVersion.V1_36,
    default_capacity=5,
    default_capacity_instance=ec2.InstanceType.of(ec2.InstanceClass.M5, ec2.InstanceSize.SMALL),
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


Cluster.Builder.create(this, "HelloEKS")
        .version(KubernetesVersion.V1_36)
        .defaultCapacity(5)
        .defaultCapacityInstance(InstanceType.of(InstanceClass.M5, InstanceSize.SMALL))
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


new Cluster(this, "HelloEKS", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    DefaultCapacity = 5,
    DefaultCapacityInstance = InstanceType.Of(InstanceClass.M5, InstanceSize.SMALL),
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	DefaultCapacity: jsii.Number(5),
	DefaultCapacityInstance: ec2.InstanceType_Of(ec2.InstanceClass_M5, ec2.InstanceSize_SMALL),
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

To access the node group that was created on your behalf, you can use cluster.defaultNodegroup.

Additional customizations are available post instantiation. To apply them, set the default capacity to 0, and use the cluster.addNodegroupCapacity method:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

const cluster = new eks.Cluster(this, 'HelloEKS', {
  version: eks.KubernetesVersion.V1_36,
  defaultCapacity: 0,
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});

cluster.addNodegroupCapacity('custom-node-group', {
  instanceTypes: [new ec2.InstanceType('m5.large')],
  minSize: 4,
  diskSize: 100,
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


cluster = eks.Cluster(self, "HelloEKS",
    version=eks.KubernetesVersion.V1_36,
    default_capacity=0,
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)

cluster.add_nodegroup_capacity("custom-node-group",
    instance_types=[ec2.InstanceType("m5.large")],
    min_size=4,
    disk_size=100
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


Cluster cluster = Cluster.Builder.create(this, "HelloEKS")
        .version(KubernetesVersion.V1_36)
        .defaultCapacity(0)
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();

cluster.addNodegroupCapacity("custom-node-group", NodegroupOptions.builder()
        .instanceTypes(List.of(new InstanceType("m5.large")))
        .minSize(4)
        .diskSize(100)
        .build());
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


var cluster = new Cluster(this, "HelloEKS", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    DefaultCapacity = 0,
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});

cluster.AddNodegroupCapacity("custom-node-group", new NodegroupOptions {
    InstanceTypes = new [] { new InstanceType("m5.large") },
    MinSize = 4,
    DiskSize = 100
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


cluster := eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	DefaultCapacity: jsii.Number(0),
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

cluster.AddNodegroupCapacity(jsii.String("custom-node-group"), &NodegroupOptions{
	InstanceTypes: []InstanceType{
		ec2.NewInstanceType(jsii.String("m5.large")),
	},
	MinSize: jsii.Number(4),
	DiskSize: jsii.Number(100),
})

To set node taints, you can set taints option.

ts
declare const cluster: eks.Cluster;
cluster.addNodegroupCapacity('custom-node-group', {
  instanceTypes: [new ec2.InstanceType('m5.large')],
  taints: [
    {
      effect: eks.TaintEffect.NO_SCHEDULE,
      key: 'foo',
      value: 'bar',
    },
  ],
});
python
# cluster: eks.Cluster

cluster.add_nodegroup_capacity("custom-node-group",
    instance_types=[ec2.InstanceType("m5.large")],
    taints=[eks.TaintSpec(
        effect=eks.TaintEffect.NO_SCHEDULE,
        key="foo",
        value="bar"
    )
    ]
)
java
Cluster cluster;

cluster.addNodegroupCapacity("custom-node-group", NodegroupOptions.builder()
        .instanceTypes(List.of(new InstanceType("m5.large")))
        .taints(List.of(TaintSpec.builder()
                .effect(TaintEffect.NO_SCHEDULE)
                .key("foo")
                .value("bar")
                .build()))
        .build());
csharp
Cluster cluster;

cluster.AddNodegroupCapacity("custom-node-group", new NodegroupOptions {
    InstanceTypes = new [] { new InstanceType("m5.large") },
    Taints = new [] { new TaintSpec {
        Effect = TaintEffect.NO_SCHEDULE,
        Key = "foo",
        Value = "bar"
    } }
});
go
var cluster Cluster

cluster.AddNodegroupCapacity(jsii.String("custom-node-group"), &NodegroupOptions{
	InstanceTypes: []InstanceType{
		ec2.NewInstanceType(jsii.String("m5.large")),
	},
	Taints: []TaintSpec{
		&TaintSpec{
			Effect: eks.TaintEffect_NO_SCHEDULE,
			Key: jsii.String("foo"),
			Value: jsii.String("bar"),
		},
	},
})

To define the type of the AMI for the node group, you may explicitly define amiType according to your requirements, supported amiType could be found HERE.

ts
declare const cluster: eks.Cluster;

// X86_64 based AMI managed node group
cluster.addNodegroupCapacity('custom-node-group', {
  instanceTypes: [new ec2.InstanceType('m5.large')], // NOTE: if amiType is x86_64-based image, the instance types here must be x86_64-based.
  amiType: eks.NodegroupAmiType.AL2023_X86_64_STANDARD,
});

// ARM_64 based AMI managed node group
cluster.addNodegroupCapacity('custom-node-group', {
  instanceTypes: [new ec2.InstanceType('m6g.medium')], // NOTE: if amiType is ARM-based image, the instance types here must be ARM-based.
  amiType: eks.NodegroupAmiType.AL2023_ARM_64_STANDARD,
});
python
# cluster: eks.Cluster


# X86_64 based AMI managed node group
cluster.add_nodegroup_capacity("custom-node-group",
    instance_types=[ec2.InstanceType("m5.large")],  # NOTE: if amiType is x86_64-based image, the instance types here must be x86_64-based.
    ami_type=eks.NodegroupAmiType.AL2023_X86_64_STANDARD
)

# ARM_64 based AMI managed node group
cluster.add_nodegroup_capacity("custom-node-group",
    instance_types=[ec2.InstanceType("m6g.medium")],  # NOTE: if amiType is ARM-based image, the instance types here must be ARM-based.
    ami_type=eks.NodegroupAmiType.AL2023_ARM_64_STANDARD
)
java
Cluster cluster;


// X86_64 based AMI managed node group
cluster.addNodegroupCapacity("custom-node-group", NodegroupOptions.builder()
        .instanceTypes(List.of(new InstanceType("m5.large"))) // NOTE: if amiType is x86_64-based image, the instance types here must be x86_64-based.
        .amiType(NodegroupAmiType.AL2023_X86_64_STANDARD)
        .build());

// ARM_64 based AMI managed node group
cluster.addNodegroupCapacity("custom-node-group", NodegroupOptions.builder()
        .instanceTypes(List.of(new InstanceType("m6g.medium"))) // NOTE: if amiType is ARM-based image, the instance types here must be ARM-based.
        .amiType(NodegroupAmiType.AL2023_ARM_64_STANDARD)
        .build());
csharp
Cluster cluster;


// X86_64 based AMI managed node group
cluster.AddNodegroupCapacity("custom-node-group", new NodegroupOptions {
    InstanceTypes = new [] { new InstanceType("m5.large") },  // NOTE: if amiType is x86_64-based image, the instance types here must be x86_64-based.
    AmiType = NodegroupAmiType.AL2023_X86_64_STANDARD
});

// ARM_64 based AMI managed node group
cluster.AddNodegroupCapacity("custom-node-group", new NodegroupOptions {
    InstanceTypes = new [] { new InstanceType("m6g.medium") },  // NOTE: if amiType is ARM-based image, the instance types here must be ARM-based.
    AmiType = NodegroupAmiType.AL2023_ARM_64_STANDARD
});
go
var cluster Cluster


// X86_64 based AMI managed node group
cluster.AddNodegroupCapacity(jsii.String("custom-node-group"), &NodegroupOptions{
	InstanceTypes: []InstanceType{
		ec2.NewInstanceType(jsii.String("m5.large")),
	},
	 // NOTE: if amiType is x86_64-based image, the instance types here must be x86_64-based.
	AmiType: eks.NodegroupAmiType_AL2023_X86_64_STANDARD,
})

// ARM_64 based AMI managed node group
cluster.AddNodegroupCapacity(jsii.String("custom-node-group"), &NodegroupOptions{
	InstanceTypes: []InstanceType{
		ec2.NewInstanceType(jsii.String("m6g.medium")),
	},
	 // NOTE: if amiType is ARM-based image, the instance types here must be ARM-based.
	AmiType: eks.NodegroupAmiType_AL2023_ARM_64_STANDARD,
})

To define the maximum number of instances which can be simultaneously replaced in a node group during a version update you can set maxUnavailable or maxUnavailablePercentage options.

For more details visit Updating a managed node group

ts
declare const cluster: eks.Cluster;
cluster.addNodegroupCapacity('custom-node-group', {
  instanceTypes: [new ec2.InstanceType('m5.large')],
  maxSize: 5,
  maxUnavailable: 2,
});
python
# cluster: eks.Cluster

cluster.add_nodegroup_capacity("custom-node-group",
    instance_types=[ec2.InstanceType("m5.large")],
    max_size=5,
    max_unavailable=2
)
java
Cluster cluster;

cluster.addNodegroupCapacity("custom-node-group", NodegroupOptions.builder()
        .instanceTypes(List.of(new InstanceType("m5.large")))
        .maxSize(5)
        .maxUnavailable(2)
        .build());
csharp
Cluster cluster;

cluster.AddNodegroupCapacity("custom-node-group", new NodegroupOptions {
    InstanceTypes = new [] { new InstanceType("m5.large") },
    MaxSize = 5,
    MaxUnavailable = 2
});
go
var cluster Cluster

cluster.AddNodegroupCapacity(jsii.String("custom-node-group"), &NodegroupOptions{
	InstanceTypes: []InstanceType{
		ec2.NewInstanceType(jsii.String("m5.large")),
	},
	MaxSize: jsii.Number(5),
	MaxUnavailable: jsii.Number(2),
})
ts
declare const cluster: eks.Cluster;
cluster.addNodegroupCapacity('custom-node-group', {
  instanceTypes: [new ec2.InstanceType('m5.large')],
  maxUnavailablePercentage: 33,
});
python
# cluster: eks.Cluster

cluster.add_nodegroup_capacity("custom-node-group",
    instance_types=[ec2.InstanceType("m5.large")],
    max_unavailable_percentage=33
)
java
Cluster cluster;

cluster.addNodegroupCapacity("custom-node-group", NodegroupOptions.builder()
        .instanceTypes(List.of(new InstanceType("m5.large")))
        .maxUnavailablePercentage(33)
        .build());
csharp
Cluster cluster;

cluster.AddNodegroupCapacity("custom-node-group", new NodegroupOptions {
    InstanceTypes = new [] { new InstanceType("m5.large") },
    MaxUnavailablePercentage = 33
});
go
var cluster Cluster

cluster.AddNodegroupCapacity(jsii.String("custom-node-group"), &NodegroupOptions{
	InstanceTypes: []InstanceType{
		ec2.NewInstanceType(jsii.String("m5.large")),
	},
	MaxUnavailablePercentage: jsii.Number(33),
})

NOTE: If you add instances with the inferentia class (inf1 or inf2) or trainium class (trn1, trn1n, or trn2) the neuron plugin will be automatically installed in the kubernetes cluster.

Default AMI type (under feature flag) ​

By default, managed node groups that do not set amiType use AL2_X86_64 (or AL2_ARM_64 for ARM instances). Amazon Linux 2 EKS-optimized AMIs reached end of support on November 26, 2025. AL2023 is the AWS-recommended default.

New applications should enable the @aws-cdk/aws-eks:defaultToAL2023 feature flag in cdk.json:

json
{
  "context": {
    "@aws-cdk/aws-eks:defaultToAL2023": true
  }
}

When the flag is enabled, the default AMI type for x86_64 instances becomes AL2023_X86_64_STANDARD, and for ARM instances it becomes AL2023_ARM_64_STANDARD. GPU instances continue to default to AL2_X86_64_GPU because AL2023 splits GPU support into separate NVIDIA and Neuron AMI variants — GPU users must pick a variant explicitly.

Migration for existing applications. Enabling this flag on an existing app will cause managed node groups that previously defaulted to AL2 to be replaced with AL2023 on the next deploy, which terminates running pods. To roll out safely, pin every existing node group to its current AMI type first, and only then enable the flag as shown below. Then gradually unpin the AMI for the nodes you want to upgrade.

ts
declare const cluster: eks.Cluster;

// Pin existing node groups to AL2 explicitly before enabling the flag.
cluster.addNodegroupCapacity('workers', {
  instanceTypes: [new ec2.InstanceType('m5.large')],
  amiType: eks.NodegroupAmiType.AL2_X86_64,
});
python
# cluster: eks.Cluster


# Pin existing node groups to AL2 explicitly before enabling the flag.
cluster.add_nodegroup_capacity("workers",
    instance_types=[ec2.InstanceType("m5.large")],
    ami_type=eks.NodegroupAmiType.AL2_X86_64
)
java
Cluster cluster;


// Pin existing node groups to AL2 explicitly before enabling the flag.
cluster.addNodegroupCapacity("workers", NodegroupOptions.builder()
        .instanceTypes(List.of(new InstanceType("m5.large")))
        .amiType(NodegroupAmiType.AL2_X86_64)
        .build());
csharp
Cluster cluster;


// Pin existing node groups to AL2 explicitly before enabling the flag.
cluster.AddNodegroupCapacity("workers", new NodegroupOptions {
    InstanceTypes = new [] { new InstanceType("m5.large") },
    AmiType = NodegroupAmiType.AL2_X86_64
});
go
var cluster Cluster


// Pin existing node groups to AL2 explicitly before enabling the flag.
cluster.AddNodegroupCapacity(jsii.String("workers"), &NodegroupOptions{
	InstanceTypes: []InstanceType{
		ec2.NewInstanceType(jsii.String("m5.large")),
	},
	AmiType: eks.NodegroupAmiType_AL2_X86_64,
})

Explicitly setting amiType will pin it — it is not affected by the feature flag.

Node Groups with IPv6 Support ​

Node groups are available with IPv6 configured networks. For custom roles assigned to node groups additional permissions are necessary in order for pods to obtain an IPv6 address. The default node role will include these permissions.

For more details visit Configuring the Amazon VPC CNI plugin for Kubernetes to use IAM roles for service accounts

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

const ipv6Management = new iam.PolicyDocument({
    statements: [new iam.PolicyStatement({
    resources: ['arn:aws:ec2:*:*:network-interface/*'],
    actions: [
        'ec2:AssignIpv6Addresses',
        'ec2:UnassignIpv6Addresses',
    ],
    })],
});

const eksClusterNodeGroupRole = new iam.Role(this, 'eksClusterNodeGroupRole', {
  roleName: 'eksClusterNodeGroupRole',
  assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'),
  managedPolicies: [
    iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonEKSWorkerNodePolicy'),
    iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonEC2ContainerRegistryReadOnly'),
    iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonEKS_CNI_Policy'),
  ],
    inlinePolicies: {
    ipv6Management,
  },
});

const cluster = new eks.Cluster(this, 'HelloEKS', {
  version: eks.KubernetesVersion.V1_36,
  defaultCapacity: 0,
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});

cluster.addNodegroupCapacity('custom-node-group', {
  instanceTypes: [new ec2.InstanceType('m5.large')],
  minSize: 2,
  diskSize: 100,
  nodeRole: eksClusterNodeGroupRole,
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


ipv6_management = iam.PolicyDocument(
    statements=[iam.PolicyStatement(
        resources=["arn:aws:ec2:*:*:network-interface/*"],
        actions=["ec2:AssignIpv6Addresses", "ec2:UnassignIpv6Addresses"
        ]
    )]
)

eks_cluster_node_group_role = iam.Role(self, "eksClusterNodeGroupRole",
    role_name="eksClusterNodeGroupRole",
    assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"),
    managed_policies=[
        iam.ManagedPolicy.from_aws_managed_policy_name("AmazonEKSWorkerNodePolicy"),
        iam.ManagedPolicy.from_aws_managed_policy_name("AmazonEC2ContainerRegistryReadOnly"),
        iam.ManagedPolicy.from_aws_managed_policy_name("AmazonEKS_CNI_Policy")
    ],
    inline_policies={
        "ipv6_management": ipv6_management
    }
)

cluster = eks.Cluster(self, "HelloEKS",
    version=eks.KubernetesVersion.V1_36,
    default_capacity=0,
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)

cluster.add_nodegroup_capacity("custom-node-group",
    instance_types=[ec2.InstanceType("m5.large")],
    min_size=2,
    disk_size=100,
    node_role=eks_cluster_node_group_role
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


PolicyDocument ipv6Management = PolicyDocument.Builder.create()
        .statements(List.of(PolicyStatement.Builder.create()
                .resources(List.of("arn:aws:ec2:*:*:network-interface/*"))
                .actions(List.of("ec2:AssignIpv6Addresses", "ec2:UnassignIpv6Addresses"))
                .build()))
        .build();

Role eksClusterNodeGroupRole = Role.Builder.create(this, "eksClusterNodeGroupRole")
        .roleName("eksClusterNodeGroupRole")
        .assumedBy(new ServicePrincipal("ec2.amazonaws.com"))
        .managedPolicies(List.of(ManagedPolicy.fromAwsManagedPolicyName("AmazonEKSWorkerNodePolicy"), ManagedPolicy.fromAwsManagedPolicyName("AmazonEC2ContainerRegistryReadOnly"), ManagedPolicy.fromAwsManagedPolicyName("AmazonEKS_CNI_Policy")))
        .inlinePolicies(Map.of(
                "ipv6Management", ipv6Management))
        .build();

Cluster cluster = Cluster.Builder.create(this, "HelloEKS")
        .version(KubernetesVersion.V1_36)
        .defaultCapacity(0)
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();

cluster.addNodegroupCapacity("custom-node-group", NodegroupOptions.builder()
        .instanceTypes(List.of(new InstanceType("m5.large")))
        .minSize(2)
        .diskSize(100)
        .nodeRole(eksClusterNodeGroupRole)
        .build());
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


var ipv6Management = new PolicyDocument(new PolicyDocumentProps {
    Statements = new [] { new PolicyStatement(new PolicyStatementProps {
        Resources = new [] { "arn:aws:ec2:*:*:network-interface/*" },
        Actions = new [] { "ec2:AssignIpv6Addresses", "ec2:UnassignIpv6Addresses" }
    }) }
});

var eksClusterNodeGroupRole = new Role(this, "eksClusterNodeGroupRole", new RoleProps {
    RoleName = "eksClusterNodeGroupRole",
    AssumedBy = new ServicePrincipal("ec2.amazonaws.com"),
    ManagedPolicies = new [] { ManagedPolicy.FromAwsManagedPolicyName("AmazonEKSWorkerNodePolicy"), ManagedPolicy.FromAwsManagedPolicyName("AmazonEC2ContainerRegistryReadOnly"), ManagedPolicy.FromAwsManagedPolicyName("AmazonEKS_CNI_Policy") },
    InlinePolicies = new Dictionary<string, PolicyDocument> {
        { "ipv6Management", ipv6Management }
    }
});

var cluster = new Cluster(this, "HelloEKS", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    DefaultCapacity = 0,
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});

cluster.AddNodegroupCapacity("custom-node-group", new NodegroupOptions {
    InstanceTypes = new [] { new InstanceType("m5.large") },
    MinSize = 2,
    DiskSize = 100,
    NodeRole = eksClusterNodeGroupRole
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


ipv6Management := iam.NewPolicyDocument(&PolicyDocumentProps{
	Statements: []PolicyStatement{
		iam.NewPolicyStatement(&PolicyStatementProps{
			Resources: []*string{
				jsii.String("arn:aws:ec2:*:*:network-interface/*"),
			},
			Actions: []*string{
				jsii.String("ec2:AssignIpv6Addresses"),
				jsii.String("ec2:UnassignIpv6Addresses"),
			},
		}),
	},
})

eksClusterNodeGroupRole := iam.NewRole(this, jsii.String("eksClusterNodeGroupRole"), &RoleProps{
	RoleName: jsii.String("eksClusterNodeGroupRole"),
	AssumedBy: iam.NewServicePrincipal(jsii.String("ec2.amazonaws.com")),
	ManagedPolicies: []IManagedPolicy{
		iam.ManagedPolicy_FromAwsManagedPolicyName(jsii.String("AmazonEKSWorkerNodePolicy")),
		iam.ManagedPolicy_*FromAwsManagedPolicyName(jsii.String("AmazonEC2ContainerRegistryReadOnly")),
		iam.ManagedPolicy_*FromAwsManagedPolicyName(jsii.String("AmazonEKS_CNI_Policy")),
	},
	InlinePolicies: map[string]PolicyDocument{
		"ipv6Management": ipv6Management,
	},
})

cluster := eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	DefaultCapacity: jsii.Number(0),
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

cluster.AddNodegroupCapacity(jsii.String("custom-node-group"), &NodegroupOptions{
	InstanceTypes: []InstanceType{
		ec2.NewInstanceType(jsii.String("m5.large")),
	},
	MinSize: jsii.Number(2),
	DiskSize: jsii.Number(100),
	NodeRole: eksClusterNodeGroupRole,
})

Spot Instances Support ​

Use capacityType to create managed node groups comprised of spot instances. To maximize the availability of your applications while using Spot Instances, we recommend that you configure a Spot managed node group to use multiple instance types with the instanceTypes property.

For more details visit Managed node group capacity types.

ts
declare const cluster: eks.Cluster;
cluster.addNodegroupCapacity('extra-ng-spot', {
  instanceTypes: [
    new ec2.InstanceType('c5.large'),
    new ec2.InstanceType('c5a.large'),
    new ec2.InstanceType('c5d.large'),
  ],
  minSize: 3,
  capacityType: eks.CapacityType.SPOT,
});
python
# cluster: eks.Cluster

cluster.add_nodegroup_capacity("extra-ng-spot",
    instance_types=[
        ec2.InstanceType("c5.large"),
        ec2.InstanceType("c5a.large"),
        ec2.InstanceType("c5d.large")
    ],
    min_size=3,
    capacity_type=eks.CapacityType.SPOT
)
java
Cluster cluster;

cluster.addNodegroupCapacity("extra-ng-spot", NodegroupOptions.builder()
        .instanceTypes(List.of(
            new InstanceType("c5.large"),
            new InstanceType("c5a.large"),
            new InstanceType("c5d.large")))
        .minSize(3)
        .capacityType(CapacityType.SPOT)
        .build());
csharp
Cluster cluster;

cluster.AddNodegroupCapacity("extra-ng-spot", new NodegroupOptions {
    InstanceTypes = new [] {
        new InstanceType("c5.large"),
        new InstanceType("c5a.large"),
        new InstanceType("c5d.large") },
    MinSize = 3,
    CapacityType = CapacityType.SPOT
});
go
var cluster Cluster

cluster.AddNodegroupCapacity(jsii.String("extra-ng-spot"), &NodegroupOptions{
	InstanceTypes: []InstanceType{
		ec2.NewInstanceType(jsii.String("c5.large")),
		ec2.NewInstanceType(jsii.String("c5a.large")),
		ec2.NewInstanceType(jsii.String("c5d.large")),
	},
	MinSize: jsii.Number(3),
	CapacityType: eks.CapacityType_SPOT,
})

Launch Template Support ​

You can specify a launch template that the node group will use. For example, this can be useful if you want to use a custom AMI or add custom user data.

When supplying a custom user data script, it must be encoded in the MIME multi-part archive format, since Amazon EKS merges with its own user data. Visit the Launch Template Docs for mode details.

ts
declare const cluster: eks.Cluster;

const userData = `MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==MYBOUNDARY=="

--==MYBOUNDARY==
Content-Type: text/x-shellscript; charset="us-ascii"

#!/bin/bash
echo "Running custom user data script"

--==MYBOUNDARY==--\\
`;
const lt = new ec2.CfnLaunchTemplate(this, 'LaunchTemplate', {
  launchTemplateData: {
    instanceType: 't3.small',
    userData: Fn.base64(userData),
  },
});

cluster.addNodegroupCapacity('extra-ng', {
  launchTemplateSpec: {
    id: lt.ref,
    version: lt.attrLatestVersionNumber,
  },
});
python
# cluster: eks.Cluster


user_data = """MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==MYBOUNDARY=="

--==MYBOUNDARY==
Content-Type: text/x-shellscript; charset="us-ascii"

#!/bin/bash
echo "Running custom user data script"

--==MYBOUNDARY==--\\
"""
lt = ec2.CfnLaunchTemplate(self, "LaunchTemplate",
    launch_template_data=ec2.CfnLaunchTemplate.LaunchTemplateDataProperty(
        instance_type="t3.small",
        user_data=Fn.base64(user_data)
    )
)

cluster.add_nodegroup_capacity("extra-ng",
    launch_template_spec=eks.LaunchTemplateSpec(
        id=lt.ref,
        version=lt.attr_latest_version_number
    )
)
java
Cluster cluster;


String userData = "MIME-Version: 1.0\nContent-Type: multipart/mixed; boundary=\"==MYBOUNDARY==\"\n\n--==MYBOUNDARY==\nContent-Type: text/x-shellscript; charset=\"us-ascii\"\n\n#!/bin/bash\necho \"Running custom user data script\"\n\n--==MYBOUNDARY==--\\\n";
CfnLaunchTemplate lt = CfnLaunchTemplate.Builder.create(this, "LaunchTemplate")
        .launchTemplateData(LaunchTemplateDataProperty.builder()
                .instanceType("t3.small")
                .userData(Fn.base64(userData))
                .build())
        .build();

cluster.addNodegroupCapacity("extra-ng", NodegroupOptions.builder()
        .launchTemplateSpec(LaunchTemplateSpec.builder()
                .id(lt.getRef())
                .version(lt.getAttrLatestVersionNumber())
                .build())
        .build());
csharp
Cluster cluster;


var userData = @"MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=""==MYBOUNDARY==""

--==MYBOUNDARY==
Content-Type: text/x-shellscript; charset=""us-ascii""

#!/bin/bash
echo ""Running custom user data script""

--==MYBOUNDARY==--\
";
var lt = new CfnLaunchTemplate(this, "LaunchTemplate", new CfnLaunchTemplateProps {
    LaunchTemplateData = new LaunchTemplateDataProperty {
        InstanceType = "t3.small",
        UserData = Fn.Base64(userData)
    }
});

cluster.AddNodegroupCapacity("extra-ng", new NodegroupOptions {
    LaunchTemplateSpec = new LaunchTemplateSpec {
        Id = lt.Ref,
        Version = lt.AttrLatestVersionNumber
    }
});
go
var cluster Cluster


userData := `MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==MYBOUNDARY=="

--==MYBOUNDARY==
Content-Type: text/x-shellscript; charset="us-ascii"

#!/bin/bash
echo "Running custom user data script"

--==MYBOUNDARY==--\\
`
lt := ec2.NewCfnLaunchTemplate(this, jsii.String("LaunchTemplate"), &CfnLaunchTemplateProps{
	LaunchTemplateData: &LaunchTemplateDataProperty{
		InstanceType: jsii.String("t3.small"),
		UserData: awscdk.Fn_Base64(userData),
	},
})

cluster.AddNodegroupCapacity(jsii.String("extra-ng"), &NodegroupOptions{
	LaunchTemplateSpec: &LaunchTemplateSpec{
		Id: lt.ref,
		Version: lt.attrLatestVersionNumber,
	},
})

Note that when using a custom AMI, Amazon EKS doesn't merge any user data. Which means you do not need the multi-part encoding. and are responsible for supplying the required bootstrap commands for nodes to join the cluster. In the following example, /ect/eks/bootstrap.sh from the AMI will be used to bootstrap the node.

ts
declare const cluster: eks.Cluster;
const userData = ec2.UserData.forLinux();
userData.addCommands(
  'set -o xtrace',
  `/etc/eks/bootstrap.sh ${cluster.clusterName}`,
);
const lt = new ec2.CfnLaunchTemplate(this, 'LaunchTemplate', {
  launchTemplateData: {
    imageId: 'some-ami-id', // custom AMI
    instanceType: 't3.small',
    userData: Fn.base64(userData.render()),
  },
});
cluster.addNodegroupCapacity('extra-ng', {
  launchTemplateSpec: {
    id: lt.ref,
    version: lt.attrLatestVersionNumber,
  },
});
python
# cluster: eks.Cluster

user_data = ec2.UserData.for_linux()
user_data.add_commands("set -o xtrace", f"/etc/eks/bootstrap.sh {cluster.clusterName}")
lt = ec2.CfnLaunchTemplate(self, "LaunchTemplate",
    launch_template_data=ec2.CfnLaunchTemplate.LaunchTemplateDataProperty(
        image_id="some-ami-id",  # custom AMI
        instance_type="t3.small",
        user_data=Fn.base64(user_data.render())
    )
)
cluster.add_nodegroup_capacity("extra-ng",
    launch_template_spec=eks.LaunchTemplateSpec(
        id=lt.ref,
        version=lt.attr_latest_version_number
    )
)
java
Cluster cluster;

UserData userData = UserData.forLinux();
userData.addCommands("set -o xtrace", String.format("/etc/eks/bootstrap.sh %s", cluster.getClusterName()));
CfnLaunchTemplate lt = CfnLaunchTemplate.Builder.create(this, "LaunchTemplate")
        .launchTemplateData(LaunchTemplateDataProperty.builder()
                .imageId("some-ami-id") // custom AMI
                .instanceType("t3.small")
                .userData(Fn.base64(userData.render()))
                .build())
        .build();
cluster.addNodegroupCapacity("extra-ng", NodegroupOptions.builder()
        .launchTemplateSpec(LaunchTemplateSpec.builder()
                .id(lt.getRef())
                .version(lt.getAttrLatestVersionNumber())
                .build())
        .build());
csharp
Cluster cluster;

var userData = UserData.ForLinux();
userData.AddCommands("set -o xtrace", $"/etc/eks/bootstrap.sh {cluster.clusterName}");
var lt = new CfnLaunchTemplate(this, "LaunchTemplate", new CfnLaunchTemplateProps {
    LaunchTemplateData = new LaunchTemplateDataProperty {
        ImageId = "some-ami-id",  // custom AMI
        InstanceType = "t3.small",
        UserData = Fn.Base64(userData.Render())
    }
});
cluster.AddNodegroupCapacity("extra-ng", new NodegroupOptions {
    LaunchTemplateSpec = new LaunchTemplateSpec {
        Id = lt.Ref,
        Version = lt.AttrLatestVersionNumber
    }
});
go
var cluster Cluster

userData := ec2.UserData_ForLinux()
userData.AddCommands(jsii.String("set -o xtrace"),
fmt.Sprintf("/etc/eks/bootstrap.sh %v", cluster.clusterName))
lt := ec2.NewCfnLaunchTemplate(this, jsii.String("LaunchTemplate"), &CfnLaunchTemplateProps{
	LaunchTemplateData: &LaunchTemplateDataProperty{
		ImageId: jsii.String("some-ami-id"),
		 // custom AMI
		InstanceType: jsii.String("t3.small"),
		UserData: awscdk.Fn_Base64(userData.Render()),
	},
})
cluster.AddNodegroupCapacity(jsii.String("extra-ng"), &NodegroupOptions{
	LaunchTemplateSpec: &LaunchTemplateSpec{
		Id: lt.ref,
		Version: lt.attrLatestVersionNumber,
	},
})

You may specify one instanceType in the launch template or multiple instanceTypes in the node group, but not both.

For more details visit Launch Template Support.

Graviton 2 instance types are supported including c6g, m6g, r6g and t4g. Graviton 3 instance types are supported including c7g.

Update clusters ​

When you rename the cluster name and redeploy the stack, the cluster replacement will be triggered and the existing one will be deleted after the new one is provisioned. As the cluster resource ARN has been changed, the cluster resource handler would not be able to delete the old one as the resource ARN in the IAM policy has been changed. As a workaround, you need to add a temporary policy to the cluster admin role for successful replacement. Consider this example if you are renaming the cluster from foo to bar:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

const cluster = new eks.Cluster(this, 'cluster-to-rename', {
  clusterName: 'foo', // rename this to 'bar'
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
  version: eks.KubernetesVersion.V1_36,
});

// allow the cluster admin role to delete the cluster 'foo'
cluster.adminRole.addToPolicy(new iam.PolicyStatement({
  actions: [
    'eks:DeleteCluster',
    'eks:DescribeCluster',
  ],
  resources: [
    Stack.of(this).formatArn({ service: 'eks', resource: 'cluster', resourceName: 'foo' }),
]
}))
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


cluster = eks.Cluster(self, "cluster-to-rename",
    cluster_name="foo",  # rename this to 'bar'
    kubectl_layer=KubectlV36Layer(self, "kubectl"),
    version=eks.KubernetesVersion.V1_36
)

# allow the cluster admin role to delete the cluster 'foo'
cluster.admin_role.add_to_policy(iam.PolicyStatement(
    actions=["eks:DeleteCluster", "eks:DescribeCluster"
    ],
    resources=[
        Stack.of(self).format_arn(service="eks", resource="cluster", resource_name="foo")
    ]
))
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


Cluster cluster = Cluster.Builder.create(this, "cluster-to-rename")
        .clusterName("foo") // rename this to 'bar'
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .version(KubernetesVersion.V1_36)
        .build();

// allow the cluster admin role to delete the cluster 'foo'
cluster.adminRole.addToPolicy(PolicyStatement.Builder.create()
        .actions(List.of("eks:DeleteCluster", "eks:DescribeCluster"))
        .resources(List.of(Stack.of(this).formatArn(ArnComponents.builder().service("eks").resource("cluster").resourceName("foo").build())))
        .build());
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


var cluster = new Cluster(this, "cluster-to-rename", new ClusterProps {
    ClusterName = "foo",  // rename this to 'bar'
    KubectlLayer = new KubectlV36Layer(this, "kubectl"),
    Version = KubernetesVersion.V1_36
});

// allow the cluster admin role to delete the cluster 'foo'
cluster.AdminRole.AddToPolicy(new PolicyStatement(new PolicyStatementProps {
    Actions = new [] { "eks:DeleteCluster", "eks:DescribeCluster" },
    Resources = new [] { Stack.Of(this).FormatArn(new ArnComponents { Service = "eks", Resource = "cluster", ResourceName = "foo" }) }
}));
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


cluster := eks.NewCluster(this, jsii.String("cluster-to-rename"), &ClusterProps{
	ClusterName: jsii.String("foo"),
	 // rename this to 'bar'
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
	Version: eks.KubernetesVersion_V1_36(),
})

// allow the cluster admin role to delete the cluster 'foo'
cluster.AdminRole.AddToPolicy(iam.NewPolicyStatement(&PolicyStatementProps{
	Actions: []*string{
		jsii.String("eks:DeleteCluster"),
		jsii.String("eks:DescribeCluster"),
	},
	Resources: []*string{
		awscdk.*stack_Of(this).FormatArn(&ArnComponents{
			Service: jsii.String("eks"),
			Resource: jsii.String("cluster"),
			ResourceName: jsii.String("foo"),
		}),
	},
}))

Fargate profiles ​

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.

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.

See Fargate Considerations in the AWS EKS User Guide.

You can add Fargate Profiles to any EKS cluster defined in your CDK app through the addFargateProfile() method. The following example adds a profile that will match all pods from the "default" namespace:

ts
declare const cluster: eks.Cluster;
cluster.addFargateProfile('MyProfile', {
  selectors: [ { namespace: 'default' } ],
});
python
# cluster: eks.Cluster

cluster.add_fargate_profile("MyProfile",
    selectors=[eks.Selector(namespace="default")]
)
java
Cluster cluster;

cluster.addFargateProfile("MyProfile", FargateProfileOptions.builder()
        .selectors(List.of(Selector.builder().namespace("default").build()))
        .build());
csharp
Cluster cluster;

cluster.AddFargateProfile("MyProfile", new FargateProfileOptions {
    Selectors = new [] { new Selector { Namespace = "default" } }
});
go
var cluster Cluster

cluster.AddFargateProfile(jsii.String("MyProfile"), &FargateProfileOptions{
	Selectors: []Selector{
		&Selector{
			Namespace: jsii.String("default"),
		},
	},
})

You can also directly use the FargateProfile construct to create profiles under different scopes:

ts
declare const cluster: eks.Cluster;
new eks.FargateProfile(this, 'MyProfile', {
  cluster,
  selectors: [ { namespace: 'default' } ],
});
python
# cluster: eks.Cluster

eks.FargateProfile(self, "MyProfile",
    cluster=cluster,
    selectors=[eks.Selector(namespace="default")]
)
java
Cluster cluster;

FargateProfile.Builder.create(this, "MyProfile")
        .cluster(cluster)
        .selectors(List.of(Selector.builder().namespace("default").build()))
        .build();
csharp
Cluster cluster;

new FargateProfile(this, "MyProfile", new FargateProfileProps {
    Cluster = cluster,
    Selectors = new [] { new Selector { Namespace = "default" } }
});
go
var cluster Cluster

eks.NewFargateProfile(this, jsii.String("MyProfile"), &FargateProfileProps{
	Cluster: Cluster,
	Selectors: []Selector{
		&Selector{
			Namespace: jsii.String("default"),
		},
	},
})

To create an EKS cluster that only uses Fargate capacity, you can use FargateCluster. The following code defines an Amazon EKS cluster with a default Fargate Profile that matches all pods from the "kube-system" and "default" namespaces. It is also configured to run CoreDNS on Fargate.

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

const cluster = new eks.FargateCluster(this, 'MyCluster', {
  version: eks.KubernetesVersion.V1_36,
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


cluster = eks.FargateCluster(self, "MyCluster",
    version=eks.KubernetesVersion.V1_36,
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


FargateCluster cluster = FargateCluster.Builder.create(this, "MyCluster")
        .version(KubernetesVersion.V1_36)
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


var cluster = new FargateCluster(this, "MyCluster", new FargateClusterProps {
    Version = KubernetesVersion.V1_36,
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


cluster := eks.NewFargateCluster(this, jsii.String("MyCluster"), &FargateClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

FargateCluster will create a default FargateProfile which can be accessed via the cluster's defaultProfile property. The created profile can also be customized by passing options as with addFargateProfile.

NOTE: Classic Load Balancers and Network Load Balancers are not supported on pods running on Fargate. For ingress, we recommend that you use the ALB Ingress Controller on Amazon EKS (minimum version v1.1.4).

Self-managed nodes ​

Another way of allocating capacity to an EKS cluster is by using self-managed nodes. EC2 instances that are part of the auto-scaling group will serve as worker nodes for the cluster. This type of capacity is also commonly referred to as EC2 Capacity* or EC2 Nodes.

For a detailed overview please visit Self Managed Nodes.

Creating an auto-scaling group and connecting it to the cluster is done using the cluster.addAutoScalingGroupCapacity method:

ts
declare const cluster: eks.Cluster;
cluster.addAutoScalingGroupCapacity('frontend-nodes', {
  instanceType: new ec2.InstanceType('t2.medium'),
  minCapacity: 3,
  vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
});
python
# cluster: eks.Cluster

cluster.add_auto_scaling_group_capacity("frontend-nodes",
    instance_type=ec2.InstanceType("t2.medium"),
    min_capacity=3,
    vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PUBLIC)
)
java
Cluster cluster;

cluster.addAutoScalingGroupCapacity("frontend-nodes", AutoScalingGroupCapacityOptions.builder()
        .instanceType(new InstanceType("t2.medium"))
        .minCapacity(3)
        .vpcSubnets(SubnetSelection.builder().subnetType(SubnetType.PUBLIC).build())
        .build());
csharp
Cluster cluster;

cluster.AddAutoScalingGroupCapacity("frontend-nodes", new AutoScalingGroupCapacityOptions {
    InstanceType = new InstanceType("t2.medium"),
    MinCapacity = 3,
    VpcSubnets = new SubnetSelection { SubnetType = SubnetType.PUBLIC }
});
go
var cluster Cluster

cluster.AddAutoScalingGroupCapacity(jsii.String("frontend-nodes"), &AutoScalingGroupCapacityOptions{
	InstanceType: ec2.NewInstanceType(jsii.String("t2.medium")),
	MinCapacity: jsii.Number(3),
	VpcSubnets: &SubnetSelection{
		SubnetType: ec2.SubnetType_PUBLIC,
	},
})

To connect an already initialized auto-scaling group, use the cluster.connectAutoScalingGroupCapacity() method:

ts
declare const cluster: eks.Cluster;
declare const asg: autoscaling.AutoScalingGroup;
cluster.connectAutoScalingGroupCapacity(asg, {});
python
# cluster: eks.Cluster
# asg: autoscaling.AutoScalingGroup

cluster.connect_auto_scaling_group_capacity(asg)
java
Cluster cluster;
AutoScalingGroup asg;

cluster.connectAutoScalingGroupCapacity(asg, AutoScalingGroupOptions.builder().build());
csharp
Cluster cluster;
AutoScalingGroup asg;

cluster.ConnectAutoScalingGroupCapacity(asg, new AutoScalingGroupOptions { });
go
var cluster Cluster
var asg AutoScalingGroup

cluster.connectAutoScalingGroupCapacity(asg, &AutoScalingGroupOptions{
})

To connect a self-managed node group to an imported cluster, use the cluster.connectAutoScalingGroupCapacity() method:

ts
declare const cluster: eks.Cluster;
declare const asg: autoscaling.AutoScalingGroup;
const importedCluster = eks.Cluster.fromClusterAttributes(this, 'ImportedCluster', {
  clusterName: cluster.clusterName,
  clusterSecurityGroupId: cluster.clusterSecurityGroupId,
});

importedCluster.connectAutoScalingGroupCapacity(asg, {});
python
# cluster: eks.Cluster
# asg: autoscaling.AutoScalingGroup

imported_cluster = eks.Cluster.from_cluster_attributes(self, "ImportedCluster",
    cluster_name=cluster.cluster_name,
    cluster_security_group_id=cluster.cluster_security_group_id
)

imported_cluster.connect_auto_scaling_group_capacity(asg)
java
Cluster cluster;
AutoScalingGroup asg;

ICluster importedCluster = Cluster.fromClusterAttributes(this, "ImportedCluster", ClusterAttributes.builder()
        .clusterName(cluster.getClusterName())
        .clusterSecurityGroupId(cluster.getClusterSecurityGroupId())
        .build());

importedCluster.connectAutoScalingGroupCapacity(asg, AutoScalingGroupOptions.builder().build());
csharp
Cluster cluster;
AutoScalingGroup asg;

var importedCluster = Cluster.FromClusterAttributes(this, "ImportedCluster", new ClusterAttributes {
    ClusterName = cluster.ClusterName,
    ClusterSecurityGroupId = cluster.ClusterSecurityGroupId
});

importedCluster.ConnectAutoScalingGroupCapacity(asg, new AutoScalingGroupOptions { });
go
var cluster Cluster
var asg AutoScalingGroup

importedCluster := eks.Cluster_FromClusterAttributes(this, jsii.String("ImportedCluster"), &ClusterAttributes{
	ClusterName: cluster.clusterName,
	ClusterSecurityGroupId: cluster.ClusterSecurityGroupId,
})

importedCluster.ConnectAutoScalingGroupCapacity(asg, &AutoScalingGroupOptions{
})

In both cases, the cluster security group will be automatically attached to the auto-scaling group, allowing for traffic to flow freely between managed and self-managed nodes.

Note: The default updateType for auto-scaling groups does not replace existing nodes. Since security groups are determined at launch time, self-managed nodes that were provisioned with version 1.78.0 or lower, will not be updated. To apply the new configuration on all your self-managed nodes, you'll need to replace the nodes using the UpdateType.REPLACING_UPDATE policy for the updateType property.

You can customize the /etc/eks/boostrap.sh script, which is responsible for bootstrapping the node to the EKS cluster. For example, you can use kubeletExtraArgs to add custom node labels or taints.

ts
declare const cluster: eks.Cluster;
cluster.addAutoScalingGroupCapacity('spot', {
  instanceType: new ec2.InstanceType('t3.large'),
  minCapacity: 2,
  bootstrapOptions: {
    kubeletExtraArgs: '--node-labels foo=bar,goo=far',
    awsApiRetryAttempts: 5,
  },
});
python
# cluster: eks.Cluster

cluster.add_auto_scaling_group_capacity("spot",
    instance_type=ec2.InstanceType("t3.large"),
    min_capacity=2,
    bootstrap_options=eks.BootstrapOptions(
        kubelet_extra_args="--node-labels foo=bar,goo=far",
        aws_api_retry_attempts=5
    )
)
java
Cluster cluster;

cluster.addAutoScalingGroupCapacity("spot", AutoScalingGroupCapacityOptions.builder()
        .instanceType(new InstanceType("t3.large"))
        .minCapacity(2)
        .bootstrapOptions(BootstrapOptions.builder()
                .kubeletExtraArgs("--node-labels foo=bar,goo=far")
                .awsApiRetryAttempts(5)
                .build())
        .build());
csharp
Cluster cluster;

cluster.AddAutoScalingGroupCapacity("spot", new AutoScalingGroupCapacityOptions {
    InstanceType = new InstanceType("t3.large"),
    MinCapacity = 2,
    BootstrapOptions = new BootstrapOptions {
        KubeletExtraArgs = "--node-labels foo=bar,goo=far",
        AwsApiRetryAttempts = 5
    }
});
go
var cluster Cluster

cluster.AddAutoScalingGroupCapacity(jsii.String("spot"), &AutoScalingGroupCapacityOptions{
	InstanceType: ec2.NewInstanceType(jsii.String("t3.large")),
	MinCapacity: jsii.Number(2),
	BootstrapOptions: &BootstrapOptions{
		KubeletExtraArgs: jsii.String("--node-labels foo=bar,goo=far"),
		AwsApiRetryAttempts: jsii.Number(5),
	},
})

To disable bootstrapping altogether (i.e. to fully customize user-data), set bootstrapEnabled to false. You can also configure the cluster to use an auto-scaling group as the default capacity:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

const cluster = new eks.Cluster(this, 'HelloEKS', {
  version: eks.KubernetesVersion.V1_36,
  defaultCapacityType: eks.DefaultCapacityType.EC2,
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


cluster = eks.Cluster(self, "HelloEKS",
    version=eks.KubernetesVersion.V1_36,
    default_capacity_type=eks.DefaultCapacityType.EC2,
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


Cluster cluster = Cluster.Builder.create(this, "HelloEKS")
        .version(KubernetesVersion.V1_36)
        .defaultCapacityType(DefaultCapacityType.EC2)
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


var cluster = new Cluster(this, "HelloEKS", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    DefaultCapacityType = DefaultCapacityType.EC2,
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


cluster := eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	DefaultCapacityType: eks.DefaultCapacityType_EC2,
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

This will allocate an auto-scaling group with 2 m5.large instances (this instance type suits most common use-cases, and is good value for money). To access the AutoScalingGroup that was created on your behalf, you can use cluster.defaultCapacity. You can also independently create an AutoScalingGroup and connect it to the cluster using the cluster.connectAutoScalingGroupCapacity method:

ts
declare const cluster: eks.Cluster;
declare const asg: autoscaling.AutoScalingGroup;
cluster.connectAutoScalingGroupCapacity(asg, {});
python
# cluster: eks.Cluster
# asg: autoscaling.AutoScalingGroup

cluster.connect_auto_scaling_group_capacity(asg)
java
Cluster cluster;
AutoScalingGroup asg;

cluster.connectAutoScalingGroupCapacity(asg, AutoScalingGroupOptions.builder().build());
csharp
Cluster cluster;
AutoScalingGroup asg;

cluster.ConnectAutoScalingGroupCapacity(asg, new AutoScalingGroupOptions { });
go
var cluster Cluster
var asg AutoScalingGroup

cluster.connectAutoScalingGroupCapacity(asg, &AutoScalingGroupOptions{
})

This will add the necessary user-data to access the apiserver and configure all connections, roles, and tags needed for the instances in the auto-scaling group to properly join the cluster.

Spot Instances ​

When using self-managed nodes, you can configure the capacity to use spot instances, greatly reducing capacity cost. To enable spot capacity, use the spotPrice property:

ts
declare const cluster: eks.Cluster;
cluster.addAutoScalingGroupCapacity('spot', {
  spotPrice: '0.1094',
  instanceType: new ec2.InstanceType('t3.large'),
  maxCapacity: 10,
});
python
# cluster: eks.Cluster

cluster.add_auto_scaling_group_capacity("spot",
    spot_price="0.1094",
    instance_type=ec2.InstanceType("t3.large"),
    max_capacity=10
)
java
Cluster cluster;

cluster.addAutoScalingGroupCapacity("spot", AutoScalingGroupCapacityOptions.builder()
        .spotPrice("0.1094")
        .instanceType(new InstanceType("t3.large"))
        .maxCapacity(10)
        .build());
csharp
Cluster cluster;

cluster.AddAutoScalingGroupCapacity("spot", new AutoScalingGroupCapacityOptions {
    SpotPrice = "0.1094",
    InstanceType = new InstanceType("t3.large"),
    MaxCapacity = 10
});
go
var cluster Cluster

cluster.AddAutoScalingGroupCapacity(jsii.String("spot"), &AutoScalingGroupCapacityOptions{
	SpotPrice: jsii.String("0.1094"),
	InstanceType: ec2.NewInstanceType(jsii.String("t3.large")),
	MaxCapacity: jsii.Number(10),
})

Spot instance nodes will be labeled with lifecycle=Ec2Spot and tainted with PreferNoSchedule.

The AWS Node Termination Handler DaemonSet will be installed from Amazon EKS Helm chart repository on these nodes. The termination handler ensures that the Kubernetes control plane responds appropriately to events that can cause your EC2 instance to become unavailable, such as EC2 maintenance events and EC2 Spot interruptions and helps gracefully stop all pods running on spot nodes that are about to be terminated.

Handler Version: 1.7.0

Chart Version: 0.9.5

To disable the installation of the termination handler, set the spotInterruptHandler property to false. This applies both to addAutoScalingGroupCapacity and connectAutoScalingGroupCapacity.

Bottlerocket ​

Bottlerocket is a Linux-based open-source operating system that is purpose-built by Amazon Web Services for running containers on virtual machines or bare metal hosts.

Bottlerocket is supported when using managed nodegroups or self-managed auto-scaling groups.

To create a Bottlerocket managed nodegroup:

ts
declare const cluster: eks.Cluster;
cluster.addNodegroupCapacity('BottlerocketNG', {
  amiType: eks.NodegroupAmiType.BOTTLEROCKET_X86_64,
});
python
# cluster: eks.Cluster

cluster.add_nodegroup_capacity("BottlerocketNG",
    ami_type=eks.NodegroupAmiType.BOTTLEROCKET_X86_64
)
java
Cluster cluster;

cluster.addNodegroupCapacity("BottlerocketNG", NodegroupOptions.builder()
        .amiType(NodegroupAmiType.BOTTLEROCKET_X86_64)
        .build());
csharp
Cluster cluster;

cluster.AddNodegroupCapacity("BottlerocketNG", new NodegroupOptions {
    AmiType = NodegroupAmiType.BOTTLEROCKET_X86_64
});
go
var cluster Cluster

cluster.AddNodegroupCapacity(jsii.String("BottlerocketNG"), &NodegroupOptions{
	AmiType: eks.NodegroupAmiType_BOTTLEROCKET_X86_64,
})

The following example will create an auto-scaling group of 2 t3.small Linux instances running with the Bottlerocket AMI.

ts
declare const cluster: eks.Cluster;
cluster.addAutoScalingGroupCapacity('BottlerocketNodes', {
  instanceType: new ec2.InstanceType('t3.small'),
  minCapacity:  2,
  machineImageType: eks.MachineImageType.BOTTLEROCKET,
});
python
# cluster: eks.Cluster

cluster.add_auto_scaling_group_capacity("BottlerocketNodes",
    instance_type=ec2.InstanceType("t3.small"),
    min_capacity=2,
    machine_image_type=eks.MachineImageType.BOTTLEROCKET
)
java
Cluster cluster;

cluster.addAutoScalingGroupCapacity("BottlerocketNodes", AutoScalingGroupCapacityOptions.builder()
        .instanceType(new InstanceType("t3.small"))
        .minCapacity(2)
        .machineImageType(MachineImageType.BOTTLEROCKET)
        .build());
csharp
Cluster cluster;

cluster.AddAutoScalingGroupCapacity("BottlerocketNodes", new AutoScalingGroupCapacityOptions {
    InstanceType = new InstanceType("t3.small"),
    MinCapacity = 2,
    MachineImageType = MachineImageType.BOTTLEROCKET
});
go
var cluster Cluster

cluster.AddAutoScalingGroupCapacity(jsii.String("BottlerocketNodes"), &AutoScalingGroupCapacityOptions{
	InstanceType: ec2.NewInstanceType(jsii.String("t3.small")),
	MinCapacity: jsii.Number(2),
	MachineImageType: eks.MachineImageType_BOTTLEROCKET,
})

The specific Bottlerocket AMI variant will be auto selected according to the k8s version for the x86_64 architecture. For example, if the Amazon EKS cluster version is 1.17, the Bottlerocket AMI variant will be auto selected as aws-k8s-1.17 behind the scene.

See Variants for more details.

Please note Bottlerocket does not allow to customize bootstrap options and bootstrapOptions properties is not supported when you create the Bottlerocket capacity.

To create a Bottlerocket managed nodegroup with Nvidia-based EC2 instance types use the BOTTLEROCKET_X86_64_NVIDIA or BOTTLEROCKET_ARM_64_NVIDIA AMIs:

ts
declare const cluster: eks.Cluster;
cluster.addNodegroupCapacity('BottlerocketNvidiaNG', {
  amiType: eks.NodegroupAmiType.BOTTLEROCKET_X86_64_NVIDIA,
  instanceTypes: [new ec2.InstanceType('g4dn.xlarge')],
});
python
# cluster: eks.Cluster

cluster.add_nodegroup_capacity("BottlerocketNvidiaNG",
    ami_type=eks.NodegroupAmiType.BOTTLEROCKET_X86_64_NVIDIA,
    instance_types=[ec2.InstanceType("g4dn.xlarge")]
)
java
Cluster cluster;

cluster.addNodegroupCapacity("BottlerocketNvidiaNG", NodegroupOptions.builder()
        .amiType(NodegroupAmiType.BOTTLEROCKET_X86_64_NVIDIA)
        .instanceTypes(List.of(new InstanceType("g4dn.xlarge")))
        .build());
csharp
Cluster cluster;

cluster.AddNodegroupCapacity("BottlerocketNvidiaNG", new NodegroupOptions {
    AmiType = NodegroupAmiType.BOTTLEROCKET_X86_64_NVIDIA,
    InstanceTypes = new [] { new InstanceType("g4dn.xlarge") }
});
go
var cluster Cluster

cluster.AddNodegroupCapacity(jsii.String("BottlerocketNvidiaNG"), &NodegroupOptions{
	AmiType: eks.NodegroupAmiType_BOTTLEROCKET_X86_64_NVIDIA,
	InstanceTypes: []InstanceType{
		ec2.NewInstanceType(jsii.String("g4dn.xlarge")),
	},
})

For more details about Bottlerocket, see Bottlerocket FAQs and Bottlerocket Open Source Blog.

Endpoint Access ​

When you create a new cluster, Amazon EKS creates an endpoint for the managed Kubernetes API server that you use to communicate with your cluster (using Kubernetes management tools such as kubectl)

By default, this API server endpoint is public to the internet, and access to the API server is secured using a combination of AWS Identity and Access Management (IAM) and native Kubernetes Role Based Access Control (RBAC).

You can configure the cluster endpoint access by using the endpointAccess property:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

const cluster = new eks.Cluster(this, 'hello-eks', {
  version: eks.KubernetesVersion.V1_36,
  endpointAccess: eks.EndpointAccess.PRIVATE, // No access outside of your VPC.
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


cluster = eks.Cluster(self, "hello-eks",
    version=eks.KubernetesVersion.V1_36,
    endpoint_access=eks.EndpointAccess.PRIVATE,  # No access outside of your VPC.
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


Cluster cluster = Cluster.Builder.create(this, "hello-eks")
        .version(KubernetesVersion.V1_36)
        .endpointAccess(EndpointAccess.PRIVATE) // No access outside of your VPC.
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


var cluster = new Cluster(this, "hello-eks", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    EndpointAccess = EndpointAccess.PRIVATE,  // No access outside of your VPC.
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


cluster := eks.NewCluster(this, jsii.String("hello-eks"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	EndpointAccess: eks.EndpointAccess_PRIVATE(),
	 // No access outside of your VPC.
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

The default value is eks.EndpointAccess.PUBLIC_AND_PRIVATE. Which means the cluster endpoint is accessible from outside of your VPC, but worker node traffic and kubectl commands issued by this library stay within your VPC.

Alb Controller ​

Some Kubernetes resources are commonly implemented on AWS with the help of the ALB Controller.

From the docs:

AWS Load Balancer Controller is a controller to help manage Elastic Load Balancers for a Kubernetes cluster.

  • It satisfies Kubernetes Ingress resources by provisioning Application Load Balancers.
  • It satisfies Kubernetes Service resources by provisioning Network Load Balancers.

To deploy the controller on your EKS cluster, configure the albController property:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

new eks.Cluster(this, 'HelloEKS', {
  version: eks.KubernetesVersion.V1_36,
  albController: {
    version: eks.AlbControllerVersion.V3_2_2,
  },
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


eks.Cluster(self, "HelloEKS",
    version=eks.KubernetesVersion.V1_36,
    alb_controller=eks.AlbControllerOptions(
        version=eks.AlbControllerVersion.V3_2_2
    ),
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


Cluster.Builder.create(this, "HelloEKS")
        .version(KubernetesVersion.V1_36)
        .albController(AlbControllerOptions.builder()
                .version(AlbControllerVersion.V3_2_2)
                .build())
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


new Cluster(this, "HelloEKS", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    AlbController = new AlbControllerOptions {
        Version = AlbControllerVersion.V3_2_2
    },
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	AlbController: &AlbControllerOptions{
		Version: eks.AlbControllerVersion_V3_2_2(),
	},
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

To provide additional Helm chart values supported by albController in CDK, use the additionalHelmChartValues property. For example, the following code snippet shows how to set the enableWafV2 flag:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

new eks.Cluster(this, 'HelloEKS', {
  version: eks.KubernetesVersion.V1_36,
  albController: {
    version: eks.AlbControllerVersion.V3_2_2,
    additionalHelmChartValues: {
      enableWafv2: false
    }
  },
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


eks.Cluster(self, "HelloEKS",
    version=eks.KubernetesVersion.V1_36,
    alb_controller=eks.AlbControllerOptions(
        version=eks.AlbControllerVersion.V3_2_2,
        additional_helm_chart_values=eks.AlbControllerHelmChartOptions(
            enable_wafv2=False
        )
    ),
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


Cluster.Builder.create(this, "HelloEKS")
        .version(KubernetesVersion.V1_36)
        .albController(AlbControllerOptions.builder()
                .version(AlbControllerVersion.V3_2_2)
                .additionalHelmChartValues(AlbControllerHelmChartOptions.builder()
                        .enableWafv2(false)
                        .build())
                .build())
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


new Cluster(this, "HelloEKS", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    AlbController = new AlbControllerOptions {
        Version = AlbControllerVersion.V3_2_2,
        AdditionalHelmChartValues = new AlbControllerHelmChartOptions {
            EnableWafv2 = false
        }
    },
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	AlbController: &AlbControllerOptions{
		Version: eks.AlbControllerVersion_V3_2_2(),
		AdditionalHelmChartValues: &AlbControllerHelmChartOptions{
			EnableWafv2: jsii.Boolean(false),
		},
	},
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

To overwrite an existing ALB controller service account, use the overwriteServiceAccount property:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

new eks.Cluster(this, 'HelloEKS', {
  version: eks.KubernetesVersion.V1_36,
  albController: {
    version: eks.AlbControllerVersion.V3_2_2,
    overwriteServiceAccount: true
  },
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


eks.Cluster(self, "HelloEKS",
    version=eks.KubernetesVersion.V1_36,
    alb_controller=eks.AlbControllerOptions(
        version=eks.AlbControllerVersion.V3_2_2,
        overwrite_service_account=True
    ),
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


Cluster.Builder.create(this, "HelloEKS")
        .version(KubernetesVersion.V1_36)
        .albController(AlbControllerOptions.builder()
                .version(AlbControllerVersion.V3_2_2)
                .overwriteServiceAccount(true)
                .build())
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


new Cluster(this, "HelloEKS", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    AlbController = new AlbControllerOptions {
        Version = AlbControllerVersion.V3_2_2,
        OverwriteServiceAccount = true
    },
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	AlbController: &AlbControllerOptions{
		Version: eks.AlbControllerVersion_V3_2_2(),
		OverwriteServiceAccount: jsii.Boolean(true),
	},
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

The albController requires defaultCapacity or at least one nodegroup. If there's no defaultCapacity or available nodegroup for the cluster, the albController deployment would fail.

Querying the controller pods should look something like this:

console
❯ kubectl get pods -n kube-system
NAME                                            READY   STATUS    RESTARTS   AGE
aws-load-balancer-controller-76bd6c7586-d929p   1/1     Running   0          109m
aws-load-balancer-controller-76bd6c7586-fqxph   1/1     Running   0          109m
...
...

Every Kubernetes manifest that utilizes the ALB Controller is effectively dependent on the controller. If the controller is deleted before the manifest, it might result in dangling ELB/ALB resources. Currently, the EKS construct library does not detect such dependencies, and they should be done explicitly.

For example:

ts
declare const cluster: eks.Cluster;
const manifest = cluster.addManifest('manifest', {/* ... */});
if (cluster.albController) {
  manifest.node.addDependency(cluster.albController);
}
python
# cluster: eks.Cluster

manifest = cluster.add_manifest("manifest", {})
if cluster.alb_controller:
    manifest.node.add_dependency(cluster.alb_controller)
java
Cluster cluster;

KubernetesManifest manifest = cluster.addManifest("manifest", Map.of());
if (cluster.getAlbController()) {
    manifest.node.addDependency(cluster.getAlbController());
}
csharp
Cluster cluster;

var manifest = cluster.AddManifest("manifest", new Dictionary<string, object> { });
if (cluster.AlbController)
{
    manifest.Node.AddDependency(cluster.AlbController);
}
go
var cluster Cluster

manifest := cluster.addManifest(jsii.String("manifest"), map[string]interface{}{
})
if cluster.AlbController {
	manifest.Node.AddDependency(cluster.AlbController)
}

VPC Support ​

You can specify the VPC of the cluster using the vpc and vpcSubnets properties:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

declare const vpc: ec2.Vpc;

new eks.Cluster(this, 'HelloEKS', {
  version: eks.KubernetesVersion.V1_36,
  vpc,
  vpcSubnets: [{ subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }],
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer

# vpc: ec2.Vpc


eks.Cluster(self, "HelloEKS",
    version=eks.KubernetesVersion.V1_36,
    vpc=vpc,
    vpc_subnets=[ec2.SubnetSelection(subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS)],
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;

Vpc vpc;


Cluster.Builder.create(this, "HelloEKS")
        .version(KubernetesVersion.V1_36)
        .vpc(vpc)
        .vpcSubnets(List.of(SubnetSelection.builder().subnetType(SubnetType.PRIVATE_WITH_EGRESS).build()))
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;

Vpc vpc;


new Cluster(this, "HelloEKS", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    Vpc = vpc,
    VpcSubnets = new [] { new SubnetSelection { SubnetType = SubnetType.PRIVATE_WITH_EGRESS } },
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"

var vpc Vpc


eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	Vpc: Vpc,
	VpcSubnets: []SubnetSelection{
		&SubnetSelection{
			SubnetType: ec2.SubnetType_PRIVATE_WITH_EGRESS,
		},
	},
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

Note: Isolated VPCs (i.e with no internet access) are not fully supported. See https://github.com/aws/aws-cdk/issues/12171. Check out this aws-cdk-example for reference.

If you do not specify a VPC, one will be created on your behalf, which you can then access via cluster.vpc. The cluster VPC will be associated to any EKS managed capacity (i.e Managed Node Groups and Fargate Profiles).

Please note that the vpcSubnets property defines the subnets where EKS will place the control plane ENIs. To choose the subnets where EKS will place the worker nodes, please refer to the Provisioning clusters section above.

If you allocate self managed capacity, you can specify which subnets should the auto-scaling group use:

ts
declare const vpc: ec2.Vpc;
declare const cluster: eks.Cluster;
cluster.addAutoScalingGroupCapacity('nodes', {
  vpcSubnets: { subnets: vpc.privateSubnets },
  instanceType: new ec2.InstanceType('t2.medium'),
});
python
# vpc: ec2.Vpc
# cluster: eks.Cluster

cluster.add_auto_scaling_group_capacity("nodes",
    vpc_subnets=ec2.SubnetSelection(subnets=vpc.private_subnets),
    instance_type=ec2.InstanceType("t2.medium")
)
java
Vpc vpc;
Cluster cluster;

cluster.addAutoScalingGroupCapacity("nodes", AutoScalingGroupCapacityOptions.builder()
        .vpcSubnets(SubnetSelection.builder().subnets(vpc.getPrivateSubnets()).build())
        .instanceType(new InstanceType("t2.medium"))
        .build());
csharp
Vpc vpc;
Cluster cluster;

cluster.AddAutoScalingGroupCapacity("nodes", new AutoScalingGroupCapacityOptions {
    VpcSubnets = new SubnetSelection { Subnets = vpc.PrivateSubnets },
    InstanceType = new InstanceType("t2.medium")
});
go
var vpc Vpc
var cluster Cluster

cluster.AddAutoScalingGroupCapacity(jsii.String("nodes"), &AutoScalingGroupCapacityOptions{
	VpcSubnets: &SubnetSelection{
		Subnets: vpc.PrivateSubnets,
	},
	InstanceType: ec2.NewInstanceType(jsii.String("t2.medium")),
})

There are two additional components you might want to provision within the VPC.

Kubectl Handler ​

The KubectlHandler is a Lambda function responsible to issuing kubectl and helm commands against the cluster when you add resource manifests to the cluster.

The handler association to the VPC is derived from the endpointAccess configuration. The rule of thumb is: If the cluster VPC can be associated, it will be.

Breaking this down, it means that if the endpoint exposes private access (via EndpointAccess.PRIVATE or EndpointAccess.PUBLIC_AND_PRIVATE), and the VPC contains private subnets, the Lambda function will be provisioned inside the VPC and use the private subnets to interact with the cluster. This is the common use-case.

If the endpoint does not expose private access (via EndpointAccess.PUBLIC) or the VPC does not contain private subnets, the function will not be provisioned within the VPC.

If your use-case requires control over the IAM role that the KubeCtl Handler assumes, a custom role can be passed through the ClusterProps (as kubectlLambdaRole) of the EKS Cluster construct.

Cluster Handler ​

The ClusterHandler is a set of Lambda functions (onEventHandler, isCompleteHandler) responsible for interacting with the EKS API in order to control the cluster lifecycle. To provision these functions inside the VPC, set the placeClusterHandlerInVpc property to true. This will place the functions inside the private subnets of the VPC based on the selection strategy specified in the vpcSubnets property.

You can configure the environment of the Cluster Handler functions by specifying it at cluster instantiation. For example, this can be useful in order to configure an http proxy:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

declare const proxyInstanceSecurityGroup: ec2.SecurityGroup;
const cluster = new eks.Cluster(this, 'hello-eks', {
  version: eks.KubernetesVersion.V1_36,
  clusterHandlerEnvironment: {
    https_proxy: 'http://proxy.myproxy.com',
  },
  /**
   * If the proxy is not open publicly, you can pass a security group to the
   * Cluster Handler Lambdas so that it can reach the proxy.
   */
  clusterHandlerSecurityGroup: proxyInstanceSecurityGroup,
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer

# proxy_instance_security_group: ec2.SecurityGroup

cluster = eks.Cluster(self, "hello-eks",
    version=eks.KubernetesVersion.V1_36,
    cluster_handler_environment={
        "https_proxy": "http://proxy.myproxy.com"
    },
    #
    # If the proxy is not open publicly, you can pass a security group to the
    # Cluster Handler Lambdas so that it can reach the proxy.
    #
    cluster_handler_security_group=proxy_instance_security_group,
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;

SecurityGroup proxyInstanceSecurityGroup;

Cluster cluster = Cluster.Builder.create(this, "hello-eks")
        .version(KubernetesVersion.V1_36)
        .clusterHandlerEnvironment(Map.of(
                "https_proxy", "http://proxy.myproxy.com"))
        /**
           * If the proxy is not open publicly, you can pass a security group to the
           * Cluster Handler Lambdas so that it can reach the proxy.
           */
        .clusterHandlerSecurityGroup(proxyInstanceSecurityGroup)
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;

SecurityGroup proxyInstanceSecurityGroup;

var cluster = new Cluster(this, "hello-eks", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    ClusterHandlerEnvironment = new Dictionary<string, string> {
        { "https_proxy", "http://proxy.myproxy.com" }
    },
    /**
       * If the proxy is not open publicly, you can pass a security group to the
       * Cluster Handler Lambdas so that it can reach the proxy.
       */
    ClusterHandlerSecurityGroup = proxyInstanceSecurityGroup,
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"

var proxyInstanceSecurityGroup SecurityGroup

cluster := eks.NewCluster(this, jsii.String("hello-eks"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	ClusterHandlerEnvironment: map[string]*string{
		"https_proxy": jsii.String("http://proxy.myproxy.com"),
	},
	/**
	   * If the proxy is not open publicly, you can pass a security group to the
	   * Cluster Handler Lambdas so that it can reach the proxy.
	   */
	ClusterHandlerSecurityGroup: proxyInstanceSecurityGroup,
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

IPv6 Support ​

You can optionally choose to configure your cluster to use IPv6 using the ipFamily definition for your cluster. Note that this will require the underlying subnets to have an associated IPv6 CIDR.

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';
declare const vpc: ec2.Vpc;

function associateSubnetWithV6Cidr(vpc: ec2.Vpc, count: number, subnet: ec2.ISubnet) {
  const cfnSubnet = subnet.node.defaultChild as ec2.CfnSubnet;
  cfnSubnet.ipv6CidrBlock = Fn.select(count, Fn.cidr(Fn.select(0, vpc.vpcIpv6CidrBlocks), 256, (128 - 64).toString()));
  cfnSubnet.assignIpv6AddressOnCreation = true;
}

// make an ipv6 cidr
const ipv6cidr = new ec2.CfnVPCCidrBlock(this, 'CIDR6', {
  vpcId: vpc.vpcId,
  amazonProvidedIpv6CidrBlock: true,
});

// connect the ipv6 cidr to all vpc subnets
let subnetcount = 0;
const subnets = vpc.publicSubnets.concat(vpc.privateSubnets);
for (let subnet of subnets) {
  // Wait for the ipv6 cidr to complete
  subnet.node.addDependency(ipv6cidr);
  associateSubnetWithV6Cidr(vpc, subnetcount, subnet);
  subnetcount = subnetcount + 1;
}

const cluster = new eks.Cluster(this, 'hello-eks', {
  version: eks.KubernetesVersion.V1_36,
  vpc: vpc,
  ipFamily: eks.IpFamily.IP_V6,
  vpcSubnets: [{ subnets: vpc.publicSubnets }],
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer
# vpc: ec2.Vpc


def associate_subnet_with_v6_cidr(self, vpc, count, subnet):
    cfn_subnet = subnet.node.default_child
    cfn_subnet.ipv6_cidr_block = Fn.select(count, Fn.cidr(Fn.select(0, vpc.vpc_ipv6_cidr_blocks), 256, (128 - 64).to_string()))
    cfn_subnet.assign_ipv6_address_on_creation = True

# make an ipv6 cidr
ipv6cidr = ec2.CfnVPCCidrBlock(self, "CIDR6",
    vpc_id=vpc.vpc_id,
    amazon_provided_ipv6_cidr_block=True
)

# connect the ipv6 cidr to all vpc subnets
subnetcount = 0
subnets = vpc.public_subnets.concat(vpc.private_subnets)
for subnet in subnets:
    # Wait for the ipv6 cidr to complete
    subnet.node.add_dependency(ipv6cidr)
    associate_subnet_with_v6_cidr(vpc, subnetcount, subnet)
    subnetcount = subnetcount + 1

cluster = eks.Cluster(self, "hello-eks",
    version=eks.KubernetesVersion.V1_36,
    vpc=vpc,
    ip_family=eks.IpFamily.IP_V6,
    vpc_subnets=[ec2.SubnetSelection(subnets=vpc.public_subnets)],
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;
Vpc vpc;


public void associateSubnetWithV6Cidr(Vpc vpc, Number count, ISubnet subnet) {
    CfnSubnet cfnSubnet = (CfnSubnet)subnet.getNode().getDefaultChild();
    cfnSubnet.getIpv6CidrBlock() = Fn.select(count, Fn.cidr(Fn.select(0, vpc.getVpcIpv6CidrBlocks()), 256, (128 - 64).toString()));
    cfnSubnet.getAssignIpv6AddressOnCreation() = true;
}

// make an ipv6 cidr
CfnVPCCidrBlock ipv6cidr = CfnVPCCidrBlock.Builder.create(this, "CIDR6")
        .vpcId(vpc.getVpcId())
        .amazonProvidedIpv6CidrBlock(true)
        .build();

// connect the ipv6 cidr to all vpc subnets
Number subnetcount = 0;
ISubnet[] subnets = vpc.publicSubnets.concat(vpc.getPrivateSubnets());
for (Object subnet : subnets) {
    // Wait for the ipv6 cidr to complete
    subnet.node.addDependency(ipv6cidr);
    associateSubnetWithV6Cidr(vpc, subnetcount, subnet);
    subnetcount = subnetcount + 1;
}

Cluster cluster = Cluster.Builder.create(this, "hello-eks")
        .version(KubernetesVersion.V1_36)
        .vpc(vpc)
        .ipFamily(IpFamily.IP_V6)
        .vpcSubnets(List.of(SubnetSelection.builder().subnets(vpc.getPublicSubnets()).build()))
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;
Vpc vpc;


public void AssociateSubnetWithV6Cidr(Vpc vpc, int count, ISubnet subnet)
{
    var cfnSubnet = (CfnSubnet)subnet.Node.DefaultChild;
    cfnSubnet.Ipv6CidrBlock = Fn.Select(count, Fn.Cidr(Fn.Select(0, vpc.VpcIpv6CidrBlocks), 256, (128 - 64).ToString()));
    cfnSubnet.AssignIpv6AddressOnCreation = true;
}

// make an ipv6 cidr
var ipv6cidr = new CfnVPCCidrBlock(this, "CIDR6", new CfnVPCCidrBlockProps {
    VpcId = vpc.VpcId,
    AmazonProvidedIpv6CidrBlock = true
});

// connect the ipv6 cidr to all vpc subnets
var subnetcount = 0;
var subnets = vpc.PublicSubnets.Concat(vpc.PrivateSubnets);
for (var subnet in subnets)
{
    // Wait for the ipv6 cidr to complete
    subnet.Node.AddDependency(ipv6cidr);
    AssociateSubnetWithV6Cidr(vpc, subnetcount, subnet);
    subnetcount = subnetcount + 1;
}

var cluster = new Cluster(this, "hello-eks", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    Vpc = vpc,
    IpFamily = IpFamily.IP_V6,
    VpcSubnets = new [] { new SubnetSelection { Subnets = vpc.PublicSubnets } },
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"
var vpc Vpc


func associateSubnetWithV6Cidr(vpc Vpc, count *f64, subnet ISubnet) {
	cfnSubnet := *subnet.Node.defaultChild.(CfnSubnet)
	cfnSubnet.ipv6CidrBlock = awscdk.Fn_Select(*count, awscdk.Fn_Cidr(awscdk.Fn_Select(jsii.Number(0), *vpc.VpcIpv6CidrBlocks), jsii.Number(256), (jsii.Number(128 - 64)).toString()))
	cfnSubnet.assignIpv6AddressOnCreation = true
}

// make an ipv6 cidr
ipv6cidr := ec2.NewCfnVPCCidrBlock(this, jsii.String("CIDR6"), &CfnVPCCidrBlockProps{
	VpcId: vpc.VpcId,
	AmazonProvidedIpv6CidrBlock: jsii.Boolean(true),
})

// connect the ipv6 cidr to all vpc subnets
subnetcount := 0
subnets := vpc.PublicSubnets.concat(vpc.PrivateSubnets)
for _, subnet := range subnets {
	// Wait for the ipv6 cidr to complete
	subnet.Node.AddDependency(ipv6cidr)
	associateSubnetWithV6Cidr(vpc, subnetcount, subnet)
	subnetcount = subnetcount + 1
}

cluster := eks.NewCluster(this, jsii.String("hello-eks"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	Vpc: vpc,
	IpFamily: eks.IpFamily_IP_V6,
	VpcSubnets: []SubnetSelection{
		&SubnetSelection{
			Subnets: vpc.*PublicSubnets,
		},
	},
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

Kubectl Support ​

The resources are created in the cluster by running kubectl apply from a python lambda function.

By default, CDK will create a new python lambda function to apply your k8s manifests. If you want to use an existing kubectl provider function, for example with tight trusted entities on your IAM Roles - you can import the existing provider and then use the imported provider when importing the cluster:

ts
const handlerRole = iam.Role.fromRoleArn(this, 'HandlerRole', 'arn:aws:iam::123456789012:role/lambda-role');
// get the serviceToken from the custom resource provider
const functionArn = lambda.Function.fromFunctionName(this, 'ProviderOnEventFunc', 'ProviderframeworkonEvent-XXX').functionArn;
const kubectlProvider = eks.KubectlProvider.fromKubectlProviderAttributes(this, 'KubectlProvider', {
  functionArn,
  kubectlRoleArn: 'arn:aws:iam::123456789012:role/kubectl-role',
  handlerRole,
});

const cluster = eks.Cluster.fromClusterAttributes(this, 'Cluster', {
  clusterName: 'cluster',
  kubectlProvider,
});
python
handler_role = iam.Role.from_role_arn(self, "HandlerRole", "arn:aws:iam::123456789012:role/lambda-role")
# get the serviceToken from the custom resource provider
function_arn = lambda_.Function.from_function_name(self, "ProviderOnEventFunc", "ProviderframeworkonEvent-XXX").function_arn
kubectl_provider = eks.KubectlProvider.from_kubectl_provider_attributes(self, "KubectlProvider",
    function_arn=function_arn,
    kubectl_role_arn="arn:aws:iam::123456789012:role/kubectl-role",
    handler_role=handler_role
)

cluster = eks.Cluster.from_cluster_attributes(self, "Cluster",
    cluster_name="cluster",
    kubectl_provider=kubectl_provider
)
java
IRole handlerRole = Role.fromRoleArn(this, "HandlerRole", "arn:aws:iam::123456789012:role/lambda-role");
// get the serviceToken from the custom resource provider
String functionArn = Function.fromFunctionName(this, "ProviderOnEventFunc", "ProviderframeworkonEvent-XXX").getFunctionArn();
IKubectlProvider kubectlProvider = KubectlProvider.fromKubectlProviderAttributes(this, "KubectlProvider", KubectlProviderAttributes.builder()
        .functionArn(functionArn)
        .kubectlRoleArn("arn:aws:iam::123456789012:role/kubectl-role")
        .handlerRole(handlerRole)
        .build());

ICluster cluster = Cluster.fromClusterAttributes(this, "Cluster", ClusterAttributes.builder()
        .clusterName("cluster")
        .kubectlProvider(kubectlProvider)
        .build());
csharp
var handlerRole = Role.FromRoleArn(this, "HandlerRole", "arn:aws:iam::123456789012:role/lambda-role");
// get the serviceToken from the custom resource provider
var functionArn = Function.FromFunctionName(this, "ProviderOnEventFunc", "ProviderframeworkonEvent-XXX").FunctionArn;
var kubectlProvider = KubectlProvider.FromKubectlProviderAttributes(this, "KubectlProvider", new KubectlProviderAttributes {
    FunctionArn = functionArn,
    KubectlRoleArn = "arn:aws:iam::123456789012:role/kubectl-role",
    HandlerRole = handlerRole
});

var cluster = Cluster.FromClusterAttributes(this, "Cluster", new ClusterAttributes {
    ClusterName = "cluster",
    KubectlProvider = kubectlProvider
});
go
handlerRole := iam.Role_FromRoleArn(this, jsii.String("HandlerRole"), jsii.String("arn:aws:iam::123456789012:role/lambda-role"))
// get the serviceToken from the custom resource provider
functionArn := lambda.Function_FromFunctionName(this, jsii.String("ProviderOnEventFunc"), jsii.String("ProviderframeworkonEvent-XXX")).FunctionArn
kubectlProvider := eks.KubectlProvider_FromKubectlProviderAttributes(this, jsii.String("KubectlProvider"), &KubectlProviderAttributes{
	FunctionArn: jsii.String(FunctionArn),
	KubectlRoleArn: jsii.String("arn:aws:iam::123456789012:role/kubectl-role"),
	HandlerRole: HandlerRole,
})

cluster := eks.Cluster_FromClusterAttributes(this, jsii.String("Cluster"), &ClusterAttributes{
	ClusterName: jsii.String("cluster"),
	KubectlProvider: KubectlProvider,
})

Environment ​

You can configure the environment of this function by specifying it at cluster instantiation. For example, this can be useful in order to configure an http proxy:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

const cluster = new eks.Cluster(this, 'hello-eks', {
  version: eks.KubernetesVersion.V1_36,
  kubectlEnvironment: {
    'http_proxy': 'http://proxy.myproxy.com',
  },
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


cluster = eks.Cluster(self, "hello-eks",
    version=eks.KubernetesVersion.V1_36,
    kubectl_environment={
        "http_proxy": "http://proxy.myproxy.com"
    },
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


Cluster cluster = Cluster.Builder.create(this, "hello-eks")
        .version(KubernetesVersion.V1_36)
        .kubectlEnvironment(Map.of(
                "http_proxy", "http://proxy.myproxy.com"))
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


var cluster = new Cluster(this, "hello-eks", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    KubectlEnvironment = new Dictionary<string, string> {
        { "http_proxy", "http://proxy.myproxy.com" }
    },
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


cluster := eks.NewCluster(this, jsii.String("hello-eks"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	KubectlEnvironment: map[string]*string{
		"http_proxy": jsii.String("http://proxy.myproxy.com"),
	},
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

Runtime ​

The kubectl handler uses kubectl, helm and the aws CLI in order to interact with the cluster. These are bundled into AWS Lambda layers included in the @aws-cdk/lambda-layer-awscli and @aws-cdk/lambda-layer-kubectl modules.

The version of kubectl used must be compatible with the Kubernetes version of the cluster. kubectl is supported within one minor version (older or newer) of Kubernetes (see Kubernetes version skew policy). Depending on which version of kubernetes you're targeting, you will need to use one of the @aws-cdk/lambda-layer-kubectl-vXY packages.

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

const cluster = new eks.Cluster(this, 'hello-eks', {
  version: eks.KubernetesVersion.V1_36,
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


cluster = eks.Cluster(self, "hello-eks",
    version=eks.KubernetesVersion.V1_36,
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


Cluster cluster = Cluster.Builder.create(this, "hello-eks")
        .version(KubernetesVersion.V1_36)
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


var cluster = new Cluster(this, "hello-eks", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


cluster := eks.NewCluster(this, jsii.String("hello-eks"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

You can also specify a custom lambda.LayerVersion if you wish to use a different version of these tools, or a version not available in any of the @aws-cdk/lambda-layer-kubectl-vXY packages. The handler expects the layer to include the following two executables:

text
helm/helm
kubectl/kubectl

See more information in the Dockerfile for @aws-cdk/lambda-layer-awscli and the Dockerfile for @aws-cdk/lambda-layer-kubectl.

ts
const layer = new lambda.LayerVersion(this, 'KubectlLayer', {
  code: lambda.Code.fromAsset('layer.zip'),
});
python
layer = lambda_.LayerVersion(self, "KubectlLayer",
    code=lambda_.Code.from_asset("layer.zip")
)
java
LayerVersion layer = LayerVersion.Builder.create(this, "KubectlLayer")
        .code(Code.fromAsset("layer.zip"))
        .build();
csharp
var layer = new LayerVersion(this, "KubectlLayer", new LayerVersionProps {
    Code = Code.FromAsset("layer.zip")
});
go
layer := lambda.NewLayerVersion(this, jsii.String("KubectlLayer"), &LayerVersionProps{
	Code: lambda.Code_FromAsset(jsii.String("layer.zip")),
})

Now specify when the cluster is defined:

ts
declare const layer: lambda.LayerVersion;
declare const vpc: ec2.Vpc;

const cluster1 = new eks.Cluster(this, 'MyCluster', {
  kubectlLayer: layer,
  vpc,
  clusterName: 'cluster-name',
  version: eks.KubernetesVersion.V1_36,
});

// or
const cluster2 = eks.Cluster.fromClusterAttributes(this, 'MyCluster', {
  kubectlLayer: layer,
  vpc,
  clusterName: 'cluster-name',
});
python
# layer: lambda.LayerVersion
# vpc: ec2.Vpc


cluster1 = eks.Cluster(self, "MyCluster",
    kubectl_layer=layer,
    vpc=vpc,
    cluster_name="cluster-name",
    version=eks.KubernetesVersion.V1_36
)

# or
cluster2 = eks.Cluster.from_cluster_attributes(self, "MyCluster",
    kubectl_layer=layer,
    vpc=vpc,
    cluster_name="cluster-name"
)
java
LayerVersion layer;
Vpc vpc;


Cluster cluster1 = Cluster.Builder.create(this, "MyCluster")
        .kubectlLayer(layer)
        .vpc(vpc)
        .clusterName("cluster-name")
        .version(KubernetesVersion.V1_36)
        .build();

// or
ICluster cluster2 = Cluster.fromClusterAttributes(this, "MyCluster", ClusterAttributes.builder()
        .kubectlLayer(layer)
        .vpc(vpc)
        .clusterName("cluster-name")
        .build());
csharp
LayerVersion layer;
Vpc vpc;


var cluster1 = new Cluster(this, "MyCluster", new ClusterProps {
    KubectlLayer = layer,
    Vpc = vpc,
    ClusterName = "cluster-name",
    Version = KubernetesVersion.V1_36
});

// or
var cluster2 = Cluster.FromClusterAttributes(this, "MyCluster", new ClusterAttributes {
    KubectlLayer = layer,
    Vpc = vpc,
    ClusterName = "cluster-name"
});
go
var layer LayerVersion
var vpc Vpc


cluster1 := eks.NewCluster(this, jsii.String("MyCluster"), &ClusterProps{
	KubectlLayer: layer,
	Vpc: Vpc,
	ClusterName: jsii.String("cluster-name"),
	Version: eks.KubernetesVersion_V1_36(),
})

// or
cluster2 := eks.Cluster_FromClusterAttributes(this, jsii.String("MyCluster"), &ClusterAttributes{
	KubectlLayer: layer,
	Vpc: Vpc,
	ClusterName: jsii.String("cluster-name"),
})

Memory ​

By default, the kubectl provider is configured with 1024MiB of memory. You can use the kubectlMemory option to specify the memory size for the AWS Lambda function:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

new eks.Cluster(this, 'MyCluster', {
  kubectlMemory: Size.gibibytes(4),
  version: eks.KubernetesVersion.V1_36,
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});

// or
declare const vpc: ec2.Vpc;
eks.Cluster.fromClusterAttributes(this, 'MyCluster', {
  kubectlMemory: Size.gibibytes(4),
  vpc,
  clusterName: 'cluster-name',
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer

# or
# vpc: ec2.Vpc


eks.Cluster(self, "MyCluster",
    kubectl_memory=Size.gibibytes(4),
    version=eks.KubernetesVersion.V1_36,
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
eks.Cluster.from_cluster_attributes(self, "MyCluster",
    kubectl_memory=Size.gibibytes(4),
    vpc=vpc,
    cluster_name="cluster-name"
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;

// or
Vpc vpc;


Cluster.Builder.create(this, "MyCluster")
        .kubectlMemory(Size.gibibytes(4))
        .version(KubernetesVersion.V1_36)
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
Cluster.fromClusterAttributes(this, "MyCluster", ClusterAttributes.builder()
        .kubectlMemory(Size.gibibytes(4))
        .vpc(vpc)
        .clusterName("cluster-name")
        .build());
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;

// or
Vpc vpc;


new Cluster(this, "MyCluster", new ClusterProps {
    KubectlMemory = Size.Gibibytes(4),
    Version = KubernetesVersion.V1_36,
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
Cluster.FromClusterAttributes(this, "MyCluster", new ClusterAttributes {
    KubectlMemory = Size.Gibibytes(4),
    Vpc = vpc,
    ClusterName = "cluster-name"
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"

// or
var vpc Vpc


eks.NewCluster(this, jsii.String("MyCluster"), &ClusterProps{
	KubectlMemory: awscdk.Size_Gibibytes(jsii.Number(4)),
	Version: eks.KubernetesVersion_V1_36(),
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})
eks.Cluster_FromClusterAttributes(this, jsii.String("MyCluster"), &ClusterAttributes{
	KubectlMemory: awscdk.Size_*Gibibytes(jsii.Number(4)),
	Vpc: Vpc,
	ClusterName: jsii.String("cluster-name"),
})

ARM64 Support ​

Instance types with ARM64 architecture are supported in both managed nodegroup and self-managed capacity. Simply specify an ARM64 instanceType (such as m6g.medium), and the latest Amazon Linux 2 AMI for ARM64 will be automatically selected.

ts
declare const cluster: eks.Cluster;
// add a managed ARM64 nodegroup
cluster.addNodegroupCapacity('extra-ng-arm', {
  instanceTypes: [new ec2.InstanceType('m6g.medium')],
  minSize: 2,
});

// add a self-managed ARM64 nodegroup
cluster.addAutoScalingGroupCapacity('self-ng-arm', {
  instanceType: new ec2.InstanceType('m6g.medium'),
  minCapacity: 2,
})
python
# cluster: eks.Cluster

# add a managed ARM64 nodegroup
cluster.add_nodegroup_capacity("extra-ng-arm",
    instance_types=[ec2.InstanceType("m6g.medium")],
    min_size=2
)

# add a self-managed ARM64 nodegroup
cluster.add_auto_scaling_group_capacity("self-ng-arm",
    instance_type=ec2.InstanceType("m6g.medium"),
    min_capacity=2
)
java
Cluster cluster;

// add a managed ARM64 nodegroup
cluster.addNodegroupCapacity("extra-ng-arm", NodegroupOptions.builder()
        .instanceTypes(List.of(new InstanceType("m6g.medium")))
        .minSize(2)
        .build());

// add a self-managed ARM64 nodegroup
cluster.addAutoScalingGroupCapacity("self-ng-arm", AutoScalingGroupCapacityOptions.builder()
        .instanceType(new InstanceType("m6g.medium"))
        .minCapacity(2)
        .build());
csharp
Cluster cluster;

// add a managed ARM64 nodegroup
cluster.AddNodegroupCapacity("extra-ng-arm", new NodegroupOptions {
    InstanceTypes = new [] { new InstanceType("m6g.medium") },
    MinSize = 2
});

// add a self-managed ARM64 nodegroup
cluster.AddAutoScalingGroupCapacity("self-ng-arm", new AutoScalingGroupCapacityOptions {
    InstanceType = new InstanceType("m6g.medium"),
    MinCapacity = 2
});
go
var cluster Cluster

// add a managed ARM64 nodegroup
cluster.AddNodegroupCapacity(jsii.String("extra-ng-arm"), &NodegroupOptions{
	InstanceTypes: []InstanceType{
		ec2.NewInstanceType(jsii.String("m6g.medium")),
	},
	MinSize: jsii.Number(2),
})

// add a self-managed ARM64 nodegroup
cluster.AddAutoScalingGroupCapacity(jsii.String("self-ng-arm"), &AutoScalingGroupCapacityOptions{
	InstanceType: ec2.NewInstanceType(jsii.String("m6g.medium")),
	MinCapacity: jsii.Number(2),
})

Masters Role ​

When you create a cluster, you can specify a mastersRole. The Cluster construct will associate this role with the system:masters RBAC group, giving it super-user access to the cluster.

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

declare const role: iam.Role;
new eks.Cluster(this, 'HelloEKS', {
  version: eks.KubernetesVersion.V1_36,
  mastersRole: role,
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer

# role: iam.Role

eks.Cluster(self, "HelloEKS",
    version=eks.KubernetesVersion.V1_36,
    masters_role=role,
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;

Role role;

Cluster.Builder.create(this, "HelloEKS")
        .version(KubernetesVersion.V1_36)
        .mastersRole(role)
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;

Role role;

new Cluster(this, "HelloEKS", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    MastersRole = role,
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"

var role Role

eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	MastersRole: role,
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

In order to interact with your cluster through kubectl, you can use the aws eks update-kubeconfig AWS CLI command to configure your local kubeconfig. The EKS module will define a CloudFormation output in your stack which contains the command to run. For example:

plaintext
Outputs:
ClusterConfigCommand43AAE40F = aws eks update-kubeconfig --name cluster-xxxxx --role-arn arn:aws:iam::112233445566:role/yyyyy

Execute the aws eks update-kubeconfig ... command in your terminal to create or update a local kubeconfig context:

console
$ aws eks update-kubeconfig --name cluster-xxxxx --role-arn arn:aws:iam::112233445566:role/yyyyy
Added new context arn:aws:eks:rrrrr:112233445566:cluster/cluster-xxxxx to /home/boom/.kube/config

And now you can simply use kubectl:

console
$ kubectl get all -n kube-system
NAME                           READY   STATUS    RESTARTS   AGE
pod/aws-node-fpmwv             1/1     Running   0          21m
pod/aws-node-m9htf             1/1     Running   0          21m
pod/coredns-5cb4fb54c7-q222j   1/1     Running   0          23m
pod/coredns-5cb4fb54c7-v9nxx   1/1     Running   0          23m
...

If you do not specify it, you won't have access to the cluster from outside of the CDK application.

Note that cluster.addManifest and new KubernetesManifest will still work.

Encryption ​

When you create an Amazon EKS cluster, envelope encryption of Kubernetes secrets using the AWS Key Management Service (AWS KMS) can be enabled. The documentation on creating a cluster can provide more details about the customer master key (CMK) that can be used for the encryption.

You can use the secretsEncryptionKey to configure which key the cluster will use to encrypt Kubernetes secrets. By default, an AWS Managed key will be used.

This setting can only be specified when the cluster is created and cannot be updated.

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

const secretsKey = new kms.Key(this, 'SecretsKey');
const cluster = new eks.Cluster(this, 'MyCluster', {
  secretsEncryptionKey: secretsKey,
  version: eks.KubernetesVersion.V1_36,
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


secrets_key = kms.Key(self, "SecretsKey")
cluster = eks.Cluster(self, "MyCluster",
    secrets_encryption_key=secrets_key,
    version=eks.KubernetesVersion.V1_36,
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


Key secretsKey = new Key(this, "SecretsKey");
Cluster cluster = Cluster.Builder.create(this, "MyCluster")
        .secretsEncryptionKey(secretsKey)
        .version(KubernetesVersion.V1_36)
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


var secretsKey = new Key(this, "SecretsKey");
var cluster = new Cluster(this, "MyCluster", new ClusterProps {
    SecretsEncryptionKey = secretsKey,
    Version = KubernetesVersion.V1_36,
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


secretsKey := kms.NewKey(this, jsii.String("SecretsKey"))
cluster := eks.NewCluster(this, jsii.String("MyCluster"), &ClusterProps{
	SecretsEncryptionKey: secretsKey,
	Version: eks.KubernetesVersion_V1_36(),
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

You can also use a similar configuration for running a cluster built using the FargateCluster construct.

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

const secretsKey = new kms.Key(this, 'SecretsKey');
const cluster = new eks.FargateCluster(this, 'MyFargateCluster', {
  secretsEncryptionKey: secretsKey,
  version: eks.KubernetesVersion.V1_36,
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


secrets_key = kms.Key(self, "SecretsKey")
cluster = eks.FargateCluster(self, "MyFargateCluster",
    secrets_encryption_key=secrets_key,
    version=eks.KubernetesVersion.V1_36,
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


Key secretsKey = new Key(this, "SecretsKey");
FargateCluster cluster = FargateCluster.Builder.create(this, "MyFargateCluster")
        .secretsEncryptionKey(secretsKey)
        .version(KubernetesVersion.V1_36)
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


var secretsKey = new Key(this, "SecretsKey");
var cluster = new FargateCluster(this, "MyFargateCluster", new FargateClusterProps {
    SecretsEncryptionKey = secretsKey,
    Version = KubernetesVersion.V1_36,
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


secretsKey := kms.NewKey(this, jsii.String("SecretsKey"))
cluster := eks.NewFargateCluster(this, jsii.String("MyFargateCluster"), &FargateClusterProps{
	SecretsEncryptionKey: secretsKey,
	Version: eks.KubernetesVersion_V1_36(),
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

The Amazon Resource Name (ARN) for that CMK can be retrieved.

ts
declare const cluster: eks.Cluster;
const clusterEncryptionConfigKeyArn = cluster.clusterEncryptionConfigKeyArn;
python
# cluster: eks.Cluster

cluster_encryption_config_key_arn = cluster.cluster_encryption_config_key_arn
java
Cluster cluster;

String clusterEncryptionConfigKeyArn = cluster.getClusterEncryptionConfigKeyArn();
csharp
Cluster cluster;

var clusterEncryptionConfigKeyArn = cluster.ClusterEncryptionConfigKeyArn;
go
var cluster Cluster

clusterEncryptionConfigKeyArn := cluster.ClusterEncryptionConfigKeyArn

Hybrid Nodes ​

When you create an Amazon EKS cluster, you can configure it to leverage the EKS Hybrid Nodes feature, allowing you to use your on-premises and edge infrastructure as nodes in your EKS cluster. Refer to the Hyrid Nodes networking documentation to configure your on-premises network, node and pod CIDRs, access control, etc before creating your EKS Cluster.

Once you have identified the on-premises node and pod (optional) CIDRs you will use for your hybrid nodes and the workloads running on them, you can specify them during cluster creation using the remoteNodeNetworks and remotePodNetworks (optional) properties:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

new eks.Cluster(this, 'Cluster', {
  version: eks.KubernetesVersion.V1_36,
  kubectlLayer: new KubectlV36Layer(this, 'KubectlLayer'),
  remoteNodeNetworks: [
    {
      cidrs: ['10.0.0.0/16'],
    },
  ],
  remotePodNetworks: [
    {
      cidrs: ['192.168.0.0/16'],
    },
  ],
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


eks.Cluster(self, "Cluster",
    version=eks.KubernetesVersion.V1_36,
    kubectl_layer=KubectlV36Layer(self, "KubectlLayer"),
    remote_node_networks=[eks.RemoteNodeNetwork(
        cidrs=["10.0.0.0/16"]
    )
    ],
    remote_pod_networks=[eks.RemotePodNetwork(
        cidrs=["192.168.0.0/16"]
    )
    ]
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


Cluster.Builder.create(this, "Cluster")
        .version(KubernetesVersion.V1_36)
        .kubectlLayer(new KubectlV36Layer(this, "KubectlLayer"))
        .remoteNodeNetworks(List.of(RemoteNodeNetwork.builder()
                .cidrs(List.of("10.0.0.0/16"))
                .build()))
        .remotePodNetworks(List.of(RemotePodNetwork.builder()
                .cidrs(List.of("192.168.0.0/16"))
                .build()))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


new Cluster(this, "Cluster", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    KubectlLayer = new KubectlV36Layer(this, "KubectlLayer"),
    RemoteNodeNetworks = new [] { new RemoteNodeNetwork {
        Cidrs = new [] { "10.0.0.0/16" }
    } },
    RemotePodNetworks = new [] { new RemotePodNetwork {
        Cidrs = new [] { "192.168.0.0/16" }
    } }
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


eks.NewCluster(this, jsii.String("Cluster"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("KubectlLayer")),
	RemoteNodeNetworks: []RemoteNodeNetwork{
		&RemoteNodeNetwork{
			Cidrs: []*string{
				jsii.String("10.0.0.0/16"),
			},
		},
	},
	RemotePodNetworks: []RemotePodNetwork{
		&RemotePodNetwork{
			Cidrs: []*string{
				jsii.String("192.168.0.0/16"),
			},
		},
	},
})

Self-Managed Add-ons ​

Amazon EKS automatically installs self-managed add-ons such as the Amazon VPC CNI plugin for Kubernetes, kube-proxy, and CoreDNS for every cluster. You can change the default configuration of the add-ons and update them when desired. If you wish to create a cluster without the default add-ons, set bootstrapSelfManagedAddons as false. When this is set to false, make sure to install the necessary alternatives which provide functionality that enables pod and service operations for your EKS cluster.

Changing the value of bootstrapSelfManagedAddons after the EKS cluster creation will result in a replacement of the cluster.

Permissions and Security ​

Amazon EKS provides several mechanism of securing the cluster and granting permissions to specific IAM users and roles.

AWS IAM Mapping ​

As described in the Amazon EKS User Guide, you can map AWS IAM users and roles to Kubernetes Role-based access control (RBAC).

The Amazon EKS construct manages the aws-auth ConfigMap Kubernetes resource on your behalf and exposes an API through the cluster.awsAuth for mapping users, roles and accounts.

Furthermore, when auto-scaling group capacity is added to the cluster, the IAM instance role of the auto-scaling group will be automatically mapped to RBAC so nodes can connect to the cluster. No manual mapping is required.

For example, let's say you want to grant an IAM user administrative privileges on your cluster:

ts
declare const cluster: eks.Cluster;
const adminUser = new iam.User(this, 'Admin');
cluster.awsAuth.addUserMapping(adminUser, { groups: [ 'system:masters' ]});
python
# cluster: eks.Cluster

admin_user = iam.User(self, "Admin")
cluster.aws_auth.add_user_mapping(admin_user, groups=["system:masters"])
java
Cluster cluster;

User adminUser = new User(this, "Admin");
cluster.awsAuth.addUserMapping(adminUser, AwsAuthMapping.builder().groups(List.of("system:masters")).build());
csharp
Cluster cluster;

var adminUser = new User(this, "Admin");
cluster.AwsAuth.AddUserMapping(adminUser, new AwsAuthMapping { Groups = new [] { "system:masters" } });
go
var cluster Cluster

adminUser := iam.NewUser(this, jsii.String("Admin"))
cluster.awsAuth.AddUserMapping(adminUser, &AwsAuthMapping{
	Groups: []*string{
		jsii.String("system:masters"),
	},
})

A convenience method for mapping a role to the system:masters group is also available:

ts
declare const cluster: eks.Cluster;
declare const role: iam.Role;
cluster.awsAuth.addMastersRole(role);
python
# cluster: eks.Cluster
# role: iam.Role

cluster.aws_auth.add_masters_role(role)
java
Cluster cluster;
Role role;

cluster.awsAuth.addMastersRole(role);
csharp
Cluster cluster;
Role role;

cluster.AwsAuth.AddMastersRole(role);
go
var cluster Cluster
var role Role

cluster.awsAuth.AddMastersRole(role)

To access the Kubernetes resources from the console, make sure your viewing principal is defined in the aws-auth ConfigMap. Some options to consider:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';
declare const cluster: eks.Cluster;
declare const your_current_role: iam.Role;
declare const vpc: ec2.Vpc;

// Option 1: Add your current assumed IAM role to system:masters. Make sure to add relevant policies.
cluster.awsAuth.addMastersRole(your_current_role);

your_current_role.addToPolicy(new iam.PolicyStatement({
  actions: [
    'eks:AccessKubernetesApi',
    'eks:Describe*',
    'eks:List*',
],
  resources: [ cluster.clusterArn ],
}));
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer
# cluster: eks.Cluster
# your_current_role: iam.Role
# vpc: ec2.Vpc


# Option 1: Add your current assumed IAM role to system:masters. Make sure to add relevant policies.
cluster.aws_auth.add_masters_role(your_current_role)

your_current_role.add_to_policy(iam.PolicyStatement(
    actions=["eks:AccessKubernetesApi", "eks:Describe*", "eks:List*"
    ],
    resources=[cluster.cluster_arn]
))
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;
Cluster cluster;
Role your_current_role;
Vpc vpc;


// Option 1: Add your current assumed IAM role to system:masters. Make sure to add relevant policies.
cluster.awsAuth.addMastersRole(your_current_role);

your_current_role.addToPolicy(PolicyStatement.Builder.create()
        .actions(List.of("eks:AccessKubernetesApi", "eks:Describe*", "eks:List*"))
        .resources(List.of(cluster.getClusterArn()))
        .build());
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;
Cluster cluster;
Role your_current_role;
Vpc vpc;


// Option 1: Add your current assumed IAM role to system:masters. Make sure to add relevant policies.
cluster.AwsAuth.AddMastersRole(your_current_role);

your_current_role.AddToPolicy(new PolicyStatement(new PolicyStatementProps {
    Actions = new [] { "eks:AccessKubernetesApi", "eks:Describe*", "eks:List*" },
    Resources = new [] { cluster.ClusterArn }
}));
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"
var cluster Cluster
var your_current_role Role
var vpc Vpc


// Option 1: Add your current assumed IAM role to system:masters. Make sure to add relevant policies.
cluster.awsAuth.AddMastersRole(your_current_role)

your_current_role.AddToPolicy(iam.NewPolicyStatement(&PolicyStatementProps{
	Actions: []*string{
		jsii.String("eks:AccessKubernetesApi"),
		jsii.String("eks:Describe*"),
		jsii.String("eks:List*"),
	},
	Resources: []*string{
		cluster.clusterArn,
	},
}))
ts
// Option 2: create your custom mastersRole with scoped assumeBy arn as the Cluster prop. Switch to this role from the AWS console.
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';
declare const vpc: ec2.Vpc;


const mastersRole = new iam.Role(this, 'MastersRole', {
  assumedBy: new iam.ArnPrincipal('arn_for_trusted_principal'),
});

const cluster = new eks.Cluster(this, 'EksCluster', {
  vpc,
  version: eks.KubernetesVersion.V1_36,
  kubectlLayer: new KubectlV36Layer(this, 'KubectlLayer'),
  mastersRole,
});

mastersRole.addToPolicy(new iam.PolicyStatement({
  actions: [
    'eks:AccessKubernetesApi',
    'eks:Describe*',
    'eks:List*',
],
  resources: [ cluster.clusterArn ],
}));
python
# Option 2: create your custom mastersRole with scoped assumeBy arn as the Cluster prop. Switch to this role from the AWS console.
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer
# vpc: ec2.Vpc


masters_role = iam.Role(self, "MastersRole",
    assumed_by=iam.ArnPrincipal("arn_for_trusted_principal")
)

cluster = eks.Cluster(self, "EksCluster",
    vpc=vpc,
    version=eks.KubernetesVersion.V1_36,
    kubectl_layer=KubectlV36Layer(self, "KubectlLayer"),
    masters_role=masters_role
)

masters_role.add_to_policy(iam.PolicyStatement(
    actions=["eks:AccessKubernetesApi", "eks:Describe*", "eks:List*"
    ],
    resources=[cluster.cluster_arn]
))
java
// Option 2: create your custom mastersRole with scoped assumeBy arn as the Cluster prop. Switch to this role from the AWS console.
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;
Vpc vpc;


Role mastersRole = Role.Builder.create(this, "MastersRole")
        .assumedBy(new ArnPrincipal("arn_for_trusted_principal"))
        .build();

Cluster cluster = Cluster.Builder.create(this, "EksCluster")
        .vpc(vpc)
        .version(KubernetesVersion.V1_36)
        .kubectlLayer(new KubectlV36Layer(this, "KubectlLayer"))
        .mastersRole(mastersRole)
        .build();

mastersRole.addToPolicy(PolicyStatement.Builder.create()
        .actions(List.of("eks:AccessKubernetesApi", "eks:Describe*", "eks:List*"))
        .resources(List.of(cluster.getClusterArn()))
        .build());
csharp
// Option 2: create your custom mastersRole with scoped assumeBy arn as the Cluster prop. Switch to this role from the AWS console.
using Amazon.CDK.LambdaLayer.KubectlV36;
Vpc vpc;


var mastersRole = new Role(this, "MastersRole", new RoleProps {
    AssumedBy = new ArnPrincipal("arn_for_trusted_principal")
});

var cluster = new Cluster(this, "EksCluster", new ClusterProps {
    Vpc = vpc,
    Version = KubernetesVersion.V1_36,
    KubectlLayer = new KubectlV36Layer(this, "KubectlLayer"),
    MastersRole = mastersRole
});

mastersRole.AddToPolicy(new PolicyStatement(new PolicyStatementProps {
    Actions = new [] { "eks:AccessKubernetesApi", "eks:Describe*", "eks:List*" },
    Resources = new [] { cluster.ClusterArn }
}));
go
// Option 2: create your custom mastersRole with scoped assumeBy arn as the Cluster prop. Switch to this role from the AWS console.
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"
var vpc Vpc


mastersRole := iam.NewRole(this, jsii.String("MastersRole"), &RoleProps{
	AssumedBy: iam.NewArnPrincipal(jsii.String("arn_for_trusted_principal")),
})

cluster := eks.NewCluster(this, jsii.String("EksCluster"), &ClusterProps{
	Vpc: Vpc,
	Version: eks.KubernetesVersion_V1_36(),
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("KubectlLayer")),
	MastersRole: MastersRole,
})

mastersRole.AddToPolicy(iam.NewPolicyStatement(&PolicyStatementProps{
	Actions: []*string{
		jsii.String("eks:AccessKubernetesApi"),
		jsii.String("eks:Describe*"),
		jsii.String("eks:List*"),
	},
	Resources: []*string{
		cluster.clusterArn,
	},
}))
ts
// Option 3: Create a new role that allows the account root principal to assume. Add this role in the `system:masters` and witch to this role from the AWS console.
declare const cluster: eks.Cluster;

const consoleReadOnlyRole = new iam.Role(this, 'ConsoleReadOnlyRole', {
  assumedBy: new iam.ArnPrincipal('arn_for_trusted_principal'),
});
consoleReadOnlyRole.addToPolicy(new iam.PolicyStatement({
  actions: [
    'eks:AccessKubernetesApi',
    'eks:Describe*',
    'eks:List*',
],
  resources: [ cluster.clusterArn ],
}));

// Add this role to system:masters RBAC group
cluster.awsAuth.addMastersRole(consoleReadOnlyRole)
python
# Option 3: Create a new role that allows the account root principal to assume. Add this role in the `system:masters` and witch to this role from the AWS console.
# cluster: eks.Cluster


console_read_only_role = iam.Role(self, "ConsoleReadOnlyRole",
    assumed_by=iam.ArnPrincipal("arn_for_trusted_principal")
)
console_read_only_role.add_to_policy(iam.PolicyStatement(
    actions=["eks:AccessKubernetesApi", "eks:Describe*", "eks:List*"
    ],
    resources=[cluster.cluster_arn]
))

# Add this role to system:masters RBAC group
cluster.aws_auth.add_masters_role(console_read_only_role)
java
// Option 3: Create a new role that allows the account root principal to assume. Add this role in the `system:masters` and witch to this role from the AWS console.
Cluster cluster;


Role consoleReadOnlyRole = Role.Builder.create(this, "ConsoleReadOnlyRole")
        .assumedBy(new ArnPrincipal("arn_for_trusted_principal"))
        .build();
consoleReadOnlyRole.addToPolicy(PolicyStatement.Builder.create()
        .actions(List.of("eks:AccessKubernetesApi", "eks:Describe*", "eks:List*"))
        .resources(List.of(cluster.getClusterArn()))
        .build());

// Add this role to system:masters RBAC group
cluster.awsAuth.addMastersRole(consoleReadOnlyRole);
csharp
// Option 3: Create a new role that allows the account root principal to assume. Add this role in the `system:masters` and witch to this role from the AWS console.
Cluster cluster;


var consoleReadOnlyRole = new Role(this, "ConsoleReadOnlyRole", new RoleProps {
    AssumedBy = new ArnPrincipal("arn_for_trusted_principal")
});
consoleReadOnlyRole.AddToPolicy(new PolicyStatement(new PolicyStatementProps {
    Actions = new [] { "eks:AccessKubernetesApi", "eks:Describe*", "eks:List*" },
    Resources = new [] { cluster.ClusterArn }
}));

// Add this role to system:masters RBAC group
cluster.AwsAuth.AddMastersRole(consoleReadOnlyRole);
go
// Option 3: Create a new role that allows the account root principal to assume. Add this role in the `system:masters` and witch to this role from the AWS console.
var cluster Cluster


consoleReadOnlyRole := iam.NewRole(this, jsii.String("ConsoleReadOnlyRole"), &RoleProps{
	AssumedBy: iam.NewArnPrincipal(jsii.String("arn_for_trusted_principal")),
})
consoleReadOnlyRole.AddToPolicy(iam.NewPolicyStatement(&PolicyStatementProps{
	Actions: []*string{
		jsii.String("eks:AccessKubernetesApi"),
		jsii.String("eks:Describe*"),
		jsii.String("eks:List*"),
	},
	Resources: []*string{
		cluster.clusterArn,
	},
}))

// Add this role to system:masters RBAC group
cluster.awsAuth.AddMastersRole(consoleReadOnlyRole)

Access Config ​

Amazon EKS supports three modes of authentication: CONFIG_MAP, API_AND_CONFIG_MAP, and API. You can enable cluster to use access entry APIs by using authenticationMode API or API_AND_CONFIG_MAP. Use authenticationMode CONFIG_MAP to continue using aws-auth configMap exclusively. When API_AND_CONFIG_MAP is enabled, the cluster will source authenticated AWS IAM principals from both Amazon EKS access entry APIs and the aws-auth configMap, with priority given to the access entry API.

To specify the authenticationMode:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';
declare const vpc: ec2.Vpc;

new eks.Cluster(this, 'Cluster', {
  vpc,
  version: eks.KubernetesVersion.V1_36,
  kubectlLayer: new KubectlV36Layer(this, 'KubectlLayer'),
  authenticationMode: eks.AuthenticationMode.API_AND_CONFIG_MAP,
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer
# vpc: ec2.Vpc


eks.Cluster(self, "Cluster",
    vpc=vpc,
    version=eks.KubernetesVersion.V1_36,
    kubectl_layer=KubectlV36Layer(self, "KubectlLayer"),
    authentication_mode=eks.AuthenticationMode.API_AND_CONFIG_MAP
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;
Vpc vpc;


Cluster.Builder.create(this, "Cluster")
        .vpc(vpc)
        .version(KubernetesVersion.V1_36)
        .kubectlLayer(new KubectlV36Layer(this, "KubectlLayer"))
        .authenticationMode(AuthenticationMode.API_AND_CONFIG_MAP)
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;
Vpc vpc;


new Cluster(this, "Cluster", new ClusterProps {
    Vpc = vpc,
    Version = KubernetesVersion.V1_36,
    KubectlLayer = new KubectlV36Layer(this, "KubectlLayer"),
    AuthenticationMode = AuthenticationMode.API_AND_CONFIG_MAP
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"
var vpc Vpc


eks.NewCluster(this, jsii.String("Cluster"), &ClusterProps{
	Vpc: Vpc,
	Version: eks.KubernetesVersion_V1_36(),
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("KubectlLayer")),
	AuthenticationMode: eks.AuthenticationMode_API_AND_CONFIG_MAP,
})

Note - Switching authentication modes on an existing cluster is a one-way operation. You can switch from CONFIG_MAP to API_AND_CONFIG_MAP. You can then switch from API_AND_CONFIG_MAP to API. You cannot revert these operations in the opposite direction. Meaning you cannot switch back to CONFIG_MAP or API_AND_CONFIG_MAP from API. And you cannot switch back to CONFIG_MAP from API_AND_CONFIG_MAP.

Read A deep dive into simplified Amazon EKS access management controls for more details.

You can disable granting the cluster admin permissions to the cluster creator role on bootstrapping by setting bootstrapClusterCreatorAdminPermissions to false.

Note - Switching bootstrapClusterCreatorAdminPermissions on an existing cluster would cause cluster replacement and should be avoided in production.

Access Entry ​

An access entry is a cluster identity—directly linked to an AWS IAM principal user or role that is used to authenticate to an Amazon EKS cluster. An Amazon EKS access policy authorizes an access entry to perform specific cluster actions.

Access policies are Amazon EKS-specific policies that assign Kubernetes permissions to access entries. Amazon EKS supports only predefined and AWS managed policies. Access policies are not AWS IAM entities and are defined and managed by Amazon EKS. Amazon EKS access policies include permission sets that support common use cases of administration, editing, or read-only access to Kubernetes resources. See Access Policy Permissions for more details.

Use AccessPolicy to include predefined AWS managed policies:

ts
// AmazonEKSClusterAdminPolicy with `cluster` scope
eks.AccessPolicy.fromAccessPolicyName('AmazonEKSClusterAdminPolicy', {
  accessScopeType: eks.AccessScopeType.CLUSTER,
});
// AmazonEKSAdminPolicy with `namespace` scope
eks.AccessPolicy.fromAccessPolicyName('AmazonEKSAdminPolicy', {
  accessScopeType: eks.AccessScopeType.NAMESPACE,
  namespaces: ['foo', 'bar'] } );
python
# AmazonEKSClusterAdminPolicy with `cluster` scope
eks.AccessPolicy.from_access_policy_name("AmazonEKSClusterAdminPolicy",
    access_scope_type=eks.AccessScopeType.CLUSTER
)
# AmazonEKSAdminPolicy with `namespace` scope
eks.AccessPolicy.from_access_policy_name("AmazonEKSAdminPolicy",
    access_scope_type=eks.AccessScopeType.NAMESPACE,
    namespaces=["foo", "bar"]
)
java
// AmazonEKSClusterAdminPolicy with `cluster` scope
AccessPolicy.fromAccessPolicyName("AmazonEKSClusterAdminPolicy", AccessPolicyNameOptions.builder()
        .accessScopeType(AccessScopeType.CLUSTER)
        .build());
// AmazonEKSAdminPolicy with `namespace` scope
AccessPolicy.fromAccessPolicyName("AmazonEKSAdminPolicy", AccessPolicyNameOptions.builder()
        .accessScopeType(AccessScopeType.NAMESPACE)
        .namespaces(List.of("foo", "bar"))
        .build());
csharp
// AmazonEKSClusterAdminPolicy with `cluster` scope
AccessPolicy.FromAccessPolicyName("AmazonEKSClusterAdminPolicy", new AccessPolicyNameOptions {
    AccessScopeType = AccessScopeType.CLUSTER
});
// AmazonEKSAdminPolicy with `namespace` scope
AccessPolicy.FromAccessPolicyName("AmazonEKSAdminPolicy", new AccessPolicyNameOptions {
    AccessScopeType = AccessScopeType.NAMESPACE,
    Namespaces = new [] { "foo", "bar" }
});
go
// AmazonEKSClusterAdminPolicy with `cluster` scope
eks.AccessPolicy_FromAccessPolicyName(jsii.String("AmazonEKSClusterAdminPolicy"), &AccessPolicyNameOptions{
	AccessScopeType: eks.AccessScopeType_CLUSTER,
})
// AmazonEKSAdminPolicy with `namespace` scope
eks.AccessPolicy_FromAccessPolicyName(jsii.String("AmazonEKSAdminPolicy"), &AccessPolicyNameOptions{
	AccessScopeType: eks.AccessScopeType_NAMESPACE,
	Namespaces: []*string{
		jsii.String("foo"),
		jsii.String("bar"),
	},
})

Use grantAccess() to grant the AccessPolicy to an IAM principal:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';
declare const vpc: ec2.Vpc;

const clusterAdminRole = new iam.Role(this, 'ClusterAdminRole', {
  assumedBy: new iam.ArnPrincipal('arn_for_trusted_principal'),
});

const eksAdminRole = new iam.Role(this, 'EKSAdminRole', {
  assumedBy: new iam.ArnPrincipal('arn_for_trusted_principal'),
});

const eksAdminViewRole = new iam.Role(this, 'EKSAdminViewRole', {
  assumedBy: new iam.ArnPrincipal('arn_for_trusted_principal'),
});

const cluster = new eks.Cluster(this, 'Cluster', {
  vpc,
  mastersRole: clusterAdminRole,
  version: eks.KubernetesVersion.V1_36,
  kubectlLayer: new KubectlV36Layer(this, 'KubectlLayer'),
  authenticationMode: eks.AuthenticationMode.API_AND_CONFIG_MAP,
});

// Cluster Admin role for this cluster
cluster.grantAccess('clusterAdminAccess', clusterAdminRole.roleArn, [
  eks.AccessPolicy.fromAccessPolicyName('AmazonEKSClusterAdminPolicy', {
    accessScopeType: eks.AccessScopeType.CLUSTER,
  }),
]);

// EKS Admin role for specified namespaces of this cluster
cluster.grantAccess('eksAdminRoleAccess', eksAdminRole.roleArn, [
  eks.AccessPolicy.fromAccessPolicyName('AmazonEKSAdminPolicy', {
    accessScopeType: eks.AccessScopeType.NAMESPACE,
    namespaces: ['foo', 'bar'],
  }),
]);

// EKS Admin Viewer role for specified namespaces of this cluster
cluster.grantAccess('eksAdminViewRoleAccess', eksAdminViewRole.roleArn, [
  eks.AccessPolicy.fromAccessPolicyName('AmazonEKSAdminViewPolicy', {
    accessScopeType: eks.AccessScopeType.NAMESPACE,
    namespaces: ['foo', 'bar'],
  }),
]);
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer
# vpc: ec2.Vpc


cluster_admin_role = iam.Role(self, "ClusterAdminRole",
    assumed_by=iam.ArnPrincipal("arn_for_trusted_principal")
)

eks_admin_role = iam.Role(self, "EKSAdminRole",
    assumed_by=iam.ArnPrincipal("arn_for_trusted_principal")
)

eks_admin_view_role = iam.Role(self, "EKSAdminViewRole",
    assumed_by=iam.ArnPrincipal("arn_for_trusted_principal")
)

cluster = eks.Cluster(self, "Cluster",
    vpc=vpc,
    masters_role=cluster_admin_role,
    version=eks.KubernetesVersion.V1_36,
    kubectl_layer=KubectlV36Layer(self, "KubectlLayer"),
    authentication_mode=eks.AuthenticationMode.API_AND_CONFIG_MAP
)

# Cluster Admin role for this cluster
cluster.grant_access("clusterAdminAccess", cluster_admin_role.role_arn, [
    eks.AccessPolicy.from_access_policy_name("AmazonEKSClusterAdminPolicy",
        access_scope_type=eks.AccessScopeType.CLUSTER
    )
])

# EKS Admin role for specified namespaces of this cluster
cluster.grant_access("eksAdminRoleAccess", eks_admin_role.role_arn, [
    eks.AccessPolicy.from_access_policy_name("AmazonEKSAdminPolicy",
        access_scope_type=eks.AccessScopeType.NAMESPACE,
        namespaces=["foo", "bar"]
    )
])

# EKS Admin Viewer role for specified namespaces of this cluster
cluster.grant_access("eksAdminViewRoleAccess", eks_admin_view_role.role_arn, [
    eks.AccessPolicy.from_access_policy_name("AmazonEKSAdminViewPolicy",
        access_scope_type=eks.AccessScopeType.NAMESPACE,
        namespaces=["foo", "bar"]
    )
])
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;
Vpc vpc;


Role clusterAdminRole = Role.Builder.create(this, "ClusterAdminRole")
        .assumedBy(new ArnPrincipal("arn_for_trusted_principal"))
        .build();

Role eksAdminRole = Role.Builder.create(this, "EKSAdminRole")
        .assumedBy(new ArnPrincipal("arn_for_trusted_principal"))
        .build();

Role eksAdminViewRole = Role.Builder.create(this, "EKSAdminViewRole")
        .assumedBy(new ArnPrincipal("arn_for_trusted_principal"))
        .build();

Cluster cluster = Cluster.Builder.create(this, "Cluster")
        .vpc(vpc)
        .mastersRole(clusterAdminRole)
        .version(KubernetesVersion.V1_36)
        .kubectlLayer(new KubectlV36Layer(this, "KubectlLayer"))
        .authenticationMode(AuthenticationMode.API_AND_CONFIG_MAP)
        .build();

// Cluster Admin role for this cluster
cluster.grantAccess("clusterAdminAccess", clusterAdminRole.getRoleArn(), List.of(AccessPolicy.fromAccessPolicyName("AmazonEKSClusterAdminPolicy", AccessPolicyNameOptions.builder()
        .accessScopeType(AccessScopeType.CLUSTER)
        .build())));

// EKS Admin role for specified namespaces of this cluster
cluster.grantAccess("eksAdminRoleAccess", eksAdminRole.getRoleArn(), List.of(AccessPolicy.fromAccessPolicyName("AmazonEKSAdminPolicy", AccessPolicyNameOptions.builder()
        .accessScopeType(AccessScopeType.NAMESPACE)
        .namespaces(List.of("foo", "bar"))
        .build())));

// EKS Admin Viewer role for specified namespaces of this cluster
cluster.grantAccess("eksAdminViewRoleAccess", eksAdminViewRole.getRoleArn(), List.of(AccessPolicy.fromAccessPolicyName("AmazonEKSAdminViewPolicy", AccessPolicyNameOptions.builder()
        .accessScopeType(AccessScopeType.NAMESPACE)
        .namespaces(List.of("foo", "bar"))
        .build())));
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;
Vpc vpc;


var clusterAdminRole = new Role(this, "ClusterAdminRole", new RoleProps {
    AssumedBy = new ArnPrincipal("arn_for_trusted_principal")
});

var eksAdminRole = new Role(this, "EKSAdminRole", new RoleProps {
    AssumedBy = new ArnPrincipal("arn_for_trusted_principal")
});

var eksAdminViewRole = new Role(this, "EKSAdminViewRole", new RoleProps {
    AssumedBy = new ArnPrincipal("arn_for_trusted_principal")
});

var cluster = new Cluster(this, "Cluster", new ClusterProps {
    Vpc = vpc,
    MastersRole = clusterAdminRole,
    Version = KubernetesVersion.V1_36,
    KubectlLayer = new KubectlV36Layer(this, "KubectlLayer"),
    AuthenticationMode = AuthenticationMode.API_AND_CONFIG_MAP
});

// Cluster Admin role for this cluster
cluster.GrantAccess("clusterAdminAccess", clusterAdminRole.RoleArn, new [] { AccessPolicy.FromAccessPolicyName("AmazonEKSClusterAdminPolicy", new AccessPolicyNameOptions {
    AccessScopeType = AccessScopeType.CLUSTER
}) });

// EKS Admin role for specified namespaces of this cluster
cluster.GrantAccess("eksAdminRoleAccess", eksAdminRole.RoleArn, new [] { AccessPolicy.FromAccessPolicyName("AmazonEKSAdminPolicy", new AccessPolicyNameOptions {
    AccessScopeType = AccessScopeType.NAMESPACE,
    Namespaces = new [] { "foo", "bar" }
}) });

// EKS Admin Viewer role for specified namespaces of this cluster
cluster.GrantAccess("eksAdminViewRoleAccess", eksAdminViewRole.RoleArn, new [] { AccessPolicy.FromAccessPolicyName("AmazonEKSAdminViewPolicy", new AccessPolicyNameOptions {
    AccessScopeType = AccessScopeType.NAMESPACE,
    Namespaces = new [] { "foo", "bar" }
}) });
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"
var vpc Vpc


clusterAdminRole := iam.NewRole(this, jsii.String("ClusterAdminRole"), &RoleProps{
	AssumedBy: iam.NewArnPrincipal(jsii.String("arn_for_trusted_principal")),
})

eksAdminRole := iam.NewRole(this, jsii.String("EKSAdminRole"), &RoleProps{
	AssumedBy: iam.NewArnPrincipal(jsii.String("arn_for_trusted_principal")),
})

eksAdminViewRole := iam.NewRole(this, jsii.String("EKSAdminViewRole"), &RoleProps{
	AssumedBy: iam.NewArnPrincipal(jsii.String("arn_for_trusted_principal")),
})

cluster := eks.NewCluster(this, jsii.String("Cluster"), &ClusterProps{
	Vpc: Vpc,
	MastersRole: clusterAdminRole,
	Version: eks.KubernetesVersion_V1_36(),
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("KubectlLayer")),
	AuthenticationMode: eks.AuthenticationMode_API_AND_CONFIG_MAP,
})

// Cluster Admin role for this cluster
cluster.GrantAccess(jsii.String("clusterAdminAccess"), clusterAdminRole.roleArn, []IAccessPolicy{
	eks.AccessPolicy_FromAccessPolicyName(jsii.String("AmazonEKSClusterAdminPolicy"), &AccessPolicyNameOptions{
		AccessScopeType: eks.AccessScopeType_CLUSTER,
	}),
})

// EKS Admin role for specified namespaces of this cluster
cluster.GrantAccess(jsii.String("eksAdminRoleAccess"), eksAdminRole.roleArn, []IAccessPolicy{
	eks.AccessPolicy_FromAccessPolicyName(jsii.String("AmazonEKSAdminPolicy"), &AccessPolicyNameOptions{
		AccessScopeType: eks.AccessScopeType_NAMESPACE,
		Namespaces: []*string{
			jsii.String("foo"),
			jsii.String("bar"),
		},
	}),
})

// EKS Admin Viewer role for specified namespaces of this cluster
cluster.GrantAccess(jsii.String("eksAdminViewRoleAccess"), eksAdminViewRole.roleArn, []IAccessPolicy{
	eks.AccessPolicy_FromAccessPolicyName(jsii.String("AmazonEKSAdminViewPolicy"), &AccessPolicyNameOptions{
		AccessScopeType: eks.AccessScopeType_NAMESPACE,
		Namespaces: []*string{
			jsii.String("foo"),
			jsii.String("bar"),
		},
	}),
})

You can optionally specify an access entry type when granting access:

ts
declare const cluster: eks.Cluster;
declare const nodeRole: iam.Role;

// For EKS Auto Mode node roles
cluster.grantAccess('NodeAccess', nodeRole.roleArn, [], { accessEntryType: eks.AccessEntryType.EC2 });
python
# cluster: eks.Cluster
# node_role: iam.Role


# For EKS Auto Mode node roles
cluster.grant_access("NodeAccess", node_role.role_arn, [], access_entry_type=eks.AccessEntryType.EC2)
java
Cluster cluster;
Role nodeRole;


// For EKS Auto Mode node roles
cluster.grantAccess("NodeAccess", nodeRole.getRoleArn(), List.of(), GrantAccessOptions.builder().accessEntryType(AccessEntryType.EC2).build());
csharp
Cluster cluster;
Role nodeRole;


// For EKS Auto Mode node roles
cluster.GrantAccess("NodeAccess", nodeRole.RoleArn, new [] {  }, new GrantAccessOptions { AccessEntryType = AccessEntryType.EC2 });
go
var cluster Cluster
var nodeRole Role


// For EKS Auto Mode node roles
cluster.GrantAccess(jsii.String("NodeAccess"), nodeRole.roleArn, []IAccessPolicy{
}, &GrantAccessOptions{
	AccessEntryType: eks.AccessEntryType_EC2,
})

Supported types: STANDARD (default), FARGATE_LINUX, EC2_LINUX, EC2_WINDOWS, EC2, HYBRID_LINUX, HYPERPOD_LINUX.

Note: HYBRID_LINUX and HYPERPOD_LINUX types cannot have access policies attached.

Migrating from ConfigMap to Access Entry ​

If the cluster is created with the authenticationMode property left undefined, it will default to CONFIG_MAP.

The update path is:

undefined(CONFIG_MAP) -> API_AND_CONFIG_MAP -> API

If you have explicitly declared AwsAuth resources and then try to switch to the API mode, which no longer supports the ConfigMap, AWS CDK will throw an error as a protective measure to prevent you from losing all the access entries in the ConfigMap. In this case, you will need to remove all the declared AwsAuth resources explicitly and define the access entries before you are allowed to transition to the API mode.

Note - This is a one-way transition. Once you switch to the API mode, you will not be able to switch back. Therefore, it is crucial to ensure that you have defined all the necessary access entries before making the switch to the API mode.

Cluster Security Group ​

When you create an Amazon EKS cluster, a cluster security group is automatically created as well. This security group is designed to allow all traffic from the control plane and managed node groups to flow freely between each other.

The ID for that security group can be retrieved after creating the cluster.

ts
declare const cluster: eks.Cluster;
const clusterSecurityGroupId = cluster.clusterSecurityGroupId;
python
# cluster: eks.Cluster

cluster_security_group_id = cluster.cluster_security_group_id
java
Cluster cluster;

String clusterSecurityGroupId = cluster.getClusterSecurityGroupId();
csharp
Cluster cluster;

var clusterSecurityGroupId = cluster.ClusterSecurityGroupId;
go
var cluster Cluster

clusterSecurityGroupId := cluster.ClusterSecurityGroupId

Node SSH Access ​

If you want to be able to SSH into your worker nodes, you must already have an SSH key in the region you're connecting to and pass it when you add capacity to the cluster. You must also be able to connect to the hosts (meaning they must have a public IP and you should be allowed to connect to them on port 22):

See SSH into nodes for a code example.

If you want to SSH into nodes in a private subnet, you should set up a bastion host in a public subnet. That setup is recommended, but is unfortunately beyond the scope of this documentation.

Service Accounts ​

With services account you can provide Kubernetes Pods access to AWS resources.

ts
declare const cluster: eks.Cluster;
// add service account
const serviceAccount = cluster.addServiceAccount('MyServiceAccount');

const bucket = new s3.Bucket(this, 'Bucket');
bucket.grantReadWrite(serviceAccount);

const mypod = cluster.addManifest('mypod', {
  apiVersion: 'v1',
  kind: 'Pod',
  metadata: { name: 'mypod' },
  spec: {
    serviceAccountName: serviceAccount.serviceAccountName,
    containers: [
      {
        name: 'hello',
        image: 'paulbouwer/hello-kubernetes:1.5',
        ports: [ { containerPort: 8080 } ],
      },
    ],
  },
});

// create the resource after the service account.
mypod.node.addDependency(serviceAccount);

// print the IAM role arn for this service account
new CfnOutput(this, 'ServiceAccountIamRole', { value: serviceAccount.role.roleArn });
python
# cluster: eks.Cluster

# add service account
service_account = cluster.add_service_account("MyServiceAccount")

bucket = s3.Bucket(self, "Bucket")
bucket.grant_read_write(service_account)

mypod = cluster.add_manifest("mypod", {
    "api_version": "v1",
    "kind": "Pod",
    "metadata": {"name": "mypod"},
    "spec": {
        "service_account_name": service_account.service_account_name,
        "containers": [{
            "name": "hello",
            "image": "paulbouwer/hello-kubernetes:1.5",
            "ports": [{"container_port": 8080}]
        }
        ]
    }
})

# create the resource after the service account.
mypod.node.add_dependency(service_account)

# print the IAM role arn for this service account
CfnOutput(self, "ServiceAccountIamRole", value=service_account.role.role_arn)
java
Cluster cluster;

// add service account
ServiceAccount serviceAccount = cluster.addServiceAccount("MyServiceAccount");

Bucket bucket = new Bucket(this, "Bucket");
bucket.grantReadWrite(serviceAccount);

KubernetesManifest mypod = cluster.addManifest("mypod", Map.of(
        "apiVersion", "v1",
        "kind", "Pod",
        "metadata", Map.of("name", "mypod"),
        "spec", Map.of(
                "serviceAccountName", serviceAccount.getServiceAccountName(),
                "containers", List.of(Map.of(
                        "name", "hello",
                        "image", "paulbouwer/hello-kubernetes:1.5",
                        "ports", List.of(Map.of("containerPort", 8080)))))));

// create the resource after the service account.
mypod.node.addDependency(serviceAccount);

// print the IAM role arn for this service account
// print the IAM role arn for this service account
CfnOutput.Builder.create(this, "ServiceAccountIamRole").value(serviceAccount.getRole().getRoleArn()).build();
csharp
Cluster cluster;

// add service account
var serviceAccount = cluster.AddServiceAccount("MyServiceAccount");

var bucket = new Bucket(this, "Bucket");
bucket.GrantReadWrite(serviceAccount);

var mypod = cluster.AddManifest("mypod", new Dictionary<string, object> {
    { "apiVersion", "v1" },
    { "kind", "Pod" },
    { "metadata", new Dictionary<string, string> { { "name", "mypod" } } },
    { "spec", new Dictionary<string, object> {
        { "serviceAccountName", serviceAccount.ServiceAccountName },
        { "containers", new [] { new Struct {
            Name = "hello",
            Image = "paulbouwer/hello-kubernetes:1.5",
            Ports = new [] { new Struct { ContainerPort = 8080 } }
        } } }
    } }
});

// create the resource after the service account.
mypod.Node.AddDependency(serviceAccount);

// print the IAM role arn for this service account
// print the IAM role arn for this service account
new CfnOutput(this, "ServiceAccountIamRole", new CfnOutputProps { Value = serviceAccount.Role.RoleArn });
go
var cluster Cluster

// add service account
serviceAccount := cluster.addServiceAccount(jsii.String("MyServiceAccount"))

bucket := s3.NewBucket(this, jsii.String("Bucket"))
bucket.GrantReadWrite(serviceAccount)

mypod := cluster.addManifest(jsii.String("mypod"), map[string]interface{}{
	"apiVersion": jsii.String("v1"),
	"kind": jsii.String("Pod"),
	"metadata": map[string]*string{
		"name": jsii.String("mypod"),
	},
	"spec": map[string]interface{}{
		"serviceAccountName": serviceAccount.serviceAccountName,
		"containers": []map[string]interface{}{
			map[string]interface{}{
				"name": jsii.String("hello"),
				"image": jsii.String("paulbouwer/hello-kubernetes:1.5"),
				"ports": []map[string]*f64{
					map[string]*f64{
						"containerPort": jsii.Number(8080),
					},
				},
			},
		},
	},
})

// create the resource after the service account.
mypod.Node.AddDependency(serviceAccount)

// print the IAM role arn for this service account
// print the IAM role arn for this service account
awscdk.NewCfnOutput(this, jsii.String("ServiceAccountIamRole"), &CfnOutputProps{
	Value: serviceAccount.Role.RoleArn,
})

Note that using serviceAccount.serviceAccountName above does not translate into a resource dependency. This is why an explicit dependency is needed. See <https://github.com/aws/aws-cdk/issues/9910> for more details.

It is possible to pass annotations and labels to the service account.

ts
declare const cluster: eks.Cluster;
// add service account with annotations and labels
const serviceAccount = cluster.addServiceAccount('MyServiceAccount', {
  annotations: {
    'eks.amazonaws.com/sts-regional-endpoints': 'false',
  },
  labels: {
    'some-label': 'with-some-value',
  },
});
python
# cluster: eks.Cluster

# add service account with annotations and labels
service_account = cluster.add_service_account("MyServiceAccount",
    annotations={
        "eks.amazonaws.com/sts-regional-endpoints": "false"
    },
    labels={
        "some-label": "with-some-value"
    }
)
java
Cluster cluster;

// add service account with annotations and labels
ServiceAccount serviceAccount = cluster.addServiceAccount("MyServiceAccount", ServiceAccountOptions.builder()
        .annotations(Map.of(
                "eks.amazonaws.com/sts-regional-endpoints", "false"))
        .labels(Map.of(
                "some-label", "with-some-value"))
        .build());
csharp
Cluster cluster;

// add service account with annotations and labels
var serviceAccount = cluster.AddServiceAccount("MyServiceAccount", new ServiceAccountOptions {
    Annotations = new Dictionary<string, string> {
        { "eks.amazonaws.com/sts-regional-endpoints", "false" }
    },
    Labels = new Dictionary<string, string> {
        { "some-label", "with-some-value" }
    }
});
go
var cluster Cluster

// add service account with annotations and labels
serviceAccount := cluster.addServiceAccount(jsii.String("MyServiceAccount"), &ServiceAccountOptions{
	Annotations: map[string]*string{
		"eks.amazonaws.com/sts-regional-endpoints": jsii.String("false"),
	},
	Labels: map[string]*string{
		"some-label": jsii.String("with-some-value"),
	},
})

To overwrite an existing service account, use the overwriteServiceAccount property:

ts
declare const cluster: eks.Cluster;
// overwrite existing service account
const serviceAccount = cluster.addServiceAccount('MyServiceAccount', {
  overwriteServiceAccount: true,
});
python
# cluster: eks.Cluster

# overwrite existing service account
service_account = cluster.add_service_account("MyServiceAccount",
    overwrite_service_account=True
)
java
Cluster cluster;

// overwrite existing service account
ServiceAccount serviceAccount = cluster.addServiceAccount("MyServiceAccount", ServiceAccountOptions.builder()
        .overwriteServiceAccount(true)
        .build());
csharp
Cluster cluster;

// overwrite existing service account
var serviceAccount = cluster.AddServiceAccount("MyServiceAccount", new ServiceAccountOptions {
    OverwriteServiceAccount = true
});
go
var cluster Cluster

// overwrite existing service account
serviceAccount := cluster.addServiceAccount(jsii.String("MyServiceAccount"), &ServiceAccountOptions{
	OverwriteServiceAccount: jsii.Boolean(true),
})

You can also add service accounts to existing clusters. To do so, pass the openIdConnectProvider property when you import the cluster into the application.

ts
// you can import an existing provider
const provider = eks.OidcProviderNative.fromOidcProviderArn(this, 'Provider', 'arn:aws:iam::123456:oidc-provider/oidc.eks.eu-west-1.amazonaws.com/id/AB123456ABC');

// or create a new one using an existing issuer url
declare const issuerUrl: string;
const provider2 = new eks.OidcProviderNative(this, 'Provider', {
  url: issuerUrl,
});

const cluster = eks.Cluster.fromClusterAttributes(this, 'MyCluster', {
  clusterName: 'Cluster',
  openIdConnectProvider: provider,
  kubectlRoleArn: 'arn:aws:iam::123456:role/service-role/k8sservicerole',
});

const serviceAccount = cluster.addServiceAccount('MyServiceAccount');

const bucket = new s3.Bucket(this, 'Bucket');
bucket.grantReadWrite(serviceAccount);
python
# or create a new one using an existing issuer url
# issuer_url: str
# you can import an existing provider
provider = eks.OidcProviderNative.from_oidc_provider_arn(self, "Provider", "arn:aws:iam::123456:oidc-provider/oidc.eks.eu-west-1.amazonaws.com/id/AB123456ABC")
provider2 = eks.OidcProviderNative(self, "Provider",
    url=issuer_url
)

cluster = eks.Cluster.from_cluster_attributes(self, "MyCluster",
    cluster_name="Cluster",
    open_id_connect_provider=provider,
    kubectl_role_arn="arn:aws:iam::123456:role/service-role/k8sservicerole"
)

service_account = cluster.add_service_account("MyServiceAccount")

bucket = s3.Bucket(self, "Bucket")
bucket.grant_read_write(service_account)
java
// or create a new one using an existing issuer url
String issuerUrl;
// you can import an existing provider
IOidcProvider provider = OidcProviderNative.fromOidcProviderArn(this, "Provider", "arn:aws:iam::123456:oidc-provider/oidc.eks.eu-west-1.amazonaws.com/id/AB123456ABC");
OidcProviderNative provider2 = OidcProviderNative.Builder.create(this, "Provider")
        .url(issuerUrl)
        .build();

ICluster cluster = Cluster.fromClusterAttributes(this, "MyCluster", ClusterAttributes.builder()
        .clusterName("Cluster")
        .openIdConnectProvider(provider)
        .kubectlRoleArn("arn:aws:iam::123456:role/service-role/k8sservicerole")
        .build());

ServiceAccount serviceAccount = cluster.addServiceAccount("MyServiceAccount");

Bucket bucket = new Bucket(this, "Bucket");
bucket.grantReadWrite(serviceAccount);
csharp
// or create a new one using an existing issuer url
string issuerUrl;
// you can import an existing provider
var provider = OidcProviderNative.FromOidcProviderArn(this, "Provider", "arn:aws:iam::123456:oidc-provider/oidc.eks.eu-west-1.amazonaws.com/id/AB123456ABC");
var provider2 = new OidcProviderNative(this, "Provider", new OidcProviderNativeProps {
    Url = issuerUrl
});

var cluster = Cluster.FromClusterAttributes(this, "MyCluster", new ClusterAttributes {
    ClusterName = "Cluster",
    OpenIdConnectProvider = provider,
    KubectlRoleArn = "arn:aws:iam::123456:role/service-role/k8sservicerole"
});

var serviceAccount = cluster.AddServiceAccount("MyServiceAccount");

var bucket = new Bucket(this, "Bucket");
bucket.GrantReadWrite(serviceAccount);
go
// or create a new one using an existing issuer url
var issuerUrl string
// you can import an existing provider
provider := eks.OidcProviderNative_FromOidcProviderArn(this, jsii.String("Provider"), jsii.String("arn:aws:iam::123456:oidc-provider/oidc.eks.eu-west-1.amazonaws.com/id/AB123456ABC"))
provider2 := eks.NewOidcProviderNative(this, jsii.String("Provider"), &OidcProviderNativeProps{
	Url: issuerUrl,
})

cluster := eks.Cluster_FromClusterAttributes(this, jsii.String("MyCluster"), &ClusterAttributes{
	ClusterName: jsii.String("Cluster"),
	OpenIdConnectProvider: provider,
	KubectlRoleArn: jsii.String("arn:aws:iam::123456:role/service-role/k8sservicerole"),
})

serviceAccount := cluster.AddServiceAccount(jsii.String("MyServiceAccount"))

bucket := s3.NewBucket(this, jsii.String("Bucket"))
bucket.GrantReadWrite(serviceAccount)

Note that adding service accounts requires running kubectl commands against the cluster. This means you must also pass the kubectlRoleArn when importing the cluster. See Using existing Clusters.

Migrating from eks.OpenIdConnectProvider to eks.OidcProviderNative ​

eks.OpenIdConnectProvider creates an IAM OIDC (OpenId Connect) provider using a custom resource while eks.OidcProviderNative uses the CFN L1 (AWS::IAM::OidcProvider) to create the provider. It is recommended for new and existing projects to use eks.OidcProviderNative. Migrating from the eks.OpenIdConnectProvider is not as trivial as switching out the property since the property controls the creation of a resource whose type is changing. Due to the potential complexlity of the migration and the requirement of a manual step (cdk import) we are not deprecating the eks.OpenIdConnectProvider construct but encourge you to migrate.

To migrate without temporarily removing the OIDCProvider, follow these steps:

  1. Set the removalPolicy of cluster.openIdConnectProvider to RETAIN.

    ts
    import * as cdk from 'aws-cdk-lib';
    declare const cluster: eks.Cluster;
    
    cdk.RemovalPolicies.of(cluster.openIdConnectProvider).apply(cdk.RemovalPolicy.RETAIN);
    
  2. Run cdk diff to verify the changes are expected then cdk deploy.

  3. Add the following to the context field of your cdk.json to enable the feature flag that creates the native oidc provider.

    json
    "@aws-cdk/aws-eks:useNativeOidcProvider": true,
    
  4. Run cdk diff and ensure the changes are expected. Example of an expected diff:

    bash
    Resources
    [-] Custom::AWSCDKOpenIdConnectProvider TestCluster/OpenIdConnectProvider/Resource TestClusterOpenIdConnectProviderE18F0FD0 orphan
    [-] AWS::IAM::Role Custom::AWSCDKOpenIdConnectProviderCustomResourceProvider/Role CustomAWSCDKOpenIdConnectProviderCustomResourceProviderRole517FED65 destroy
    [-] AWS::Lambda::Function Custom::AWSCDKOpenIdConnectProviderCustomResourceProvider/Handler CustomAWSCDKOpenIdConnectProviderCustomResourceProviderHandlerF2C543E0 destroy
    [+] AWS::IAM::OIDCProvider TestCluster/OidcProviderNative TestClusterOidcProviderNative0BE3F155
    
  5. Run cdk import --force and provide the ARN of the existing OpenIdConnectProvider when prompted. You will get a warning about pending changes to existing resources which is expected.

  6. Run cdk deploy to apply any pending changes. This will apply the destroy/orphan changes in the above example.

If you are creating the OpenIdConnectProvider manually via new eks.OpenIdConnectProvider, follow these steps:

  1. Set the removalPolicy of the existing OpenIdConnectProvider to RemovalPolicy.RETAIN.

    ts
    import * as cdk from 'aws-cdk-lib';
    // Step 1: Add retain policy to existing provider
    const existingProvider = new eks.OpenIdConnectProvider(this, 'Provider', {
      url: 'https://oidc.eks.us-west-2.amazonaws.com/id/EXAMPLE',
      removalPolicy: cdk.RemovalPolicy.RETAIN, // Add this line
    });
    
  2. Deploy with the retain policy to avoid deletion of the underlying resource.

    bash
    cdk deploy
    
  3. Replace OpenIdConnectProvider with OidcProviderNative in your code.

    ts
    // Step 3: Replace with native provider
    const nativeProvider = new eks.OidcProviderNative(this, 'Provider', {
      url: 'https://oidc.eks.us-west-2.amazonaws.com/id/EXAMPLE',
    });
    
  4. Run cdk diff and verify the changes are expected. Example of an expected diff:

    bash
    Resources
    [-] Custom::AWSCDKOpenIdConnectProvider TestCluster/OpenIdConnectProvider/Resource TestClusterOpenIdConnectProviderE18F0FD0 orphan
    [-] AWS::IAM::Role Custom::AWSCDKOpenIdConnectProviderCustomResourceProvider/Role CustomAWSCDKOpenIdConnectProviderCustomResourceProviderRole517FED65 destroy
    [-] AWS::Lambda::Function Custom::AWSCDKOpenIdConnectProviderCustomResourceProvider/Handler CustomAWSCDKOpenIdConnectProviderCustomResourceProviderHandlerF2C543E0 destroy
    [+] AWS::IAM::OIDCProvider TestCluster/OidcProviderNative TestClusterOidcProviderNative0BE3F155
    
  5. Run cdk import --force to import the existing OIDC provider resource by providing the existing ARN.

  6. Run cdk deploy to apply any pending changes. This will apply the destroy/orphan operations in the example diff above.

Pod Identities ​

Amazon EKS Pod Identities is a feature that simplifies how Kubernetes applications running on Amazon EKS can obtain AWS IAM credentials. It provides a way to associate an IAM role with a Kubernetes service account, allowing pods to retrieve temporary AWS credentials without the need to manage IAM roles and policies directly.

By default, ServiceAccount creates an OpenIdConnectProvider for IRSA(IAM roles for service accounts) if identityType is undefined or IdentityType.IRSA.

You may opt in Amaozn EKS Pod Identities as below:

ts
declare const cluster: eks.Cluster;

new eks.ServiceAccount(this, 'ServiceAccount', {
  cluster,
  name: 'test-sa',
  namespace: 'default',
  identityType: eks.IdentityType.POD_IDENTITY,
});
python
# cluster: eks.Cluster


eks.ServiceAccount(self, "ServiceAccount",
    cluster=cluster,
    name="test-sa",
    namespace="default",
    identity_type=eks.IdentityType.POD_IDENTITY
)
java
Cluster cluster;


ServiceAccount.Builder.create(this, "ServiceAccount")
        .cluster(cluster)
        .name("test-sa")
        .namespace("default")
        .identityType(IdentityType.POD_IDENTITY)
        .build();
csharp
Cluster cluster;


new ServiceAccount(this, "ServiceAccount", new ServiceAccountProps {
    Cluster = cluster,
    Name = "test-sa",
    Namespace = "default",
    IdentityType = IdentityType.POD_IDENTITY
});
go
var cluster Cluster


eks.NewServiceAccount(this, jsii.String("ServiceAccount"), &ServiceAccountProps{
	Cluster: Cluster,
	Name: jsii.String("test-sa"),
	Namespace: jsii.String("default"),
	IdentityType: eks.IdentityType_POD_IDENTITY,
})

When you create the ServiceAccount with the identityType set to POD_IDENTITY, ServiceAccount contruct will perform the following actions behind the scenes:

  1. It will create an IAM role with the necessary trust policy to allow the "pods.eks.amazonaws.com" principal to assume the role. This trust policy grants the EKS service the permission to retrieve temporary AWS credentials on behalf of the pods using this service account.

  2. It will enable the "Amazon EKS Pod Identity Agent" add-on on the EKS cluster. This add-on is responsible for managing the temporary AWS credentials and making them available to the pods.

  3. It will create an association between the IAM role and the Kubernetes service account. This association allows the pods using this service account to obtain the temporary AWS credentials from the associated IAM role.

This simplifies the process of configuring IAM permissions for your Kubernetes applications running on Amazon EKS. It handles the creation of the IAM role, the installation of the Pod Identity Agent add-on, and the association between the role and the service account, making it easier to manage AWS credentials for your applications.

Applying Kubernetes Resources ​

The library supports several popular resource deployment mechanisms, among which are:

Kubernetes Manifests ​

The KubernetesManifest construct or cluster.addManifest method can be used to apply Kubernetes resource manifests to this cluster.

When using cluster.addManifest, the manifest construct is defined within the cluster's stack scope. If the manifest contains attributes from a different stack which depend on the cluster stack, a circular dependency will be created and you will get a synth time error. To avoid this, directly use new KubernetesManifest to create the manifest in the scope of the other stack.

The following examples will deploy the paulbouwer/hello-kubernetes service on the cluster:

ts
declare const cluster: eks.Cluster;
const appLabel = { app: "hello-kubernetes" };

const deployment = {
  apiVersion: "apps/v1",
  kind: "Deployment",
  metadata: { name: "hello-kubernetes" },
  spec: {
    replicas: 3,
    selector: { matchLabels: appLabel },
    template: {
      metadata: { labels: appLabel },
      spec: {
        containers: [
          {
            name: "hello-kubernetes",
            image: "paulbouwer/hello-kubernetes:1.5",
            ports: [ { containerPort: 8080 } ],
          },
        ],
      },
    },
  },
};

const service = {
  apiVersion: "v1",
  kind: "Service",
  metadata: { name: "hello-kubernetes" },
  spec: {
    type: "LoadBalancer",
    ports: [ { port: 80, targetPort: 8080 } ],
    selector: appLabel,
  }
};

// option 1: use a construct
new eks.KubernetesManifest(this, 'hello-kub', {
  cluster,
  manifest: [ deployment, service ],
});

// or, option2: use `addManifest`
cluster.addManifest('hello-kub', service, deployment);
python
# cluster: eks.Cluster

app_label = {"app": "hello-kubernetes"}

deployment = {
    "api_version": "apps/v1",
    "kind": "Deployment",
    "metadata": {"name": "hello-kubernetes"},
    "spec": {
        "replicas": 3,
        "selector": {"match_labels": app_label},
        "template": {
            "metadata": {"labels": app_label},
            "spec": {
                "containers": [{
                    "name": "hello-kubernetes",
                    "image": "paulbouwer/hello-kubernetes:1.5",
                    "ports": [{"container_port": 8080}]
                }
                ]
            }
        }
    }
}

service = {
    "api_version": "v1",
    "kind": "Service",
    "metadata": {"name": "hello-kubernetes"},
    "spec": {
        "type": "LoadBalancer",
        "ports": [{"port": 80, "target_port": 8080}],
        "selector": app_label
    }
}

# option 1: use a construct
eks.KubernetesManifest(self, "hello-kub",
    cluster=cluster,
    manifest=[deployment, service]
)

# or, option2: use `addManifest`
cluster.add_manifest("hello-kub", service, deployment)
java
Cluster cluster;

Map<String, String> appLabel = Map.of("app", "hello-kubernetes");

Map<String, Object> deployment = Map.of(
        "apiVersion", "apps/v1",
        "kind", "Deployment",
        "metadata", Map.of("name", "hello-kubernetes"),
        "spec", Map.of(
                "replicas", 3,
                "selector", Map.of("matchLabels", appLabel),
                "template", Map.of(
                        "metadata", Map.of("labels", appLabel),
                        "spec", Map.of(
                                "containers", List.of(Map.of(
                                        "name", "hello-kubernetes",
                                        "image", "paulbouwer/hello-kubernetes:1.5",
                                        "ports", List.of(Map.of("containerPort", 8080))))))));

Map<String, Object> service = Map.of(
        "apiVersion", "v1",
        "kind", "Service",
        "metadata", Map.of("name", "hello-kubernetes"),
        "spec", Map.of(
                "type", "LoadBalancer",
                "ports", List.of(Map.of("port", 80, "targetPort", 8080)),
                "selector", appLabel));

// option 1: use a construct
// option 1: use a construct
KubernetesManifest.Builder.create(this, "hello-kub")
        .cluster(cluster)
        .manifest(List.of(deployment, service))
        .build();

// or, option2: use `addManifest`
cluster.addManifest("hello-kub", service, deployment);
csharp
Cluster cluster;

IDictionary<string, string> appLabel = new Dictionary<string, string> { { "app", "hello-kubernetes" } };

IDictionary<string, object> deployment = new Dictionary<string, object> {
    { "apiVersion", "apps/v1" },
    { "kind", "Deployment" },
    { "metadata", new Dictionary<string, string> { { "name", "hello-kubernetes" } } },
    { "spec", new Dictionary<string, object> {
        { "replicas", 3 },
        { "selector", new Dictionary<string, IDictionary<string, string>> { { "matchLabels", appLabel } } },
        { "template", new Dictionary<string, IDictionary<string, IDictionary<string, string>>> {
            { "metadata", new Dictionary<string, IDictionary<string, string>> { { "labels", appLabel } } },
            { "spec", new Dictionary<string, IDictionary<string, object>[]> {
                { "containers", new [] { new Dictionary<string, object> {
                    { "name", "hello-kubernetes" },
                    { "image", "paulbouwer/hello-kubernetes:1.5" },
                    { "ports", new [] { new Dictionary<string, int> { { "containerPort", 8080 } } } }
                } } }
            } }
        } }
    } }
};

IDictionary<string, object> service = new Dictionary<string, object> {
    { "apiVersion", "v1" },
    { "kind", "Service" },
    { "metadata", new Dictionary<string, string> { { "name", "hello-kubernetes" } } },
    { "spec", new Dictionary<string, object> {
        { "type", "LoadBalancer" },
        { "ports", new [] { new Dictionary<string, int> { { "port", 80 }, { "targetPort", 8080 } } } },
        { "selector", appLabel }
    } }
};

// option 1: use a construct
// option 1: use a construct
new KubernetesManifest(this, "hello-kub", new KubernetesManifestProps {
    Cluster = cluster,
    Manifest = new [] { deployment, service }
});

// or, option2: use `addManifest`
cluster.AddManifest("hello-kub", service, deployment);
go
var cluster Cluster

appLabel := map[string]*string{
	"app": jsii.String("hello-kubernetes"),
}

deployment := map[string]interface{}{
	"apiVersion": jsii.String("apps/v1"),
	"kind": jsii.String("Deployment"),
	"metadata": map[string]*string{
		"name": jsii.String("hello-kubernetes"),
	},
	"spec": map[string]interface{}{
		"replicas": jsii.Number(3),
		"selector": map[string]map[string]*string{
			"matchLabels": appLabel,
		},
		"template": map[string]map[string]map[string]*string{
			"metadata": map[string]map[string]*string{
				"labels": appLabel,
			},
			"spec": map[string][]map[string]interface{}{
				"containers": []map[string]interface{}{
					map[string]interface{}{
						"name": jsii.String("hello-kubernetes"),
						"image": jsii.String("paulbouwer/hello-kubernetes:1.5"),
						"ports": []map[string]*f64{
							map[string]*f64{
								"containerPort": jsii.Number(8080),
							},
						},
					},
				},
			},
		},
	},
}

service := map[string]interface{}{
	"apiVersion": jsii.String("v1"),
	"kind": jsii.String("Service"),
	"metadata": map[string]*string{
		"name": jsii.String("hello-kubernetes"),
	},
	"spec": map[string]interface{}{
		"type": jsii.String("LoadBalancer"),
		"ports": []map[string]*f64{
			map[string]*f64{
				"port": jsii.Number(80),
				"targetPort": jsii.Number(8080),
			},
		},
		"selector": appLabel,
	},
}

// option 1: use a construct
// option 1: use a construct
eks.NewKubernetesManifest(this, jsii.String("hello-kub"), &KubernetesManifestProps{
	Cluster: Cluster,
	Manifest: []map[string]interface{}{
		deployment,
		service,
	},
})

// or, option2: use `addManifest`
cluster.addManifest(jsii.String("hello-kub"), service, deployment)

ALB Controller Integration ​

The KubernetesManifest construct can detect ingress resources inside your manifest and automatically add the necessary annotations so they are picked up by the ALB Controller.

See Alb Controller

To that end, it offers the following properties:

  • ingressAlb - Signal that the ingress detection should be done.
  • ingressAlbScheme - Which ALB scheme should be applied. Defaults to internal.

Adding resources from a URL ​

The following example will deploy the resource manifest hosting on remote server:

text
// This example is only available in TypeScript

import * as yaml from 'js-yaml';
import * as request from 'sync-request';

declare const cluster: eks.Cluster;
const manifestUrl = 'https://url/of/manifest.yaml';
const manifest = yaml.safeLoadAll(request('GET', manifestUrl).getBody());
cluster.addManifest('my-resource', manifest);

Dependencies ​

There are cases where Kubernetes resources must be deployed in a specific order. For example, you cannot define a resource in a Kubernetes namespace before the namespace was created.

You can represent dependencies between KubernetesManifests using resource.node.addDependency():

ts
declare const cluster: eks.Cluster;
const namespace = cluster.addManifest('my-namespace', {
  apiVersion: 'v1',
  kind: 'Namespace',
  metadata: { name: 'my-app' },
});

const service = cluster.addManifest('my-service', {
  metadata: {
    name: 'myservice',
    namespace: 'my-app',
  },
  spec: { }, // ...
});

service.node.addDependency(namespace); // will apply `my-namespace` before `my-service`.
python
# cluster: eks.Cluster

namespace = cluster.add_manifest("my-namespace", {
    "api_version": "v1",
    "kind": "Namespace",
    "metadata": {"name": "my-app"}
})

service = cluster.add_manifest("my-service", {
    "metadata": {
        "name": "myservice",
        "namespace": "my-app"
    },
    "spec": {}
})

service.node.add_dependency(namespace)
java
Cluster cluster;

KubernetesManifest namespace = cluster.addManifest("my-namespace", Map.of(
        "apiVersion", "v1",
        "kind", "Namespace",
        "metadata", Map.of("name", "my-app")));

KubernetesManifest service = cluster.addManifest("my-service", Map.of(
        "metadata", Map.of(
                "name", "myservice",
                "namespace", "my-app"),
        "spec", Map.of()));

service.node.addDependency(namespace);
csharp
Cluster cluster;

var namespace = cluster.AddManifest("my-namespace", new Dictionary<string, object> {
    { "apiVersion", "v1" },
    { "kind", "Namespace" },
    { "metadata", new Dictionary<string, string> { { "name", "my-app" } } }
});

var service = cluster.AddManifest("my-service", new Dictionary<string, object> {
    { "metadata", new Dictionary<string, string> {
        { "name", "myservice" },
        { "namespace", "my-app" }
    } },
    { "spec", new Dictionary<string, object> { } }
});

service.Node.AddDependency(namespace);
go
var cluster Cluster

namespace := cluster.addManifest(jsii.String("my-namespace"), map[string]interface{}{
	"apiVersion": jsii.String("v1"),
	"kind": jsii.String("Namespace"),
	"metadata": map[string]*string{
		"name": jsii.String("my-app"),
	},
})

service := cluster.addManifest(jsii.String("my-service"), map[string]interface{}{
	"metadata": map[string]*string{
		"name": jsii.String("myservice"),
		"namespace": jsii.String("my-app"),
	},
	"spec": map[string]interface{}{
	},
})

service.Node.AddDependency(namespace)

NOTE: when a KubernetesManifest includes multiple resources (either directly or through cluster.addManifest()) (e.g. cluster.addManifest('foo', r1, r2, r3,...)), these resources will be applied as a single manifest via kubectl and will be applied sequentially (the standard behavior in kubectl).


Since Kubernetes manifests are implemented as CloudFormation resources in the CDK. This means that if the manifest is deleted from your code (or the stack is deleted), the next cdk deploy will issue a kubectl delete command and the Kubernetes resources in that manifest will be deleted.

Resource Pruning ​

When a resource is deleted from a Kubernetes manifest, the EKS module will automatically delete these resources by injecting a prune label to all manifest resources. This label is then passed to kubectl apply --prune.

Pruning is enabled by default but can be disabled through the prune option when a cluster is defined:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

new eks.Cluster(this, 'MyCluster', {
  version: eks.KubernetesVersion.V1_36,
  prune: false,
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


eks.Cluster(self, "MyCluster",
    version=eks.KubernetesVersion.V1_36,
    prune=False,
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


Cluster.Builder.create(this, "MyCluster")
        .version(KubernetesVersion.V1_36)
        .prune(false)
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


new Cluster(this, "MyCluster", new ClusterProps {
    Version = KubernetesVersion.V1_36,
    Prune = false,
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


eks.NewCluster(this, jsii.String("MyCluster"), &ClusterProps{
	Version: eks.KubernetesVersion_V1_36(),
	Prune: jsii.Boolean(false),
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

Manifests Validation ​

The kubectl CLI supports applying a manifest by skipping the validation. This can be accomplished by setting the skipValidation flag to true in the KubernetesManifest props.

ts
declare const cluster: eks.Cluster;
new eks.KubernetesManifest(this, 'HelloAppWithoutValidation', {
  cluster,
  manifest: [{ foo: 'bar' }],
  skipValidation: true,
});
python
# cluster: eks.Cluster

eks.KubernetesManifest(self, "HelloAppWithoutValidation",
    cluster=cluster,
    manifest=[{"foo": "bar"}],
    skip_validation=True
)
java
Cluster cluster;

KubernetesManifest.Builder.create(this, "HelloAppWithoutValidation")
        .cluster(cluster)
        .manifest(List.of(Map.of("foo", "bar")))
        .skipValidation(true)
        .build();
csharp
Cluster cluster;

new KubernetesManifest(this, "HelloAppWithoutValidation", new KubernetesManifestProps {
    Cluster = cluster,
    Manifest = new [] { new Dictionary<string, object> { { "foo", "bar" } } },
    SkipValidation = true
});
go
var cluster Cluster

eks.NewKubernetesManifest(this, jsii.String("HelloAppWithoutValidation"), &KubernetesManifestProps{
	Cluster: Cluster,
	Manifest: []map[string]interface{}{
		map[string]interface{}{
			"foo": jsii.String("bar"),
		},
	},
	SkipValidation: jsii.Boolean(true),
})

Helm Charts ​

The HelmChart construct or cluster.addHelmChart method can be used to add Kubernetes resources to this cluster using Helm.

When using cluster.addHelmChart, the manifest construct is defined within the cluster's stack scope. If the manifest contains attributes from a different stack which depend on the cluster stack, a circular dependency will be created and you will get a synth time error. To avoid this, directly use new HelmChart to create the chart in the scope of the other stack.

The following example will install the NGINX Ingress Controller to your cluster using Helm.

ts
declare const cluster: eks.Cluster;
// option 1: use a construct
new eks.HelmChart(this, 'NginxIngress', {
  cluster,
  chart: 'nginx-ingress',
  repository: 'https://helm.nginx.com/stable',
  namespace: 'kube-system',
});

// or, option2: use `addHelmChart`
cluster.addHelmChart('NginxIngress', {
  chart: 'nginx-ingress',
  repository: 'https://helm.nginx.com/stable',
  namespace: 'kube-system',
});
python
# cluster: eks.Cluster

# option 1: use a construct
eks.HelmChart(self, "NginxIngress",
    cluster=cluster,
    chart="nginx-ingress",
    repository="https://helm.nginx.com/stable",
    namespace="kube-system"
)

# or, option2: use `addHelmChart`
cluster.add_helm_chart("NginxIngress",
    chart="nginx-ingress",
    repository="https://helm.nginx.com/stable",
    namespace="kube-system"
)
java
Cluster cluster;

// option 1: use a construct
// option 1: use a construct
HelmChart.Builder.create(this, "NginxIngress")
        .cluster(cluster)
        .chart("nginx-ingress")
        .repository("https://helm.nginx.com/stable")
        .namespace("kube-system")
        .build();

// or, option2: use `addHelmChart`
cluster.addHelmChart("NginxIngress", HelmChartOptions.builder()
        .chart("nginx-ingress")
        .repository("https://helm.nginx.com/stable")
        .namespace("kube-system")
        .build());
csharp
Cluster cluster;

// option 1: use a construct
// option 1: use a construct
new HelmChart(this, "NginxIngress", new HelmChartProps {
    Cluster = cluster,
    Chart = "nginx-ingress",
    Repository = "https://helm.nginx.com/stable",
    Namespace = "kube-system"
});

// or, option2: use `addHelmChart`
cluster.AddHelmChart("NginxIngress", new HelmChartOptions {
    Chart = "nginx-ingress",
    Repository = "https://helm.nginx.com/stable",
    Namespace = "kube-system"
});
go
var cluster Cluster

// option 1: use a construct
// option 1: use a construct
eks.NewHelmChart(this, jsii.String("NginxIngress"), &HelmChartProps{
	Cluster: Cluster,
	Chart: jsii.String("nginx-ingress"),
	Repository: jsii.String("https://helm.nginx.com/stable"),
	Namespace: jsii.String("kube-system"),
})

// or, option2: use `addHelmChart`
cluster.addHelmChart(jsii.String("NginxIngress"), &HelmChartOptions{
	Chart: jsii.String("nginx-ingress"),
	Repository: jsii.String("https://helm.nginx.com/stable"),
	Namespace: jsii.String("kube-system"),
})

Helm charts will be installed and updated using helm upgrade --install, where a few parameters are being passed down (such as repo, values, version, namespace, wait, timeout, etc). This means that if the chart is added to CDK with the same release name, it will try to update the chart in the cluster.

Additionally, the chartAsset property can be an aws-s3-assets.Asset. This allows the use of local, private helm charts.

ts
import * as s3Assets from 'aws-cdk-lib/aws-s3-assets';

declare const cluster: eks.Cluster;
const chartAsset = new s3Assets.Asset(this, 'ChartAsset', {
  path: '/path/to/asset'
});

cluster.addHelmChart('test-chart', {
  chartAsset: chartAsset,
});
python
import aws_cdk.aws_s3_assets as s3_assets

# cluster: eks.Cluster

chart_asset = s3_assets.Asset(self, "ChartAsset",
    path="/path/to/asset"
)

cluster.add_helm_chart("test-chart",
    chart_asset=chart_asset
)
java
import software.amazon.awscdk.services.s3.assets.*;

Cluster cluster;

Asset chartAsset = Asset.Builder.create(this, "ChartAsset")
        .path("/path/to/asset")
        .build();

cluster.addHelmChart("test-chart", HelmChartOptions.builder()
        .chartAsset(chartAsset)
        .build());
csharp
using Amazon.CDK.AWS.S3.Assets;

Cluster cluster;

var chartAsset = new Asset(this, "ChartAsset", new AssetProps {
    Path = "/path/to/asset"
});

cluster.AddHelmChart("test-chart", new HelmChartOptions {
    ChartAsset = chartAsset
});
go
import s3Assets "github.com/aws/aws-cdk-go/awscdk"

var cluster Cluster

chartAsset := s3Assets.NewAsset(this, jsii.String("ChartAsset"), &AssetProps{
	Path: jsii.String("/path/to/asset"),
})

cluster.addHelmChart(jsii.String("test-chart"), &HelmChartOptions{
	ChartAsset: chartAsset,
})

Nested values passed to the values parameter should be provided as a nested dictionary:

ts
declare const cluster: eks.Cluster;

cluster.addHelmChart('ExternalSecretsOperator', {
  chart: 'external-secrets',
  release: 'external-secrets',
  repository: 'https://charts.external-secrets.io',
  namespace: 'external-secrets',
  values: {
    installCRDs: true,
    webhook: {
      port: 9443
    }
  },
});
python
# cluster: eks.Cluster


cluster.add_helm_chart("ExternalSecretsOperator",
    chart="external-secrets",
    release="external-secrets",
    repository="https://charts.external-secrets.io",
    namespace="external-secrets",
    values={
        "install_cr_ds": True,
        "webhook": {
            "port": 9443
        }
    }
)
java
Cluster cluster;


cluster.addHelmChart("ExternalSecretsOperator", HelmChartOptions.builder()
        .chart("external-secrets")
        .release("external-secrets")
        .repository("https://charts.external-secrets.io")
        .namespace("external-secrets")
        .values(Map.of(
                "installCRDs", true,
                "webhook", Map.of(
                        "port", 9443)))
        .build());
csharp
Cluster cluster;


cluster.AddHelmChart("ExternalSecretsOperator", new HelmChartOptions {
    Chart = "external-secrets",
    Release = "external-secrets",
    Repository = "https://charts.external-secrets.io",
    Namespace = "external-secrets",
    Values = new Dictionary<string, object> {
        { "installCRDs", true },
        { "webhook", new Dictionary<string, int> {
            { "port", 9443 }
        } }
    }
});
go
var cluster Cluster


cluster.addHelmChart(jsii.String("ExternalSecretsOperator"), &HelmChartOptions{
	Chart: jsii.String("external-secrets"),
	Release: jsii.String("external-secrets"),
	Repository: jsii.String("https://charts.external-secrets.io"),
	Namespace: jsii.String("external-secrets"),
	Values: map[string]interface{}{
		"installCRDs": jsii.Boolean(true),
		"webhook": map[string]*f64{
			"port": jsii.Number(9443),
		},
	},
})

Helm chart can come with Custom Resource Definitions (CRDs) defined that by default will be installed by helm as well. However in special cases it might be needed to skip the installation of CRDs, for that the property skipCrds can be used.

ts
declare const cluster: eks.Cluster;
// option 1: use a construct
new eks.HelmChart(this, 'NginxIngress', {
  cluster,
  chart: 'nginx-ingress',
  repository: 'https://helm.nginx.com/stable',
  namespace: 'kube-system',
  skipCrds: true,
});
python
# cluster: eks.Cluster

# option 1: use a construct
eks.HelmChart(self, "NginxIngress",
    cluster=cluster,
    chart="nginx-ingress",
    repository="https://helm.nginx.com/stable",
    namespace="kube-system",
    skip_crds=True
)
java
Cluster cluster;

// option 1: use a construct
// option 1: use a construct
HelmChart.Builder.create(this, "NginxIngress")
        .cluster(cluster)
        .chart("nginx-ingress")
        .repository("https://helm.nginx.com/stable")
        .namespace("kube-system")
        .skipCrds(true)
        .build();
csharp
Cluster cluster;

// option 1: use a construct
// option 1: use a construct
new HelmChart(this, "NginxIngress", new HelmChartProps {
    Cluster = cluster,
    Chart = "nginx-ingress",
    Repository = "https://helm.nginx.com/stable",
    Namespace = "kube-system",
    SkipCrds = true
});
go
var cluster Cluster

// option 1: use a construct
// option 1: use a construct
eks.NewHelmChart(this, jsii.String("NginxIngress"), &HelmChartProps{
	Cluster: Cluster,
	Chart: jsii.String("nginx-ingress"),
	Repository: jsii.String("https://helm.nginx.com/stable"),
	Namespace: jsii.String("kube-system"),
	SkipCrds: jsii.Boolean(true),
})

OCI Charts ​

OCI charts are also supported. Also replace the ${VARS} with appropriate values.

ts
declare const cluster: eks.Cluster;
// option 1: use a construct
new eks.HelmChart(this, 'MyOCIChart', {
  cluster,
  chart: 'some-chart',
  repository: 'oci://${ACCOUNT_ID}.dkr.ecr.${ACCOUNT_REGION}.amazonaws.com/${REPO_NAME}',
  namespace: 'oci',
  version: '0.0.1'
});
python
# cluster: eks.Cluster

# option 1: use a construct
eks.HelmChart(self, "MyOCIChart",
    cluster=cluster,
    chart="some-chart",
    repository="oci://${ACCOUNT_ID}.dkr.ecr.${ACCOUNT_REGION}.amazonaws.com/${REPO_NAME}",
    namespace="oci",
    version="0.0.1"
)
java
Cluster cluster;

// option 1: use a construct
// option 1: use a construct
HelmChart.Builder.create(this, "MyOCIChart")
        .cluster(cluster)
        .chart("some-chart")
        .repository("oci://${ACCOUNT_ID}.dkr.ecr.${ACCOUNT_REGION}.amazonaws.com/${REPO_NAME}")
        .namespace("oci")
        .version("0.0.1")
        .build();
csharp
Cluster cluster;

// option 1: use a construct
// option 1: use a construct
new HelmChart(this, "MyOCIChart", new HelmChartProps {
    Cluster = cluster,
    Chart = "some-chart",
    Repository = "oci://${ACCOUNT_ID}.dkr.ecr.${ACCOUNT_REGION}.amazonaws.com/${REPO_NAME}",
    Namespace = "oci",
    Version = "0.0.1"
});
go
var cluster Cluster

// option 1: use a construct
// option 1: use a construct
eks.NewHelmChart(this, jsii.String("MyOCIChart"), &HelmChartProps{
	Cluster: Cluster,
	Chart: jsii.String("some-chart"),
	Repository: jsii.String("oci://${ACCOUNT_ID}.dkr.ecr.${ACCOUNT_REGION}.amazonaws.com/${REPO_NAME}"),
	Namespace: jsii.String("oci"),
	Version: jsii.String("0.0.1"),
})

Helm charts are implemented as CloudFormation resources in CDK. This means that if the chart is deleted from your code (or the stack is deleted), the next cdk deploy will issue a helm uninstall command and the Helm chart will be deleted.

When there is no release defined, a unique ID will be allocated for the release based on the construct path.

By default, all Helm charts will be installed concurrently. In some cases, this could cause race conditions where two Helm charts attempt to deploy the same resource or if Helm charts depend on each other. You can use chart.node.addDependency() in order to declare a dependency order between charts:

ts
declare const cluster: eks.Cluster;
const chart1 = cluster.addHelmChart('MyChart', {
  chart: 'foo',
});
const chart2 = cluster.addHelmChart('MyChart', {
  chart: 'bar',
});

chart2.node.addDependency(chart1);
python
# cluster: eks.Cluster

chart1 = cluster.add_helm_chart("MyChart",
    chart="foo"
)
chart2 = cluster.add_helm_chart("MyChart",
    chart="bar"
)

chart2.node.add_dependency(chart1)
java
Cluster cluster;

HelmChart chart1 = cluster.addHelmChart("MyChart", HelmChartOptions.builder()
        .chart("foo")
        .build());
HelmChart chart2 = cluster.addHelmChart("MyChart", HelmChartOptions.builder()
        .chart("bar")
        .build());

chart2.node.addDependency(chart1);
csharp
Cluster cluster;

var chart1 = cluster.AddHelmChart("MyChart", new HelmChartOptions {
    Chart = "foo"
});
var chart2 = cluster.AddHelmChart("MyChart", new HelmChartOptions {
    Chart = "bar"
});

chart2.Node.AddDependency(chart1);
go
var cluster Cluster

chart1 := cluster.addHelmChart(jsii.String("MyChart"), &HelmChartOptions{
	Chart: jsii.String("foo"),
})
chart2 := cluster.addHelmChart(jsii.String("MyChart"), &HelmChartOptions{
	Chart: jsii.String("bar"),
})

chart2.Node.AddDependency(chart1)

CDK8s Charts ​

CDK8s is an open-source library that enables Kubernetes manifest authoring using familiar programming languages. It is founded on the same technologies as the AWS CDK, such as constructs and jsii.

To learn more about cdk8s, visit the Getting Started tutorials.

The EKS module natively integrates with cdk8s and allows you to apply cdk8s charts on AWS EKS clusters via the cluster.addCdk8sChart method.

In addition to cdk8s, you can also use cdk8s+, which provides higher level abstraction for the core kubernetes api objects. You can think of it like the L2 constructs for Kubernetes. Any other cdk8s based libraries are also supported, for example cdk8s-debore.

To get started, add the following dependencies to your package.json file:

json
"dependencies": {
  "cdk8s": "^2.0.0",
  "cdk8s-plus-25": "^2.0.0",
  "constructs": "^10.5.0"
}

Note that here we are using cdk8s-plus-25 as we are targeting Kubernetes version 1.25.0. If you operate a different kubernetes version, you should use the corresponding cdk8s-plus-XX library. See Select the appropriate cdk8s+ library for more details.

Similarly to how you would create a stack by extending aws-cdk-lib.Stack, we recommend you create a chart of your own that extends cdk8s.Chart, and add your kubernetes resources to it. You can use aws-cdk construct attributes and properties inside your cdk8s construct freely.

In this example we create a chart that accepts an s3.Bucket and passes its name to a kubernetes pod as an environment variable.

+ my-chart.ts

ts
import { aws_s3 as s3 } from 'aws-cdk-lib';
import * as constructs from 'constructs';
import * as cdk8s from 'cdk8s';
import * as kplus from 'cdk8s-plus-25';

export interface MyChartProps {
  readonly bucket: s3.Bucket;
}

export class MyChart extends cdk8s.Chart {
  constructor(scope: constructs.Construct, id: string, props: MyChartProps) {
    super(scope, id);

    new kplus.Pod(this, 'Pod', {
      containers: [
        {
          image: 'my-image',
          envVariables: {
            BUCKET_NAME: kplus.EnvValue.fromValue(props.bucket.bucketName),
          },
        }
      ],
    });
  }
}
python
from aws_cdk import aws_s3 as s3
import constructs as constructs
import cdk8s as cdk8s
import cdk8s_plus_25 as kplus

class MyChart(cdk8s.Chart):
    def __init__(self, scope, id, *, bucket):
        super().__init__(scope, id)

        kplus.Pod(self, "Pod",
            containers=[kplus.ContainerProps(
                image="my-image",
                env_variables={
                    "BUCKET_NAME": kplus.EnvValue.from_value(bucket.bucket_name)
                }
            )
            ]
        )
java
import software.amazon.awscdk.services.s3.*;
import software.constructs.*;
import org.cdk8s.*;
import org.cdk8s.plus25.*;

public class MyChartProps {
    private Bucket bucket;
    public Bucket getBucket() {
        return this.bucket;
    }
    public MyChartProps bucket(Bucket bucket) {
        this.bucket = bucket;
        return this;
    }
}

public class MyChart extends Chart {
    public MyChart(Construct scope, String id, MyChartProps props) {
        super(scope, id);

        Pod.Builder.create(this, "Pod")
                .containers(List.of(ContainerProps.builder()
                        .image("my-image")
                        .envVariables(Map.of(
                                "BUCKET_NAME", EnvValue.fromValue(props.getBucket().getBucketName())))
                        .build()))
                .build();
    }
}
csharp
using Amazon.CDK.AWS.S3;
using Constructs;
using Org.Cdk8s;
using Org.Cdk8s.Plus25;

class MyChartProps
{
    public Bucket Bucket { get; set; }
}

class MyChart : Chart
{
    public MyChart(Construct scope, string id, MyChartProps props) : base(scope, id)
    {

        new Pod(this, "Pod", new PodProps {
            Containers = new [] { new ContainerProps {
                Image = "my-image",
                EnvVariables = new Dictionary<string, EnvValue> {
                    { "BUCKET_NAME", EnvValue.FromValue(props.Bucket.BucketName) }
                }
            } }
        });
    }
}
go
import "github.com/aws/aws-cdk-go/awscdk"
import constructs "github.com/aws/constructs-go/constructs"
import cdk8s "github.com/cdk8s-team/cdk8s-core-go/cdk8s"
import "github.com/cdk8s-team/cdk8s-plus-go/cdk8splus25"

type MyChartProps struct {
	bucket Bucket
}

type MyChart struct {
	Chart
}

func NewMyChart(scope Construct, id *string, props MyChartProps) *MyChart {
	this := &MyChart{}
	cdk8s.NewChart_Override(this, scope, id)

	kplus.NewPod(this, jsii.String("Pod"), &PodProps{
		Containers: []ContainerProps{
			&ContainerProps{
				Image: jsii.String("my-image"),
				EnvVariables: map[string]EnvValue{
					"BUCKET_NAME": kplus.EnvValue_fromValue(props.bucket.bucketName),
				},
			},
		},
	})
	return this
}

Then, in your AWS CDK app:

ts
declare const cluster: eks.Cluster;

// some bucket..
const bucket = new s3.Bucket(this, 'Bucket');

// create a cdk8s chart and use `cdk8s.App` as the scope.
const myChart = new MyChart(new cdk8s.App(), 'MyChart', { bucket });

// add the cdk8s chart to the cluster
cluster.addCdk8sChart('my-chart', myChart);
python
# cluster: eks.Cluster


# some bucket..
bucket = s3.Bucket(self, "Bucket")

# create a cdk8s chart and use `cdk8s.App` as the scope.
my_chart = MyChart(cdk8s.App(), "MyChart", bucket=bucket)

# add the cdk8s chart to the cluster
cluster.add_cdk8s_chart("my-chart", my_chart)
java
Cluster cluster;


// some bucket..
Bucket bucket = new Bucket(this, "Bucket");

// create a cdk8s chart and use `cdk8s.App` as the scope.
MyChart myChart = new MyChart(new App(), "MyChart", new MyChartProps().bucket(bucket));

// add the cdk8s chart to the cluster
cluster.addCdk8sChart("my-chart", myChart);
csharp
Cluster cluster;


// some bucket..
var bucket = new Bucket(this, "Bucket");

// create a cdk8s chart and use `cdk8s.App` as the scope.
var myChart = new MyChart(new App(), "MyChart", new MyChartProps { Bucket = bucket });

// add the cdk8s chart to the cluster
cluster.AddCdk8sChart("my-chart", myChart);
go
var cluster Cluster


// some bucket..
bucket := s3.NewBucket(this, jsii.String("Bucket"))

// create a cdk8s chart and use `cdk8s.App` as the scope.
myChart := NewMyChart(cdk8s.NewApp(), jsii.String("MyChart"), &myChartProps{
	bucket: bucket,
})

// add the cdk8s chart to the cluster
cluster.addCdk8sChart(jsii.String("my-chart"), myChart)

Custom CDK8s Constructs ​

You can also compose a few stock cdk8s+ constructs into your own custom construct. However, since mixing scopes between aws-cdk and cdk8s is currently not supported, the Construct class you'll need to use is the one from the constructs module, and not from aws-cdk-lib like you normally would. This is why we used new cdk8s.App() as the scope of the chart above.

ts
import * as constructs from 'constructs';
import * as cdk8s from 'cdk8s';
import * as kplus from 'cdk8s-plus-25';

export interface LoadBalancedWebService {
  readonly port: number;
  readonly image: string;
  readonly replicas: number;
}

const app = new cdk8s.App();
const chart = new cdk8s.Chart(app, 'my-chart');

export class LoadBalancedWebService extends constructs.Construct {
  constructor(scope: constructs.Construct, id: string, props: LoadBalancedWebService) {
    super(scope, id);

    const deployment = new kplus.Deployment(chart, 'Deployment', {
      replicas: props.replicas,
      containers: [ new kplus.Container({ image: props.image }) ],
    });

    deployment.exposeViaService({
      ports: [
        { port: props.port },
      ],
      serviceType: kplus.ServiceType.LOAD_BALANCER,
    });
  }
}
python
import constructs as constructs
import cdk8s as cdk8s
import cdk8s_plus_25 as kplus

app = cdk8s.App()
chart = cdk8s.Chart(app, "my-chart")

class LoadBalancedWebService(constructs.Construct):
    def __init__(self, scope, id, props):
        super().__init__(scope, id)

        deployment = kplus.Deployment(chart, "Deployment",
            replicas=props.replicas,
            containers=[kplus.Container(image=props.image)]
        )

        deployment.expose_via_service(
            ports=[kplus.ServicePort(port=props.port)
            ],
            service_type=kplus.ServiceType.LOAD_BALANCER
        )
java
import software.constructs.*;
import org.cdk8s.*;
import org.cdk8s.plus25.*;

public class LoadBalancedWebService {
    private Number port;
    public Number getPort() {
        return this.port;
    }
    public LoadBalancedWebService port(Number port) {
        this.port = port;
        return this;
    }
    private String image;
    public String getImage() {
        return this.image;
    }
    public LoadBalancedWebService image(String image) {
        this.image = image;
        return this;
    }
    private Number replicas;
    public Number getReplicas() {
        return this.replicas;
    }
    public LoadBalancedWebService replicas(Number replicas) {
        this.replicas = replicas;
        return this;
    }
}

App app = new App();
Chart chart = new Chart(app, "my-chart");

public class LoadBalancedWebService extends Construct {
    public LoadBalancedWebService(Construct scope, String id, LoadBalancedWebService props) {
        super(scope, id);

        Deployment deployment = Deployment.Builder.create(chart, "Deployment")
                .replicas(props.getReplicas())
                .containers(List.of(Container.Builder.create().image(props.getImage()).build()))
                .build();

        deployment.exposeViaService(DeploymentExposeViaServiceOptions.builder()
                .ports(List.of(ServicePort.builder().port(props.getPort()).build()))
                .serviceType(ServiceType.LOAD_BALANCER)
                .build());
    }
}
csharp
using Constructs;
using Org.Cdk8s;
using Org.Cdk8s.Plus25;

class LoadBalancedWebService
{
    public int Port { get; set; }
    public string Image { get; set; }
    public int Replicas { get; set; }
}

var app = new App();
var chart = new Chart(app, "my-chart");

class LoadBalancedWebService : Construct
{
    public LoadBalancedWebService(Construct scope, string id, LoadBalancedWebService props) : base(scope, id)
    {

        var deployment = new Deployment(chart, "Deployment", new DeploymentProps {
            Replicas = props.Replicas,
            Containers = new [] { new Container(new ContainerProps { Image = props.Image }) }
        });

        deployment.ExposeViaService(new DeploymentExposeViaServiceOptions {
            Ports = new [] { new ServicePort { Port = props.Port } },
            ServiceType = ServiceType.LOAD_BALANCER
        });
    }
}
go
import constructs "github.com/aws/constructs-go/constructs"
import "github.com/cdk8s-team/cdk8s-core-go/cdk8s"
import "github.com/cdk8s-team/cdk8s-plus-go/cdk8splus25"

type LoadBalancedWebService struct {
	port *f64
	image *string
	replicas *f64
}

app := cdk8s.NewApp()
chart := cdk8s.NewChart(app, jsii.String("my-chart"))

type LoadBalancedWebService struct {
	Construct
}

func NewLoadBalancedWebService(scope Construct, id *string, props LoadBalancedWebService) *LoadBalancedWebService {
	this := &LoadBalancedWebService{}
	constructs.NewConstruct_Override(this, scope, id)

	deployment := kplus.NewDeployment(chart, jsii.String("Deployment"), &DeploymentProps{
		Replicas: props.replicas,
		Containers: []ContainerProps{
			kplus.NewContainer(&ContainerProps{
				Image: props.image,
			}),
		},
	})

	deployment.ExposeViaService(&DeploymentExposeViaServiceOptions{
		Ports: []ServicePort{
			&ServicePort{
				Port: props.port,
			},
		},
		ServiceType: kplus.ServiceType_LOAD_BALANCER,
	})
	return this
}

Manually importing k8s specs and CRD's ​

If you find yourself unable to use cdk8s+, or just like to directly use the k8s native objects or CRD's, you can do so by manually importing them using the cdk8s-cli.

See Importing kubernetes objects for detailed instructions.

Patching Kubernetes Resources ​

The KubernetesPatch construct can be used to update existing kubernetes resources. The following example can be used to patch the hello-kubernetes deployment from the example above with 5 replicas.

ts
declare const cluster: eks.Cluster;
new eks.KubernetesPatch(this, 'hello-kub-deployment-label', {
  cluster,
  resourceName: "deployment/hello-kubernetes",
  applyPatch: { spec: { replicas: 5 } },
  restorePatch: { spec: { replicas: 3 } },
})
python
# cluster: eks.Cluster

eks.KubernetesPatch(self, "hello-kub-deployment-label",
    cluster=cluster,
    resource_name="deployment/hello-kubernetes",
    apply_patch={"spec": {"replicas": 5}},
    restore_patch={"spec": {"replicas": 3}}
)
java
Cluster cluster;

KubernetesPatch.Builder.create(this, "hello-kub-deployment-label")
        .cluster(cluster)
        .resourceName("deployment/hello-kubernetes")
        .applyPatch(Map.of("spec", Map.of("replicas", 5)))
        .restorePatch(Map.of("spec", Map.of("replicas", 3)))
        .build();
csharp
Cluster cluster;

new KubernetesPatch(this, "hello-kub-deployment-label", new KubernetesPatchProps {
    Cluster = cluster,
    ResourceName = "deployment/hello-kubernetes",
    ApplyPatch = new Dictionary<string, object> { { "spec", new Dictionary<string, int> { { "replicas", 5 } } } },
    RestorePatch = new Dictionary<string, object> { { "spec", new Dictionary<string, int> { { "replicas", 3 } } } }
});
go
var cluster Cluster

eks.NewKubernetesPatch(this, jsii.String("hello-kub-deployment-label"), &KubernetesPatchProps{
	Cluster: Cluster,
	ResourceName: jsii.String("deployment/hello-kubernetes"),
	ApplyPatch: map[string]interface{}{
		"spec": map[string]*f64{
			"replicas": jsii.Number(5),
		},
	},
	RestorePatch: map[string]interface{}{
		"spec": map[string]*f64{
			"replicas": jsii.Number(3),
		},
	},
})

Querying Kubernetes Resources ​

The KubernetesObjectValue construct can be used to query for information about kubernetes objects, and use that as part of your CDK application.

For example, you can fetch the address of a LoadBalancer type service:

ts
declare const cluster: eks.Cluster;
// query the load balancer address
const myServiceAddress = new eks.KubernetesObjectValue(this, 'LoadBalancerAttribute', {
  cluster: cluster,
  objectType: 'service',
  objectName: 'my-service',
  jsonPath: '.status.loadBalancer.ingress[0].hostname', // https://kubernetes.io/docs/reference/kubectl/jsonpath/
});

// pass the address to a lambda function
const proxyFunction = new lambda.Function(this, 'ProxyFunction', {
  handler: 'index.handler',
  code: lambda.Code.fromInline('my-code'),
  runtime: lambda.Runtime.NODEJS_LATEST,
  environment: {
    myServiceAddress: myServiceAddress.value,
  },
})
python
# cluster: eks.Cluster

# query the load balancer address
my_service_address = eks.KubernetesObjectValue(self, "LoadBalancerAttribute",
    cluster=cluster,
    object_type="service",
    object_name="my-service",
    json_path=".status.loadBalancer.ingress[0].hostname"
)

# pass the address to a lambda function
proxy_function = lambda_.Function(self, "ProxyFunction",
    handler="index.handler",
    code=lambda_.Code.from_inline("my-code"),
    runtime=lambda_.Runtime.NODEJS_LATEST,
    environment={
        "my_service_address": my_service_address.value
    }
)
java
Cluster cluster;

// query the load balancer address
KubernetesObjectValue myServiceAddress = KubernetesObjectValue.Builder.create(this, "LoadBalancerAttribute")
        .cluster(cluster)
        .objectType("service")
        .objectName("my-service")
        .jsonPath(".status.loadBalancer.ingress[0].hostname")
        .build();

// pass the address to a lambda function
Function proxyFunction = Function.Builder.create(this, "ProxyFunction")
        .handler("index.handler")
        .code(Code.fromInline("my-code"))
        .runtime(Runtime.NODEJS_LATEST)
        .environment(Map.of(
                "myServiceAddress", myServiceAddress.getValue()))
        .build();
csharp
Cluster cluster;

// query the load balancer address
var myServiceAddress = new KubernetesObjectValue(this, "LoadBalancerAttribute", new KubernetesObjectValueProps {
    Cluster = cluster,
    ObjectType = "service",
    ObjectName = "my-service",
    JsonPath = ".status.loadBalancer.ingress[0].hostname"
});

// pass the address to a lambda function
var proxyFunction = new Function(this, "ProxyFunction", new FunctionProps {
    Handler = "index.handler",
    Code = Code.FromInline("my-code"),
    Runtime = Runtime.NODEJS_LATEST,
    Environment = new Dictionary<string, string> {
        { "myServiceAddress", myServiceAddress.Value }
    }
});
go
var cluster Cluster

// query the load balancer address
myServiceAddress := eks.NewKubernetesObjectValue(this, jsii.String("LoadBalancerAttribute"), &KubernetesObjectValueProps{
	Cluster: cluster,
	ObjectType: jsii.String("service"),
	ObjectName: jsii.String("my-service"),
	JsonPath: jsii.String(".status.loadBalancer.ingress[0].hostname"),
})

// pass the address to a lambda function
proxyFunction := lambda.NewFunction(this, jsii.String("ProxyFunction"), &FunctionProps{
	Handler: jsii.String("index.handler"),
	Code: lambda.Code_FromInline(jsii.String("my-code")),
	Runtime: lambda.Runtime_NODEJS_LATEST(),
	Environment: map[string]*string{
		"myServiceAddress": myServiceAddress.value,
	},
})

Specifically, since the above use-case is quite common, there is an easier way to access that information:

ts
declare const cluster: eks.Cluster;
const loadBalancerAddress = cluster.getServiceLoadBalancerAddress('my-service');
python
# cluster: eks.Cluster

load_balancer_address = cluster.get_service_load_balancer_address("my-service")
java
Cluster cluster;

String loadBalancerAddress = cluster.getServiceLoadBalancerAddress("my-service");
csharp
Cluster cluster;

var loadBalancerAddress = cluster.GetServiceLoadBalancerAddress("my-service");
go
var cluster Cluster

loadBalancerAddress := cluster.GetServiceLoadBalancerAddress(jsii.String("my-service"))

Add-ons ​

Add-ons is a software that provides supporting operational capabilities to Kubernetes applications. The EKS module supports adding add-ons to your cluster using the eks.Addon class.

ts
declare const cluster: eks.Cluster;

new eks.Addon(this, 'Addon', {
  cluster,
  addonName: 'coredns',
  addonVersion: 'v1.11.4-eksbuild.2',
  // whether to preserve the add-on software on your cluster but Amazon EKS stops managing any settings for the add-on.
  preserveOnDelete: false,
  configurationValues: {
    replicaCount: 2,
  },
});
python
# cluster: eks.Cluster


eks.Addon(self, "Addon",
    cluster=cluster,
    addon_name="coredns",
    addon_version="v1.11.4-eksbuild.2",
    # whether to preserve the add-on software on your cluster but Amazon EKS stops managing any settings for the add-on.
    preserve_on_delete=False,
    configuration_values={
        "replica_count": 2
    }
)
java
Cluster cluster;


Addon.Builder.create(this, "Addon")
        .cluster(cluster)
        .addonName("coredns")
        .addonVersion("v1.11.4-eksbuild.2")
        // whether to preserve the add-on software on your cluster but Amazon EKS stops managing any settings for the add-on.
        .preserveOnDelete(false)
        .configurationValues(Map.of(
                "replicaCount", 2))
        .build();
csharp
Cluster cluster;


new Addon(this, "Addon", new AddonProps {
    Cluster = cluster,
    AddonName = "coredns",
    AddonVersion = "v1.11.4-eksbuild.2",
    // whether to preserve the add-on software on your cluster but Amazon EKS stops managing any settings for the add-on.
    PreserveOnDelete = false,
    ConfigurationValues = new Dictionary<string, object> {
        { "replicaCount", 2 }
    }
});
go
var cluster Cluster


eks.NewAddon(this, jsii.String("Addon"), &AddonProps{
	Cluster: Cluster,
	AddonName: jsii.String("coredns"),
	AddonVersion: jsii.String("v1.11.4-eksbuild.2"),
	// whether to preserve the add-on software on your cluster but Amazon EKS stops managing any settings for the add-on.
	PreserveOnDelete: jsii.Boolean(false),
	ConfigurationValues: map[string]interface{}{
		"replicaCount": jsii.Number(2),
	},
})

Using existing clusters ​

The Amazon EKS library allows defining Kubernetes resources such as Kubernetes manifests and Helm charts on clusters that are not defined as part of your CDK app.

First, you'll need to "import" a cluster to your CDK app. To do that, use the eks.Cluster.fromClusterAttributes() static method:

ts
const cluster = eks.Cluster.fromClusterAttributes(this, 'MyCluster', {
  clusterName: 'my-cluster-name',
  kubectlRoleArn: 'arn:aws:iam::1111111:role/iam-role-that-has-masters-access',
});
python
cluster = eks.Cluster.from_cluster_attributes(self, "MyCluster",
    cluster_name="my-cluster-name",
    kubectl_role_arn="arn:aws:iam::1111111:role/iam-role-that-has-masters-access"
)
java
ICluster cluster = Cluster.fromClusterAttributes(this, "MyCluster", ClusterAttributes.builder()
        .clusterName("my-cluster-name")
        .kubectlRoleArn("arn:aws:iam::1111111:role/iam-role-that-has-masters-access")
        .build());
csharp
var cluster = Cluster.FromClusterAttributes(this, "MyCluster", new ClusterAttributes {
    ClusterName = "my-cluster-name",
    KubectlRoleArn = "arn:aws:iam::1111111:role/iam-role-that-has-masters-access"
});
go
cluster := eks.Cluster_FromClusterAttributes(this, jsii.String("MyCluster"), &ClusterAttributes{
	ClusterName: jsii.String("my-cluster-name"),
	KubectlRoleArn: jsii.String("arn:aws:iam::1111111:role/iam-role-that-has-masters-access"),
})

Then, you can use addManifest or addHelmChart to define resources inside your Kubernetes cluster. For example:

ts
declare const cluster: eks.Cluster;
cluster.addManifest('Test', {
  apiVersion: 'v1',
  kind: 'ConfigMap',
  metadata: {
    name: 'myconfigmap',
  },
  data: {
    Key: 'value',
    Another: '123454',
  },
});
python
# cluster: eks.Cluster

cluster.add_manifest("Test", {
    "api_version": "v1",
    "kind": "ConfigMap",
    "metadata": {
        "name": "myconfigmap"
    },
    "data": {
        "Key": "value",
        "Another": "123454"
    }
})
java
Cluster cluster;

cluster.addManifest("Test", Map.of(
        "apiVersion", "v1",
        "kind", "ConfigMap",
        "metadata", Map.of(
                "name", "myconfigmap"),
        "data", Map.of(
                "Key", "value",
                "Another", "123454")));
csharp
Cluster cluster;

cluster.AddManifest("Test", new Dictionary<string, object> {
    { "apiVersion", "v1" },
    { "kind", "ConfigMap" },
    { "metadata", new Dictionary<string, string> {
        { "name", "myconfigmap" }
    } },
    { "data", new Dictionary<string, string> {
        { "Key", "value" },
        { "Another", "123454" }
    } }
});
go
var cluster Cluster

cluster.addManifest(jsii.String("Test"), map[string]interface{}{
	"apiVersion": jsii.String("v1"),
	"kind": jsii.String("ConfigMap"),
	"metadata": map[string]*string{
		"name": jsii.String("myconfigmap"),
	},
	"data": map[string]*string{
		"Key": jsii.String("value"),
		"Another": jsii.String("123454"),
	},
})

At the minimum, when importing clusters for kubectl management, you will need to specify:

  • clusterName - the name of the cluster.
  • kubectlRoleArn - the ARN of an IAM role mapped to the system:masters RBAC role. If the cluster you are importing was created using the AWS CDK, the CloudFormation stack has an output that includes an IAM role that can be used. Otherwise, you can create an IAM role and map it to system:masters manually. The trust policy of this role should include the the arn:aws::iam::${accountId}:root principal in order to allow the execution role of the kubectl resource to assume it.

If the cluster is configured with private-only or private and restricted public Kubernetes endpoint access, you must also specify:

  • kubectlSecurityGroupId - the ID of an EC2 security group that is allowed connections to the cluster's control security group. For example, the EKS managed cluster security group.
  • kubectlPrivateSubnetIds - a list of private VPC subnets IDs that will be used to access the Kubernetes endpoint.

Logging ​

EKS supports cluster logging for 5 different types of events:

  • API requests to the cluster.
  • Cluster access via the Kubernetes API.
  • Authentication requests into the cluster.
  • State of cluster controllers.
  • Scheduling decisions.

You can enable logging for each one separately using the clusterLogging property. For example:

ts
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';

const cluster = new eks.Cluster(this, 'Cluster', {
  // ...
  version: eks.KubernetesVersion.V1_36,
  clusterLogging: [
    eks.ClusterLoggingTypes.API,
    eks.ClusterLoggingTypes.AUTHENTICATOR,
    eks.ClusterLoggingTypes.SCHEDULER,
  ],
  kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
});
python
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer


cluster = eks.Cluster(self, "Cluster",
    # ...
    version=eks.KubernetesVersion.V1_36,
    cluster_logging=[eks.ClusterLoggingTypes.API, eks.ClusterLoggingTypes.AUTHENTICATOR, eks.ClusterLoggingTypes.SCHEDULER
    ],
    kubectl_layer=KubectlV36Layer(self, "kubectl")
)
java
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;


Cluster cluster = Cluster.Builder.create(this, "Cluster")
        // ...
        .version(KubernetesVersion.V1_36)
        .clusterLogging(List.of(ClusterLoggingTypes.API, ClusterLoggingTypes.AUTHENTICATOR, ClusterLoggingTypes.SCHEDULER))
        .kubectlLayer(new KubectlV36Layer(this, "kubectl"))
        .build();
csharp
using Amazon.CDK.LambdaLayer.KubectlV36;


var cluster = new Cluster(this, "Cluster", new ClusterProps {
    // ...
    Version = KubernetesVersion.V1_36,
    ClusterLogging = new [] { ClusterLoggingTypes.API, ClusterLoggingTypes.AUTHENTICATOR, ClusterLoggingTypes.SCHEDULER },
    KubectlLayer = new KubectlV36Layer(this, "kubectl")
});
go
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"


cluster := eks.NewCluster(this, jsii.String("Cluster"), &ClusterProps{
	// ...
	Version: eks.KubernetesVersion_V1_36(),
	ClusterLogging: []ClusterLoggingTypes{
		eks.ClusterLoggingTypes_API,
		eks.ClusterLoggingTypes_AUTHENTICATOR,
		eks.ClusterLoggingTypes_SCHEDULER,
	},
	KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
})

NodeGroup Repair Config ​

You can enable Managed Node Group auto-repair config using enableNodeAutoRepair property. For example:

ts
declare const cluster: eks.Cluster;

cluster.addNodegroupCapacity('NodeGroup', {
  enableNodeAutoRepair:true,
});
python
# cluster: eks.Cluster


cluster.add_nodegroup_capacity("NodeGroup",
    enable_node_auto_repair=True
)
java
Cluster cluster;


cluster.addNodegroupCapacity("NodeGroup", NodegroupOptions.builder()
        .enableNodeAutoRepair(true)
        .build());
csharp
Cluster cluster;


cluster.AddNodegroupCapacity("NodeGroup", new NodegroupOptions {
    EnableNodeAutoRepair = true
});
go
var cluster Cluster


cluster.AddNodegroupCapacity(jsii.String("NodeGroup"), &NodegroupOptions{
	EnableNodeAutoRepair: jsii.Boolean(true),
})

Types ​

NameKindSummary
AccessEntryConstruct (L2)Represents an access entry in an Amazon EKS cluster.
AddonConstruct (L2)Represents an Amazon EKS Add-On.
ClusterConstruct (L2)A Cluster represents a managed Kubernetes Service (EKS).
FargateClusterConstruct (L2)Defines an EKS cluster that runs entirely on AWS Fargate.
NodegroupConstruct (L2)The Nodegroup resource class.
OidcProviderNativeConstruct (L2)IAM OIDC identity providers are entities in IAM that describe an external identity provider (IdP) service that supports the OpenID Connect (OIDC) standard, such as Google or Salesforce.
OpenIdConnectProviderConstruct (L2)IAM OIDC identity providers are entities in IAM that describe an external identity provider (IdP) service that supports the OpenID Connect (OIDC) standard, such as Google or Salesforce.
CfnAccessEntryConstruct (L1)Creates an access entry.
CfnAddonConstruct (L1)Creates an Amazon EKS add-on.
CfnCapabilityConstruct (L1)An object representing a managed capability in an Amazon EKS cluster.
CfnCertificateAuthorityConstruct (L1)Resource Type definition for EKS CertificateAuthority.
CfnClusterConstruct (L1)Creates an Amazon EKS control plane.
CfnFargateProfileConstruct (L1)Creates an AWS Fargate profile for your Amazon EKS cluster.
CfnIdentityProviderConfigConstruct (L1)Associates an identity provider configuration to a cluster.
CfnNodegroupConstruct (L1)Creates a managed node group for an Amazon EKS cluster.
CfnPodIdentityAssociationConstruct (L1)Amazon EKS Pod Identity associations provide the ability to manage credentials for your applications, similar to the way that Amazon EC2 instance profiles provide credentials to Amazon EC2 instances.
AccessEntryAttributesStructRepresents the attributes of an access entry.
AccessEntryPropsStructRepresents the properties required to create an Amazon EKS access entry.
AccessEntryTypeEnumRepresents the different types of access entries that can be used in an Amazon EKS cluster.
AccessPolicyClassRepresents an Amazon EKS Access Policy that implements the IAccessPolicy interface.
AccessPolicyArnClassRepresents an Amazon EKS Access Policy ARN.
AccessPolicyNameOptionsStructRepresents the options required to create an Amazon EKS Access Policy using the fromAccessPolicyName() method.
AccessPolicyPropsStructProperties for configuring an Amazon EKS Access Policy.
AccessScopeStructRepresents the scope of an access policy.
AccessScopeTypeEnumRepresents the scope type of an access policy.
AddonAttributesStructRepresents the attributes of an addon for an Amazon EKS cluster.
AddonPropsStructProperties for creating an Amazon EKS Add-On.
AlbControllerConstructConstruct for installing the AWS ALB Contoller on EKS clusters.
AlbControllerHelmChartOptionsStructHelm chart options that can be set for AlbControllerChart To add any new supported values refer https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/main/helm/aws-load-balancer-controller/values.yaml.
AlbControllerOptionsStructOptions for AlbController.
AlbControllerPropsStructProperties for AlbController.
AlbControllerVersionClassController version.
AlbSchemeEnumALB Scheme.
AuthenticationModeEnumRepresents the authentication mode for an Amazon EKS cluster.
AutoScalingGroupCapacityOptionsStructOptions for adding worker nodes.
AutoScalingGroupOptionsStructOptions for adding an AutoScalingGroup as capacity.
AwsAuthConstructManages mapping between IAM users and roles to Kubernetes RBAC configuration.
AwsAuthMappingStructAwsAuth mapping.
AwsAuthPropsStructConfiguration props for the AwsAuth construct.
BootstrapOptionsStructEKS node bootstrapping options.
CapacityTypeEnumCapacity type of the managed node group.
CfnAccessEntry.AccessPolicyPropertyStructAn access policy includes permissions that allow Amazon EKS to authorize an IAM principal to work with Kubernetes objects on your cluster.
CfnAccessEntry.AccessScopePropertyStructThe scope of an AccessPolicy that's associated to an AccessEntry .
CfnAccessEntryPropsStructProperties for defining a CfnAccessEntry.
CfnAddon.NamespaceConfigPropertyStructThe custom namespace configuration to use with the add-on.
CfnAddon.PodIdentityAssociationPropertyStructAmazon EKS Pod Identity associations provide the ability to manage credentials for your applications, similar to the way that Amazon EC2 instance profiles provide credentials to Amazon EC2 instances.
CfnAddonPropsStructProperties for defining a CfnAddon.
CfnCapability.AckPropertyStructConfiguration settings for an ACK (AWS Controllers for Kubernetes) capability.
CfnCapability.ArgoCdPropertyStructConfiguration settings for an Argo CD capability.
CfnCapability.ArgoCdRoleMappingPropertyStructA mapping between an Argo CD role and IAM Identity CenterIAM;
CfnCapability.AwsIdcPropertyStructConfiguration for integrating Argo CD with IAM Identity Center.
CfnCapability.CapabilityConfigurationPropertyStructConfiguration settings for a capability.
CfnCapability.NetworkAccessPropertyStructConfiguration for network access to the Argo CD capability's managed API server endpoint.
CfnCapability.SsoIdentityPropertyStructAn IAM Identity CenterIAM;
CfnCapabilityPropsStructProperties for defining a CfnCapability.
CfnCertificateAuthority.ScheduledEventsPropertyStructThe scheduled auto-activation events for the certificate authority, computed from its validity window.
CfnCertificateAuthority.ValidityPropertyStructThe validity period of the certificate authority.
CfnCertificateAuthorityPropsStructProperties for defining a CfnCertificateAuthority.
CfnCluster.AccessConfigPropertyStructThe access configuration for the cluster.
CfnCluster.ActiveCertificateAuthorityPropertyStructIdentifies the certificate authority currently signing certificates for the cluster.
CfnCluster.BlockStoragePropertyStructIndicates the current configuration of the block storage capability on your EKS Auto Mode cluster.
CfnCluster.CertificateAuthorityPropertyStructThe certificate authority information for the cluster, including the trust bundle and the currently active signing certificate authority.
CfnCluster.ClusterLoggingPropertyStructThe cluster control plane logging configuration for your cluster.
CfnCluster.ComputeConfigPropertyStructIndicates the current configuration of the compute capability on your EKS Auto Mode cluster.
CfnCluster.ControlPlanePlacementPropertyStructThe placement configuration for all the control plane instances of your local Amazon EKS cluster on an AWS Outpost.
CfnCluster.ControlPlaneScalingConfigPropertyStructThe control plane scaling tier configuration.
CfnCluster.ElasticLoadBalancingPropertyStructIndicates the current configuration of the load balancing capability on your EKS Auto Mode cluster.
CfnCluster.EncryptionConfigPropertyStructThe encryption configuration for the cluster.
CfnCluster.EtcdPlacementPropertyStructThe placement configuration for the etcd instances of your local Amazon EKS cluster on an AWS Outpost.
CfnCluster.HorizontalPodAutoscalerControllerConfigPropertyStructThe horizontal pod autoscaler controller configuration.
CfnCluster.KubeApiServerConfigPropertyStructThe configuration for the Kubernetes API server on an Amazon EKS cluster.
CfnCluster.KubeControllerManagerConfigPropertyStructThe configuration for the Kubernetes controller manager on an Amazon EKS cluster.
CfnCluster.KubernetesNetworkConfigPropertyStructThe Kubernetes network configuration for the cluster.
CfnCluster.KubeSchedulerConfigPropertyStructThe configuration for the Kubernetes scheduler on an Amazon EKS cluster.
CfnCluster.LoggingPropertyStructEnable or disable exporting the Kubernetes control plane logs for your cluster to CloudWatch Logs.
CfnCluster.LoggingTypeConfigPropertyStructThe enabled logging type.
CfnCluster.NodeResourcesFitConfigPropertyStructThe NodeResourcesFit plugin configuration for the Kubernetes scheduler.
CfnCluster.OutpostConfigPropertyStructThe configuration of your local Amazon EKS cluster on an AWS Outpost.
CfnCluster.PodGcControllerConfigPropertyStructThe pod garbage collector controller configuration.
CfnCluster.ProviderPropertyStructIdentifies the AWS Key Management Service ( AWS ) key used to encrypt the secrets.
CfnCluster.RemoteNetworkConfigPropertyStructThe configuration in the cluster for EKS Hybrid Nodes.
CfnCluster.RemoteNodeNetworkPropertyStructA network CIDR that can contain hybrid nodes.
CfnCluster.RemotePodNetworkPropertyStructA network CIDR that can contain pods that run Kubernetes webhooks on hybrid nodes.
CfnCluster.ResourcesVpcConfigPropertyStructAn object representing the VPC configuration to use for an Amazon EKS cluster.
CfnCluster.ResourceWeightPropertyStructA resource weight entry for the scheduler scoring strategy.
CfnCluster.RollbackConfigPropertyStructThe rollback configuration to use for the cluster version rollback.
CfnCluster.ScoringStrategyPropertyStructThe scoring strategy configuration for the NodeResourcesFit scheduler plugin.
CfnCluster.ServiceNodePortRangePropertyStructThe port range for Kubernetes NodePort services.
CfnCluster.StorageConfigPropertyStructRequest to update the configuration of the storage capability of your EKS Auto Mode cluster.
CfnCluster.UpgradePolicyPropertyStructThe support policy to use for the cluster.
CfnCluster.ZonalShiftConfigPropertyStructThe configuration for zonal shift for the cluster.
CfnClusterPropsStructProperties for defining a CfnCluster.
CfnFargateProfile.LabelPropertyStructA key-value pair.
CfnFargateProfile.SelectorPropertyStructAn object representing an AWS Fargate profile selector.
CfnFargateProfilePropsStructProperties for defining a CfnFargateProfile.
CfnIdentityProviderConfig.OidcIdentityProviderConfigPropertyStructAn object representing the configuration for an OpenID Connect (OIDC) identity provider.
CfnIdentityProviderConfig.RequiredClaimPropertyStructA key-value pair that describes a required claim in the identity token.
CfnIdentityProviderConfigPropsStructProperties for defining a CfnIdentityProviderConfig.
CfnNodegroup.LaunchTemplateSpecificationPropertyStructAn object representing a node group launch template specification.
CfnNodegroup.NodeRepairConfigOverridesPropertyStructSpecify granular overrides for specific repair actions.
CfnNodegroup.NodeRepairConfigPropertyStructThe node auto repair configuration for the node group.
CfnNodegroup.RemoteAccessPropertyStructAn object representing the remote access configuration for the managed node group.
CfnNodegroup.ScalingConfigPropertyStructAn object representing the scaling configuration details for the Auto Scaling group that is associated with your node group.
CfnNodegroup.TaintPropertyStructA property that allows a node to repel a Pod .
CfnNodegroup.UpdateConfigPropertyStructThe update configuration for the node group.
CfnNodegroup.WarmPoolConfigPropertyStructThe warm pool configuration for the node group.
CfnNodegroupPropsStructProperties for defining a CfnNodegroup.
CfnPodIdentityAssociationPropsStructProperties for defining a CfnPodIdentityAssociation.
ClusterAttributesStructAttributes for EKS clusters.
ClusterLoggingTypesEnumEKS cluster logging types.
ClusterOptionsStructOptions for EKS clusters.
ClusterPropsStructCommon configuration props for EKS clusters.
CommonClusterOptionsStructOptions for configuring an EKS cluster.
ControlPlaneScalingTierEnumControl plane scaling tier for EKS Provisioned Control Plane.
CoreDnsComputeTypeEnumThe type of compute resources to use for CoreDNS.
CpuArchEnumCPU architecture.
DefaultCapacityTypeEnumThe default capacity type for the cluster.
EksOptimizedImageClassConstruct an Amazon Linux 2 image from the latest EKS Optimized AMI published in SSM.
EksOptimizedImagePropsStructProperties for EksOptimizedImage.
EndpointAccessClassEndpoint access characteristics.
FargateClusterPropsStructConfiguration props for EKS Fargate.
FargateProfileConstructFargate profiles allows an administrator to declare which pods run on Fargate.
FargateProfileOptionsStructOptions for defining EKS Fargate Profiles.
FargateProfilePropsStructConfiguration props for EKS Fargate Profiles.
GrantAccessOptionsStructOptions for granting access to a cluster.
HelmChartConstructRepresents a helm chart within the Kubernetes system.
HelmChartOptionsStructHelm Chart options.
HelmChartPropsStructHelm Chart properties.
IAccessEntryInterfaceRepresents an access entry in an Amazon EKS cluster.
IAccessPolicyInterfaceRepresents an access policy that defines the permissions and scope for a user or role to access an Amazon EKS cluster.
IAddonInterfaceRepresents an Amazon EKS Add-On.
IClusterInterfaceAn EKS cluster.
IdentityTypeEnumEnum representing the different identity types that can be used for a Kubernetes service account.
IKubectlProviderInterfaceImported KubectlProvider that can be used in place of the default one created by CDK.
IngressLoadBalancerAddressOptionsStructOptions for fetching an IngressLoadBalancerAddress.
INodegroupInterfaceNodeGroup interface.
IpFamilyEnumEKS cluster IP family.
KubectlProviderConstructImplementation of Kubectl Lambda.
KubectlProviderAttributesStructKubectl Provider Attributes.
KubectlProviderPropsStructProperties for a KubectlProvider.
KubernetesManifestConstructRepresents a manifest within the Kubernetes system.
KubernetesManifestOptionsStructOptions for KubernetesManifest.
KubernetesManifestPropsStructProperties for KubernetesManifest.
KubernetesObjectValueConstructRepresents a value of a specific object deployed in the cluster.
KubernetesObjectValuePropsStructProperties for KubernetesObjectValue.
KubernetesPatchConstructA CloudFormation resource which applies/restores a JSON patch into a Kubernetes resource.
KubernetesPatchPropsStructProperties for KubernetesPatch.
KubernetesVersionClassKubernetes cluster version.
LaunchTemplateSpecStructLaunch template property specification.
MachineImageTypeEnumThe machine image type.
NodegroupAmiTypeEnumThe AMI type for your node group.
NodegroupOptionsStructThe Nodegroup Options for addNodeGroup() method.
NodegroupPropsStructNodeGroup properties interface.
NodegroupRemoteAccessStructThe remote access (SSH) configuration to use with your node group.
NodeTypeEnumWhether the worker nodes should support GPU or just standard instances.
OidcProviderNativePropsStructInitialization properties for OidcProviderNative.
OpenIdConnectProviderPropsStructInitialization properties for OpenIdConnectProvider.
PatchTypeEnumValues for kubectl patch --type argument.
RemoteNodeNetworkStructNetwork configuration of nodes run on-premises with EKS Hybrid Nodes.
RemotePodNetworkStructNetwork configuration of pods run on-premises with EKS Hybrid Nodes.
SelectorStructFargate profile selector.
ServiceAccountConstructService Account.
ServiceAccountOptionsStructOptions for ServiceAccount.
ServiceAccountPropsStructProperties for defining service accounts.
ServiceLoadBalancerAddressOptionsStructOptions for fetching a ServiceLoadBalancerAddress.
TaintEffectEnumEffect types of kubernetes node taint.
TaintSpecStructTaint interface.