Amazon EKS V2 Construct Library
The aws-eks-v2 module is a rewrite of the existing aws-eks module (https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_eks-readme.html). This new iteration leverages native L1 CFN resources, replacing the previous custom resource approach for creating EKS clusters and Fargate Profiles.
Compared to the original EKS module, it has the following major changes:
- Use native L1 AWS::EKS::Cluster resource to replace custom resource Custom::AWSCDK-EKS-Cluster
- Use native L1 AWS::EKS::FargateProfile resource to replace custom resource Custom::AWSCDK-EKS-FargateProfile
- Kubectl Handler will not be created by default. It will only be created if users specify it.
- Remove AwsAuth construct. Permissions to the cluster will be managed by Access Entry.
- Remove the limit of 1 cluster per stack
- Remove nested stacks
- API changes to make them more ergonomic.
Quick start
Here is the minimal example of defining an AWS EKS cluster
const cluster = new eks.Cluster(this, 'hello-eks', {
version: eks.KubernetesVersion.V1_36,
});
cluster = eks.Cluster(self, "hello-eks",
version=eks.KubernetesVersion.V1_36
)
Cluster cluster = Cluster.Builder.create(this, "hello-eks")
.version(KubernetesVersion.V1_36)
.build();
var cluster = new Cluster(this, "hello-eks", new ClusterProps {
Version = KubernetesVersion.V1_36
});
cluster := eks.NewCluster(this, jsii.String("hello-eks"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
})
Architecture
kubectl | |
+------------>| Kubectl Handler |
| | (Optional) |
| +-----------------+
+-------------------------------------+-------------------------------------+
| EKS Cluster (Auto Mode) |
| AWS::EKS::Cluster |
| |
| +---------------------------------------------------------------------+ |
| | Auto Mode Compute (Managed by EKS) (Default) | |
| | | |
| | - Automatically provisions EC2 instances | |
| | - Auto scaling based on pod requirements | |
| | - No manual node group configuration needed | |
| | | |
| +---------------------------------------------------------------------+ |
| |
+---------------------------------------------------------------------------+
In a nutshell:
Auto Mode (Default) – The fully managed capacity mode in EKS. EKS automatically provisions and scales EC2 capacity based on pod requirements. It manages internal system and general-purpose NodePools, handles networking and storage setup, and removes the need for user-managed node groups or Auto Scaling Groups.
tsconst cluster = new eks.Cluster(this, 'AutoModeCluster', { version: eks.KubernetesVersion.V1_36, // Auto Mode is enabled by default });Managed Node Groups – The semi-managed capacity mode. EKS provisions and manages EC2 nodes on your behalf but you configure the instance types, scaling ranges, and update strategy. AWS handles node health, draining, and rolling updates while you retain control over scaling and cost optimization.
You can also define Fargate Profiles that determine which pods or namespaces run on Fargate infrastructure.
tsconst cluster = new eks.Cluster(this, 'ManagedNodeCluster', { version: eks.KubernetesVersion.V1_36, defaultCapacityType: eks.DefaultCapacityType.NODEGROUP, }); // Add a Fargate Profile for specific workloads (e.g., default namespace) cluster.addFargateProfile('FargateProfile', { selectors: [ { namespace: 'default' }, // Run pods in 'default' on Fargate ], });Fargate Mode – The Fargate capacity mode. EKS runs your pods directly on AWS Fargate without provisioning EC2 nodes.
tsconst cluster = new eks.FargateCluster(this, 'FargateCluster', { version: eks.KubernetesVersion.V1_36, });Self-Managed Nodes – The fully manual capacity mode. You create and manage EC2 instances (via an Auto Scaling Group) and connect them to the cluster manually. This provides maximum flexibility for custom AMIs or configurations but also the highest operational overhead.
tsconst cluster = new eks.Cluster(this, 'SelfManagedCluster', { version: eks.KubernetesVersion.V1_36, }); // Add self-managed Auto Scaling Group cluster.addAutoScalingGroupCapacity('self-managed-asg', { instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MEDIUM), minCapacity: 1, maxCapacity: 5, });Kubectl Handler (Optional) – A Lambda-backed custom resource created by the AWS CDK to execute
kubectlcommands (likeapplyorpatch) during deployment. Regardless of the capacity mode, this handler may still be created to apply Kubernetes manifests as part of CDK provisioning.
Provisioning cluster
Creating a new cluster is done using the Cluster constructs. The only required property is the kubernetes version.
new eks.Cluster(this, 'HelloEKS', {
version: eks.KubernetesVersion.V1_36,
});
eks.Cluster(self, "HelloEKS",
version=eks.KubernetesVersion.V1_36
)
Cluster.Builder.create(this, "HelloEKS")
.version(KubernetesVersion.V1_36)
.build();
new Cluster(this, "HelloEKS", new ClusterProps {
Version = KubernetesVersion.V1_36
});
eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
})
You can also use FargateCluster to provision a cluster that uses only fargate workers.
new eks.FargateCluster(this, 'HelloEKS', {
version: eks.KubernetesVersion.V1_36,
});
eks.FargateCluster(self, "HelloEKS",
version=eks.KubernetesVersion.V1_36
)
FargateCluster.Builder.create(this, "HelloEKS")
.version(KubernetesVersion.V1_36)
.build();
new FargateCluster(this, "HelloEKS", new FargateClusterProps {
Version = KubernetesVersion.V1_36
});
eks.NewFargateCluster(this, jsii.String("HelloEKS"), &FargateClusterProps{
Version: eks.KubernetesVersion_V1_36(),
})
Note: Unlike the previous EKS cluster, Kubectl Handler will not be created by default. It will only be deployed when kubectlProviderOptions property is used.
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';
new eks.Cluster(this, 'hello-eks', {
version: eks.KubernetesVersion.V1_36,
kubectlProviderOptions: {
kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
}
});
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer
eks.Cluster(self, "hello-eks",
version=eks.KubernetesVersion.V1_36,
kubectl_provider_options=eks.KubectlProviderOptions(
kubectl_layer=KubectlV36Layer(self, "kubectl")
)
)
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;
Cluster.Builder.create(this, "hello-eks")
.version(KubernetesVersion.V1_36)
.kubectlProviderOptions(KubectlProviderOptions.builder()
.kubectlLayer(new KubectlV36Layer(this, "kubectl"))
.build())
.build();
using Amazon.CDK.LambdaLayer.KubectlV36;
new Cluster(this, "hello-eks", new ClusterProps {
Version = KubernetesVersion.V1_36,
KubectlProviderOptions = new KubectlProviderOptions {
KubectlLayer = new KubectlV36Layer(this, "kubectl")
}
});
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"
eks.NewCluster(this, jsii.String("hello-eks"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
KubectlProviderOptions: &KubectlProviderOptions{
KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
},
})
EKS Auto Mode
Amazon EKS Auto Mode extends AWS management of Kubernetes clusters beyond the cluster itself, allowing AWS to set up and manage the infrastructure that enables the smooth operation of your workloads.
Using Auto Mode
While aws-eks uses DefaultCapacityType.NODEGROUP by default, aws-eks-v2 uses DefaultCapacityType.AUTOMODE as the default capacity type.
Auto Mode is enabled by default when creating a new cluster without specifying any capacity-related properties:
// Create EKS cluster with Auto Mode implicitly enabled
const cluster = new eks.Cluster(this, 'EksAutoCluster', {
version: eks.KubernetesVersion.V1_36,
});
# Create EKS cluster with Auto Mode implicitly enabled
cluster = eks.Cluster(self, "EksAutoCluster",
version=eks.KubernetesVersion.V1_36
)
// Create EKS cluster with Auto Mode implicitly enabled
Cluster cluster = Cluster.Builder.create(this, "EksAutoCluster")
.version(KubernetesVersion.V1_36)
.build();
// Create EKS cluster with Auto Mode implicitly enabled
var cluster = new Cluster(this, "EksAutoCluster", new ClusterProps {
Version = KubernetesVersion.V1_36
});
// Create EKS cluster with Auto Mode implicitly enabled
cluster := eks.NewCluster(this, jsii.String("EksAutoCluster"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
})
You can also explicitly enable Auto Mode using defaultCapacityType:
// Create EKS cluster with Auto Mode explicitly enabled
const cluster = new eks.Cluster(this, 'EksAutoCluster', {
version: eks.KubernetesVersion.V1_36,
defaultCapacityType: eks.DefaultCapacityType.AUTOMODE,
});
# Create EKS cluster with Auto Mode explicitly enabled
cluster = eks.Cluster(self, "EksAutoCluster",
version=eks.KubernetesVersion.V1_36,
default_capacity_type=eks.DefaultCapacityType.AUTOMODE
)
// Create EKS cluster with Auto Mode explicitly enabled
Cluster cluster = Cluster.Builder.create(this, "EksAutoCluster")
.version(KubernetesVersion.V1_36)
.defaultCapacityType(DefaultCapacityType.AUTOMODE)
.build();
// Create EKS cluster with Auto Mode explicitly enabled
var cluster = new Cluster(this, "EksAutoCluster", new ClusterProps {
Version = KubernetesVersion.V1_36,
DefaultCapacityType = DefaultCapacityType.AUTOMODE
});
// Create EKS cluster with Auto Mode explicitly enabled
cluster := eks.NewCluster(this, jsii.String("EksAutoCluster"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
DefaultCapacityType: eks.DefaultCapacityType_AUTOMODE,
})
Node Pools
When Auto Mode is enabled, the cluster comes with two default node pools:
system: For running system components and add-onsgeneral-purpose: For running your application workloads
These node pools are managed automatically by EKS. You can configure which node pools to enable through the compute property:
const cluster = new eks.Cluster(this, 'EksAutoCluster', {
version: eks.KubernetesVersion.V1_36,
defaultCapacityType: eks.DefaultCapacityType.AUTOMODE,
compute: {
nodePools: ['system', 'general-purpose'],
},
});
cluster = eks.Cluster(self, "EksAutoCluster",
version=eks.KubernetesVersion.V1_36,
default_capacity_type=eks.DefaultCapacityType.AUTOMODE,
compute=eks.ComputeConfig(
node_pools=["system", "general-purpose"]
)
)
Cluster cluster = Cluster.Builder.create(this, "EksAutoCluster")
.version(KubernetesVersion.V1_36)
.defaultCapacityType(DefaultCapacityType.AUTOMODE)
.compute(ComputeConfig.builder()
.nodePools(List.of("system", "general-purpose"))
.build())
.build();
var cluster = new Cluster(this, "EksAutoCluster", new ClusterProps {
Version = KubernetesVersion.V1_36,
DefaultCapacityType = DefaultCapacityType.AUTOMODE,
Compute = new ComputeConfig {
NodePools = new [] { "system", "general-purpose" }
}
});
cluster := eks.NewCluster(this, jsii.String("EksAutoCluster"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
DefaultCapacityType: eks.DefaultCapacityType_AUTOMODE,
Compute: &ComputeConfig{
NodePools: []*string{
jsii.String("system"),
jsii.String("general-purpose"),
},
},
})
For more information, see Create a Node Pool for EKS Auto Mode.
Disabling Default Node Pools
You can disable the default node pools entirely by setting an empty array for nodePools. This is useful when you want to use Auto Mode features but manage your compute resources separately:
const cluster = new eks.Cluster(this, 'EksAutoCluster', {
version: eks.KubernetesVersion.V1_36,
defaultCapacityType: eks.DefaultCapacityType.AUTOMODE,
compute: {
nodePools: [], // Disable default node pools
},
});
cluster = eks.Cluster(self, "EksAutoCluster",
version=eks.KubernetesVersion.V1_36,
default_capacity_type=eks.DefaultCapacityType.AUTOMODE,
compute=eks.ComputeConfig(
node_pools=[]
)
)
Cluster cluster = Cluster.Builder.create(this, "EksAutoCluster")
.version(KubernetesVersion.V1_36)
.defaultCapacityType(DefaultCapacityType.AUTOMODE)
.compute(ComputeConfig.builder()
.nodePools(List.of())
.build())
.build();
var cluster = new Cluster(this, "EksAutoCluster", new ClusterProps {
Version = KubernetesVersion.V1_36,
DefaultCapacityType = DefaultCapacityType.AUTOMODE,
Compute = new ComputeConfig {
NodePools = new [] { }
}
});
cluster := eks.NewCluster(this, jsii.String("EksAutoCluster"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
DefaultCapacityType: eks.DefaultCapacityType_AUTOMODE,
Compute: &ComputeConfig{
NodePools: []*string{
},
},
})
When node pools are disabled this way, no IAM role will be created for the node pools, preventing deployment failures that would otherwise occur when a role is created without any node pools.
Node Groups as the default capacity type
If you prefer to manage your own node groups instead of using Auto Mode, you can use the traditional node group approach by specifying defaultCapacityType as NODEGROUP:
// Create EKS cluster with traditional managed node group
const cluster = new eks.Cluster(this, 'EksCluster', {
version: eks.KubernetesVersion.V1_36,
defaultCapacityType: eks.DefaultCapacityType.NODEGROUP,
defaultCapacity: 3, // Number of instances
defaultCapacityInstance: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.LARGE),
});
# Create EKS cluster with traditional managed node group
cluster = eks.Cluster(self, "EksCluster",
version=eks.KubernetesVersion.V1_36,
default_capacity_type=eks.DefaultCapacityType.NODEGROUP,
default_capacity=3, # Number of instances
default_capacity_instance=ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.LARGE)
)
// Create EKS cluster with traditional managed node group
Cluster cluster = Cluster.Builder.create(this, "EksCluster")
.version(KubernetesVersion.V1_36)
.defaultCapacityType(DefaultCapacityType.NODEGROUP)
.defaultCapacity(3) // Number of instances
.defaultCapacityInstance(InstanceType.of(InstanceClass.T3, InstanceSize.LARGE))
.build();
// Create EKS cluster with traditional managed node group
var cluster = new Cluster(this, "EksCluster", new ClusterProps {
Version = KubernetesVersion.V1_36,
DefaultCapacityType = DefaultCapacityType.NODEGROUP,
DefaultCapacity = 3, // Number of instances
DefaultCapacityInstance = InstanceType.Of(InstanceClass.T3, InstanceSize.LARGE)
});
// Create EKS cluster with traditional managed node group
cluster := eks.NewCluster(this, jsii.String("EksCluster"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
DefaultCapacityType: eks.DefaultCapacityType_NODEGROUP,
DefaultCapacity: jsii.Number(3),
// Number of instances
DefaultCapacityInstance: ec2.InstanceType_Of(ec2.InstanceClass_T3, ec2.InstanceSize_LARGE),
})
You can also create a cluster with no initial capacity and add node groups later:
const cluster = new eks.Cluster(this, 'EksCluster', {
version: eks.KubernetesVersion.V1_36,
defaultCapacityType: eks.DefaultCapacityType.NODEGROUP,
defaultCapacity: 0,
});
// Add node groups as needed
cluster.addNodegroupCapacity('custom-node-group', {
minSize: 1,
maxSize: 3,
instanceTypes: [ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.LARGE)],
});
cluster = eks.Cluster(self, "EksCluster",
version=eks.KubernetesVersion.V1_36,
default_capacity_type=eks.DefaultCapacityType.NODEGROUP,
default_capacity=0
)
# Add node groups as needed
cluster.add_nodegroup_capacity("custom-node-group",
min_size=1,
max_size=3,
instance_types=[ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.LARGE)]
)
Cluster cluster = Cluster.Builder.create(this, "EksCluster")
.version(KubernetesVersion.V1_36)
.defaultCapacityType(DefaultCapacityType.NODEGROUP)
.defaultCapacity(0)
.build();
// Add node groups as needed
cluster.addNodegroupCapacity("custom-node-group", NodegroupOptions.builder()
.minSize(1)
.maxSize(3)
.instanceTypes(List.of(InstanceType.of(InstanceClass.T3, InstanceSize.LARGE)))
.build());
var cluster = new Cluster(this, "EksCluster", new ClusterProps {
Version = KubernetesVersion.V1_36,
DefaultCapacityType = DefaultCapacityType.NODEGROUP,
DefaultCapacity = 0
});
// Add node groups as needed
cluster.AddNodegroupCapacity("custom-node-group", new NodegroupOptions {
MinSize = 1,
MaxSize = 3,
InstanceTypes = new [] { InstanceType.Of(InstanceClass.T3, InstanceSize.LARGE) }
});
cluster := eks.NewCluster(this, jsii.String("EksCluster"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
DefaultCapacityType: eks.DefaultCapacityType_NODEGROUP,
DefaultCapacity: jsii.Number(0),
})
// Add node groups as needed
cluster.AddNodegroupCapacity(jsii.String("custom-node-group"), &NodegroupOptions{
MinSize: jsii.Number(1),
MaxSize: jsii.Number(3),
InstanceTypes: []InstanceType{
ec2.InstanceType_Of(ec2.InstanceClass_T3, ec2.InstanceSize_LARGE),
},
})
Read Managed node groups for more information on how to add node groups to the cluster.
Mixed with Auto Mode and Node Groups
You can combine Auto Mode with traditional node groups for specific workload requirements:
const cluster = new eks.Cluster(this, 'Cluster', {
version: eks.KubernetesVersion.V1_36,
defaultCapacityType: eks.DefaultCapacityType.AUTOMODE,
compute: {
nodePools: ['system', 'general-purpose'],
},
});
// Add specialized node group for specific workloads
cluster.addNodegroupCapacity('specialized-workload', {
minSize: 1,
maxSize: 3,
instanceTypes: [ec2.InstanceType.of(ec2.InstanceClass.C5, ec2.InstanceSize.XLARGE)],
labels: {
workload: 'specialized',
},
});
cluster = eks.Cluster(self, "Cluster",
version=eks.KubernetesVersion.V1_36,
default_capacity_type=eks.DefaultCapacityType.AUTOMODE,
compute=eks.ComputeConfig(
node_pools=["system", "general-purpose"]
)
)
# Add specialized node group for specific workloads
cluster.add_nodegroup_capacity("specialized-workload",
min_size=1,
max_size=3,
instance_types=[ec2.InstanceType.of(ec2.InstanceClass.C5, ec2.InstanceSize.XLARGE)],
labels={
"workload": "specialized"
}
)
Cluster cluster = Cluster.Builder.create(this, "Cluster")
.version(KubernetesVersion.V1_36)
.defaultCapacityType(DefaultCapacityType.AUTOMODE)
.compute(ComputeConfig.builder()
.nodePools(List.of("system", "general-purpose"))
.build())
.build();
// Add specialized node group for specific workloads
cluster.addNodegroupCapacity("specialized-workload", NodegroupOptions.builder()
.minSize(1)
.maxSize(3)
.instanceTypes(List.of(InstanceType.of(InstanceClass.C5, InstanceSize.XLARGE)))
.labels(Map.of(
"workload", "specialized"))
.build());
var cluster = new Cluster(this, "Cluster", new ClusterProps {
Version = KubernetesVersion.V1_36,
DefaultCapacityType = DefaultCapacityType.AUTOMODE,
Compute = new ComputeConfig {
NodePools = new [] { "system", "general-purpose" }
}
});
// Add specialized node group for specific workloads
cluster.AddNodegroupCapacity("specialized-workload", new NodegroupOptions {
MinSize = 1,
MaxSize = 3,
InstanceTypes = new [] { InstanceType.Of(InstanceClass.C5, InstanceSize.XLARGE) },
Labels = new Dictionary<string, string> {
{ "workload", "specialized" }
}
});
cluster := eks.NewCluster(this, jsii.String("Cluster"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
DefaultCapacityType: eks.DefaultCapacityType_AUTOMODE,
Compute: &ComputeConfig{
NodePools: []*string{
jsii.String("system"),
jsii.String("general-purpose"),
},
},
})
// Add specialized node group for specific workloads
cluster.AddNodegroupCapacity(jsii.String("specialized-workload"), &NodegroupOptions{
MinSize: jsii.Number(1),
MaxSize: jsii.Number(3),
InstanceTypes: []InstanceType{
ec2.InstanceType_Of(ec2.InstanceClass_C5, ec2.InstanceSize_XLARGE),
},
Labels: map[string]*string{
"workload": jsii.String("specialized"),
},
})
Important Notes
Auto Mode and traditional capacity management are mutually exclusive at the default capacity level. You cannot opt in to Auto Mode and specify
defaultCapacityordefaultCapacityInstance.When Auto Mode is enabled:
- The cluster will automatically manage compute resources
- Node pools cannot be modified, only enabled or disabled
- EKS will handle scaling and management of the node pools
Auto Mode requires specific IAM permissions. The construct will automatically attach the required managed policies.
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.
The scaling tier is configured through the controlPlaneScalingTier property using the ControlPlaneScalingTier enum-like class.
const cluster = new eks.Cluster(this, 'HighPerformanceCluster', {
version: eks.KubernetesVersion.V1_36,
controlPlaneScalingTier: eks.ControlPlaneScalingTier.TIER_XL,
});
cluster = eks.Cluster(self, "HighPerformanceCluster",
version=eks.KubernetesVersion.V1_36,
control_plane_scaling_tier=eks.ControlPlaneScalingTier.TIER_XL
)
Cluster cluster = Cluster.Builder.create(this, "HighPerformanceCluster")
.version(KubernetesVersion.V1_36)
.controlPlaneScalingTier(ControlPlaneScalingTier.TIER_XL)
.build();
var cluster = new Cluster(this, "HighPerformanceCluster", new ClusterProps {
Version = KubernetesVersion.V1_36,
ControlPlaneScalingTier = ControlPlaneScalingTier.TIER_XL
});
cluster := eks.NewCluster(this, jsii.String("HighPerformanceCluster"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
ControlPlaneScalingTier: eks.ControlPlaneScalingTier_TIER_XL(),
})
Available scaling tiers:
STANDARD- Standard control plane (default, no additional cost)TIER_XL- Extra-large provisioned tierTIER_2XL- 2x extra-large provisioned tierTIER_4XL- 4x extra-large provisioned tierTIER_8XL- 8x extra-large provisioned tier
For a tier that is not yet available as a named member, use the ControlPlaneScalingTier.of(...) escape hatch, for example eks.ControlPlaneScalingTier.of('tier-16xl').
If controlPlaneScalingTier is omitted, the EKS service applies its default (standard) control plane.
For more details visit Amazon EKS Provisioned Control Plane.
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.
By default, when using DefaultCapacityType.NODEGROUP, 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).
new eks.Cluster(this, 'HelloEKS', {
version: eks.KubernetesVersion.V1_36,
defaultCapacityType: eks.DefaultCapacityType.NODEGROUP,
});
eks.Cluster(self, "HelloEKS",
version=eks.KubernetesVersion.V1_36,
default_capacity_type=eks.DefaultCapacityType.NODEGROUP
)
Cluster.Builder.create(this, "HelloEKS")
.version(KubernetesVersion.V1_36)
.defaultCapacityType(DefaultCapacityType.NODEGROUP)
.build();
new Cluster(this, "HelloEKS", new ClusterProps {
Version = KubernetesVersion.V1_36,
DefaultCapacityType = DefaultCapacityType.NODEGROUP
});
eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
DefaultCapacityType: eks.DefaultCapacityType_NODEGROUP,
})
At cluster instantiation time, you can customize the number of instances and their type:
new eks.Cluster(this, 'HelloEKS', {
version: eks.KubernetesVersion.V1_36,
defaultCapacityType: eks.DefaultCapacityType.NODEGROUP,
defaultCapacity: 5,
defaultCapacityInstance: ec2.InstanceType.of(ec2.InstanceClass.M5, ec2.InstanceSize.SMALL),
});
eks.Cluster(self, "HelloEKS",
version=eks.KubernetesVersion.V1_36,
default_capacity_type=eks.DefaultCapacityType.NODEGROUP,
default_capacity=5,
default_capacity_instance=ec2.InstanceType.of(ec2.InstanceClass.M5, ec2.InstanceSize.SMALL)
)
Cluster.Builder.create(this, "HelloEKS")
.version(KubernetesVersion.V1_36)
.defaultCapacityType(DefaultCapacityType.NODEGROUP)
.defaultCapacity(5)
.defaultCapacityInstance(InstanceType.of(InstanceClass.M5, InstanceSize.SMALL))
.build();
new Cluster(this, "HelloEKS", new ClusterProps {
Version = KubernetesVersion.V1_36,
DefaultCapacityType = DefaultCapacityType.NODEGROUP,
DefaultCapacity = 5,
DefaultCapacityInstance = InstanceType.Of(InstanceClass.M5, InstanceSize.SMALL)
});
eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
DefaultCapacityType: eks.DefaultCapacityType_NODEGROUP,
DefaultCapacity: jsii.Number(5),
DefaultCapacityInstance: ec2.InstanceType_Of(ec2.InstanceClass_M5, ec2.InstanceSize_SMALL),
})
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:
const cluster = new eks.Cluster(this, 'HelloEKS', {
version: eks.KubernetesVersion.V1_36,
defaultCapacityType: eks.DefaultCapacityType.NODEGROUP,
defaultCapacity: 0,
});
cluster.addNodegroupCapacity('custom-node-group', {
instanceTypes: [new ec2.InstanceType('m5.large')],
minSize: 4,
diskSize: 100,
});
cluster = eks.Cluster(self, "HelloEKS",
version=eks.KubernetesVersion.V1_36,
default_capacity_type=eks.DefaultCapacityType.NODEGROUP,
default_capacity=0
)
cluster.add_nodegroup_capacity("custom-node-group",
instance_types=[ec2.InstanceType("m5.large")],
min_size=4,
disk_size=100
)
Cluster cluster = Cluster.Builder.create(this, "HelloEKS")
.version(KubernetesVersion.V1_36)
.defaultCapacityType(DefaultCapacityType.NODEGROUP)
.defaultCapacity(0)
.build();
cluster.addNodegroupCapacity("custom-node-group", NodegroupOptions.builder()
.instanceTypes(List.of(new InstanceType("m5.large")))
.minSize(4)
.diskSize(100)
.build());
var cluster = new Cluster(this, "HelloEKS", new ClusterProps {
Version = KubernetesVersion.V1_36,
DefaultCapacityType = DefaultCapacityType.NODEGROUP,
DefaultCapacity = 0
});
cluster.AddNodegroupCapacity("custom-node-group", new NodegroupOptions {
InstanceTypes = new [] { new InstanceType("m5.large") },
MinSize = 4,
DiskSize = 100
});
cluster := eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
DefaultCapacityType: eks.DefaultCapacityType_NODEGROUP,
DefaultCapacity: jsii.Number(0),
})
cluster.AddNodegroupCapacity(jsii.String("custom-node-group"), &NodegroupOptions{
InstanceTypes: []InstanceType{
ec2.NewInstanceType(jsii.String("m5.large")),
},
MinSize: jsii.Number(4),
DiskSize: jsii.Number(100),
})
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:
{
"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.
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,
});
# 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
)
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());
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
});
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.
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:
declare const cluster: eks.Cluster;
cluster.addFargateProfile('MyProfile', {
selectors: [ { namespace: 'default' } ],
});
# cluster: eks.Cluster
cluster.add_fargate_profile("MyProfile",
selectors=[eks.Selector(namespace="default")]
)
Cluster cluster;
cluster.addFargateProfile("MyProfile", FargateProfileOptions.builder()
.selectors(List.of(Selector.builder().namespace("default").build()))
.build());
Cluster cluster;
cluster.AddFargateProfile("MyProfile", new FargateProfileOptions {
Selectors = new [] { new Selector { Namespace = "default" } }
});
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:
declare const cluster: eks.Cluster;
new eks.FargateProfile(this, 'MyProfile', {
cluster,
selectors: [ { namespace: 'default' } ],
});
# cluster: eks.Cluster
eks.FargateProfile(self, "MyProfile",
cluster=cluster,
selectors=[eks.Selector(namespace="default")]
)
Cluster cluster;
FargateProfile.Builder.create(this, "MyProfile")
.cluster(cluster)
.selectors(List.of(Selector.builder().namespace("default").build()))
.build();
Cluster cluster;
new FargateProfile(this, "MyProfile", new FargateProfileProps {
Cluster = cluster,
Selectors = new [] { new Selector { Namespace = "default" } }
});
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.
const cluster = new eks.FargateCluster(this, 'MyCluster', {
version: eks.KubernetesVersion.V1_36,
});
cluster = eks.FargateCluster(self, "MyCluster",
version=eks.KubernetesVersion.V1_36
)
FargateCluster cluster = FargateCluster.Builder.create(this, "MyCluster")
.version(KubernetesVersion.V1_36)
.build();
var cluster = new FargateCluster(this, "MyCluster", new FargateClusterProps {
Version = KubernetesVersion.V1_36
});
cluster := eks.NewFargateCluster(this, jsii.String("MyCluster"), &FargateClusterProps{
Version: eks.KubernetesVersion_V1_36(),
})
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 capacity
Self-managed capacity gives you the most control over your worker nodes by allowing you to create and manage your own EC2 Auto Scaling Groups. This approach provides maximum flexibility for custom AMIs, instance configurations, and scaling policies, but requires more operational overhead.
You can add self-managed capacity to any cluster using the addAutoScalingGroupCapacity method:
const cluster = new eks.Cluster(this, 'Cluster', {
version: eks.KubernetesVersion.V1_36,
});
cluster.addAutoScalingGroupCapacity('self-managed-nodes', {
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MEDIUM),
minCapacity: 1,
maxCapacity: 10,
desiredCapacity: 3,
});
cluster = eks.Cluster(self, "Cluster",
version=eks.KubernetesVersion.V1_36
)
cluster.add_auto_scaling_group_capacity("self-managed-nodes",
instance_type=ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MEDIUM),
min_capacity=1,
max_capacity=10,
desired_capacity=3
)
Cluster cluster = Cluster.Builder.create(this, "Cluster")
.version(KubernetesVersion.V1_36)
.build();
cluster.addAutoScalingGroupCapacity("self-managed-nodes", AutoScalingGroupCapacityOptions.builder()
.instanceType(InstanceType.of(InstanceClass.T3, InstanceSize.MEDIUM))
.minCapacity(1)
.maxCapacity(10)
.desiredCapacity(3)
.build());
var cluster = new Cluster(this, "Cluster", new ClusterProps {
Version = KubernetesVersion.V1_36
});
cluster.AddAutoScalingGroupCapacity("self-managed-nodes", new AutoScalingGroupCapacityOptions {
InstanceType = InstanceType.Of(InstanceClass.T3, InstanceSize.MEDIUM),
MinCapacity = 1,
MaxCapacity = 10,
DesiredCapacity = 3
});
cluster := eks.NewCluster(this, jsii.String("Cluster"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
})
cluster.AddAutoScalingGroupCapacity(jsii.String("self-managed-nodes"), &AutoScalingGroupCapacityOptions{
InstanceType: ec2.InstanceType_Of(ec2.InstanceClass_T3, ec2.InstanceSize_MEDIUM),
MinCapacity: jsii.Number(1),
MaxCapacity: jsii.Number(10),
DesiredCapacity: jsii.Number(3),
})
You can specify custom subnets for the Auto Scaling Group:
declare const vpc: ec2.Vpc;
declare const cluster: eks.Cluster;
cluster.addAutoScalingGroupCapacity('custom-subnet-nodes', {
vpcSubnets: { subnets: vpc.privateSubnets },
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MEDIUM),
minCapacity: 2,
});
# vpc: ec2.Vpc
# cluster: eks.Cluster
cluster.add_auto_scaling_group_capacity("custom-subnet-nodes",
vpc_subnets=ec2.SubnetSelection(subnets=vpc.private_subnets),
instance_type=ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MEDIUM),
min_capacity=2
)
Vpc vpc;
Cluster cluster;
cluster.addAutoScalingGroupCapacity("custom-subnet-nodes", AutoScalingGroupCapacityOptions.builder()
.vpcSubnets(SubnetSelection.builder().subnets(vpc.getPrivateSubnets()).build())
.instanceType(InstanceType.of(InstanceClass.T3, InstanceSize.MEDIUM))
.minCapacity(2)
.build());
Vpc vpc;
Cluster cluster;
cluster.AddAutoScalingGroupCapacity("custom-subnet-nodes", new AutoScalingGroupCapacityOptions {
VpcSubnets = new SubnetSelection { Subnets = vpc.PrivateSubnets },
InstanceType = InstanceType.Of(InstanceClass.T3, InstanceSize.MEDIUM),
MinCapacity = 2
});
var vpc Vpc
var cluster Cluster
cluster.AddAutoScalingGroupCapacity(jsii.String("custom-subnet-nodes"), &AutoScalingGroupCapacityOptions{
VpcSubnets: &SubnetSelection{
Subnets: vpc.PrivateSubnets,
},
InstanceType: ec2.InstanceType_Of(ec2.InstanceClass_T3, ec2.InstanceSize_MEDIUM),
MinCapacity: jsii.Number(2),
})
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)
You can configure the cluster endpoint access by using the endpointAccess property:
const cluster = new eks.Cluster(this, 'hello-eks', {
version: eks.KubernetesVersion.V1_36,
endpointAccess: eks.EndpointAccess.PRIVATE, // No access outside of your VPC.
});
cluster = eks.Cluster(self, "hello-eks",
version=eks.KubernetesVersion.V1_36,
endpoint_access=eks.EndpointAccess.PRIVATE
)
Cluster cluster = Cluster.Builder.create(this, "hello-eks")
.version(KubernetesVersion.V1_36)
.endpointAccess(EndpointAccess.PRIVATE)
.build();
var cluster = new Cluster(this, "hello-eks", new ClusterProps {
Version = KubernetesVersion.V1_36,
EndpointAccess = EndpointAccess.PRIVATE
});
cluster := eks.NewCluster(this, jsii.String("hello-eks"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
EndpointAccess: eks.EndpointAccess_PRIVATE(),
})
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:
new eks.Cluster(this, 'HelloEKS', {
version: eks.KubernetesVersion.V1_36,
albController: {
version: eks.AlbControllerVersion.V3_2_2,
},
});
eks.Cluster(self, "HelloEKS",
version=eks.KubernetesVersion.V1_36,
alb_controller=eks.AlbControllerOptions(
version=eks.AlbControllerVersion.V3_2_2
)
)
Cluster.Builder.create(this, "HelloEKS")
.version(KubernetesVersion.V1_36)
.albController(AlbControllerOptions.builder()
.version(AlbControllerVersion.V3_2_2)
.build())
.build();
new Cluster(this, "HelloEKS", new ClusterProps {
Version = KubernetesVersion.V1_36,
AlbController = new AlbControllerOptions {
Version = AlbControllerVersion.V3_2_2
}
});
eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
AlbController: &AlbControllerOptions{
Version: eks.AlbControllerVersion_V3_2_2(),
},
})
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:
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
}
},
});
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={
"enable_wafv2": False
}
)
)
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(Map.of(
"enableWafv2", false))
.build())
.build();
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 Dictionary<string, object> {
{ "enableWafv2", false }
}
}
});
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: map[string]interface{}{
"enableWafv2": jsii.Boolean(false),
},
},
})
To overwrite an existing ALB controller service account, use the overwriteServiceAccount property:
new eks.Cluster(this, 'HelloEKS', {
version: eks.KubernetesVersion.V1_36,
albController: {
version: eks.AlbControllerVersion.V3_2_2,
overwriteServiceAccount: true,
},
});
eks.Cluster(self, "HelloEKS",
version=eks.KubernetesVersion.V1_36,
alb_controller=eks.AlbControllerOptions(
version=eks.AlbControllerVersion.V3_2_2,
overwrite_service_account=True
)
)
Cluster.Builder.create(this, "HelloEKS")
.version(KubernetesVersion.V1_36)
.albController(AlbControllerOptions.builder()
.version(AlbControllerVersion.V3_2_2)
.overwriteServiceAccount(true)
.build())
.build();
new Cluster(this, "HelloEKS", new ClusterProps {
Version = KubernetesVersion.V1_36,
AlbController = new AlbControllerOptions {
Version = AlbControllerVersion.V3_2_2,
OverwriteServiceAccount = true
}
});
eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
AlbController: &AlbControllerOptions{
Version: eks.AlbControllerVersion_V3_2_2(),
OverwriteServiceAccount: jsii.Boolean(true),
},
})
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:
❯ 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:
declare const cluster: eks.Cluster;
const manifest = cluster.addManifest('manifest', {/* ... */});
if (cluster.albController) {
manifest.node.addDependency(cluster.albController);
}
# cluster: eks.Cluster
manifest = cluster.add_manifest("manifest", {})
if cluster.alb_controller:
manifest.node.add_dependency(cluster.alb_controller)
Cluster cluster;
KubernetesManifest manifest = cluster.addManifest("manifest", Map.of());
if (cluster.getAlbController()) {
manifest.node.addDependency(cluster.getAlbController());
}
Cluster cluster;
var manifest = cluster.AddManifest("manifest", new Dictionary<string, object> { });
if (cluster.AlbController)
{
manifest.Node.AddDependency(cluster.AlbController);
}
var cluster Cluster
manifest := cluster.addManifest(jsii.String("manifest"), map[string]interface{}{
})
if cluster.AlbController {
manifest.Node.AddDependency(cluster.AlbController)
}
You can specify the VPC of the cluster using the vpc and vpcSubnets properties:
declare const vpc: ec2.Vpc;
new eks.Cluster(this, 'HelloEKS', {
version: eks.KubernetesVersion.V1_36,
vpc,
vpcSubnets: [{ subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }],
});
# 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)]
)
Vpc vpc;
Cluster.Builder.create(this, "HelloEKS")
.version(KubernetesVersion.V1_36)
.vpc(vpc)
.vpcSubnets(List.of(SubnetSelection.builder().subnetType(SubnetType.PRIVATE_WITH_EGRESS).build()))
.build();
Vpc vpc;
new Cluster(this, "HelloEKS", new ClusterProps {
Version = KubernetesVersion.V1_36,
Vpc = vpc,
VpcSubnets = new [] { new SubnetSelection { SubnetType = SubnetType.PRIVATE_WITH_EGRESS } }
});
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,
},
},
})
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:
declare const vpc: ec2.Vpc;
declare const cluster: eks.Cluster;
cluster.addAutoScalingGroupCapacity('nodes', {
vpcSubnets: { subnets: vpc.privateSubnets },
instanceType: new ec2.InstanceType('t2.medium'),
});
# 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")
)
Vpc vpc;
Cluster cluster;
cluster.addAutoScalingGroupCapacity("nodes", AutoScalingGroupCapacityOptions.builder()
.vpcSubnets(SubnetSelection.builder().subnets(vpc.getPrivateSubnets()).build())
.instanceType(new InstanceType("t2.medium"))
.build());
Vpc vpc;
Cluster cluster;
cluster.AddAutoScalingGroupCapacity("nodes", new AutoScalingGroupCapacityOptions {
VpcSubnets = new SubnetSelection { Subnets = vpc.PrivateSubnets },
InstanceType = new InstanceType("t2.medium")
});
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 is an additional components you might want to provision within the VPC.
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.
Kubectl Support
You can choose to have CDK create a Kubectl Handler - a Python Lambda Function to apply k8s manifests using kubectl apply. This handler will not be created by default.
To create a Kubectl Handler, use kubectlProviderOptions when creating the cluster. kubectlLayer is the only required property in kubectlProviderOptions.
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';
new eks.Cluster(this, 'hello-eks', {
version: eks.KubernetesVersion.V1_36,
kubectlProviderOptions: {
kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
}
});
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer
eks.Cluster(self, "hello-eks",
version=eks.KubernetesVersion.V1_36,
kubectl_provider_options=eks.KubectlProviderOptions(
kubectl_layer=KubectlV36Layer(self, "kubectl")
)
)
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;
Cluster.Builder.create(this, "hello-eks")
.version(KubernetesVersion.V1_36)
.kubectlProviderOptions(KubectlProviderOptions.builder()
.kubectlLayer(new KubectlV36Layer(this, "kubectl"))
.build())
.build();
using Amazon.CDK.LambdaLayer.KubectlV36;
new Cluster(this, "hello-eks", new ClusterProps {
Version = KubernetesVersion.V1_36,
KubectlProviderOptions = new KubectlProviderOptions {
KubectlLayer = new KubectlV36Layer(this, "kubectl")
}
});
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"
eks.NewCluster(this, jsii.String("hello-eks"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
KubectlProviderOptions: &KubectlProviderOptions{
KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
},
})
Kubectl Handler created along with the cluster will be granted admin permissions to the cluster.
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:
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', {
serviceToken: functionArn,
role: handlerRole,
});
const cluster = eks.Cluster.fromClusterAttributes(this, 'Cluster', {
clusterName: 'cluster',
kubectlProvider,
});
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",
service_token=function_arn,
role=handler_role
)
cluster = eks.Cluster.from_cluster_attributes(self, "Cluster",
cluster_name="cluster",
kubectl_provider=kubectl_provider
)
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()
.serviceToken(functionArn)
.role(handlerRole)
.build());
ICluster cluster = Cluster.fromClusterAttributes(this, "Cluster", ClusterAttributes.builder()
.clusterName("cluster")
.kubectlProvider(kubectlProvider)
.build());
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 {
ServiceToken = functionArn,
Role = handlerRole
});
var cluster = Cluster.FromClusterAttributes(this, "Cluster", new ClusterAttributes {
ClusterName = "cluster",
KubectlProvider = kubectlProvider
});
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{
ServiceToken: functionArn,
Role: 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:
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';
const cluster = new eks.Cluster(this, 'hello-eks', {
version: eks.KubernetesVersion.V1_36,
kubectlProviderOptions: {
kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
environment: {
'http_proxy': 'http://proxy.myproxy.com',
},
}
});
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer
cluster = eks.Cluster(self, "hello-eks",
version=eks.KubernetesVersion.V1_36,
kubectl_provider_options=eks.KubectlProviderOptions(
kubectl_layer=KubectlV36Layer(self, "kubectl"),
environment={
"http_proxy": "http://proxy.myproxy.com"
}
)
)
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;
Cluster cluster = Cluster.Builder.create(this, "hello-eks")
.version(KubernetesVersion.V1_36)
.kubectlProviderOptions(KubectlProviderOptions.builder()
.kubectlLayer(new KubectlV36Layer(this, "kubectl"))
.environment(Map.of(
"http_proxy", "http://proxy.myproxy.com"))
.build())
.build();
using Amazon.CDK.LambdaLayer.KubectlV36;
var cluster = new Cluster(this, "hello-eks", new ClusterProps {
Version = KubernetesVersion.V1_36,
KubectlProviderOptions = new KubectlProviderOptions {
KubectlLayer = new KubectlV36Layer(this, "kubectl"),
Environment = new Dictionary<string, string> {
{ "http_proxy", "http://proxy.myproxy.com" }
}
}
});
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"
cluster := eks.NewCluster(this, jsii.String("hello-eks"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
KubectlProviderOptions: &KubectlProviderOptions{
KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
Environment: map[string]*string{
"http_proxy": jsii.String("http://proxy.myproxy.com"),
},
},
})
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.
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';
const cluster = new eks.Cluster(this, 'hello-eks', {
version: eks.KubernetesVersion.V1_36,
kubectlProviderOptions: {
kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
},
});
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer
cluster = eks.Cluster(self, "hello-eks",
version=eks.KubernetesVersion.V1_36,
kubectl_provider_options=eks.KubectlProviderOptions(
kubectl_layer=KubectlV36Layer(self, "kubectl")
)
)
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;
Cluster cluster = Cluster.Builder.create(this, "hello-eks")
.version(KubernetesVersion.V1_36)
.kubectlProviderOptions(KubectlProviderOptions.builder()
.kubectlLayer(new KubectlV36Layer(this, "kubectl"))
.build())
.build();
using Amazon.CDK.LambdaLayer.KubectlV36;
var cluster = new Cluster(this, "hello-eks", new ClusterProps {
Version = KubernetesVersion.V1_36,
KubectlProviderOptions = new KubectlProviderOptions {
KubectlLayer = new KubectlV36Layer(this, "kubectl")
}
});
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"
cluster := eks.NewCluster(this, jsii.String("hello-eks"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
KubectlProviderOptions: &KubectlProviderOptions{
KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
},
})
Memory
By default, the kubectl provider is configured with 1024MiB of memory. You can use the memory option to specify the memory size for the AWS Lambda function:
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';
new eks.Cluster(this, 'MyCluster', {
kubectlProviderOptions: {
kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
memory: Size.gibibytes(4),
},
version: eks.KubernetesVersion.V1_36,
});
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer
eks.Cluster(self, "MyCluster",
kubectl_provider_options=eks.KubectlProviderOptions(
kubectl_layer=KubectlV36Layer(self, "kubectl"),
memory=Size.gibibytes(4)
),
version=eks.KubernetesVersion.V1_36
)
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;
Cluster.Builder.create(this, "MyCluster")
.kubectlProviderOptions(KubectlProviderOptions.builder()
.kubectlLayer(new KubectlV36Layer(this, "kubectl"))
.memory(Size.gibibytes(4))
.build())
.version(KubernetesVersion.V1_36)
.build();
using Amazon.CDK.LambdaLayer.KubectlV36;
new Cluster(this, "MyCluster", new ClusterProps {
KubectlProviderOptions = new KubectlProviderOptions {
KubectlLayer = new KubectlV36Layer(this, "kubectl"),
Memory = Size.Gibibytes(4)
},
Version = KubernetesVersion.V1_36
});
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"
eks.NewCluster(this, jsii.String("MyCluster"), &ClusterProps{
KubectlProviderOptions: &KubectlProviderOptions{
KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
Memory: awscdk.Size_Gibibytes(jsii.Number(4)),
},
Version: eks.KubernetesVersion_V1_36(),
})
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.
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,
})
# 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
)
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());
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
});
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 AmazonEKSClusterAdminPolicy through Access Entry.
declare const role: iam.Role;
new eks.Cluster(this, 'HelloEKS', {
version: eks.KubernetesVersion.V1_36,
mastersRole: role,
});
# role: iam.Role
eks.Cluster(self, "HelloEKS",
version=eks.KubernetesVersion.V1_36,
masters_role=role
)
Role role;
Cluster.Builder.create(this, "HelloEKS")
.version(KubernetesVersion.V1_36)
.mastersRole(role)
.build();
Role role;
new Cluster(this, "HelloEKS", new ClusterProps {
Version = KubernetesVersion.V1_36,
MastersRole = role
});
var role Role
eks.NewCluster(this, jsii.String("HelloEKS"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
MastersRole: role,
})
If you do not specify it, you won't have access to the cluster from outside of the CDK application.
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.
const secretsKey = new kms.Key(this, 'SecretsKey');
const cluster = new eks.Cluster(this, 'MyCluster', {
secretsEncryptionKey: secretsKey,
version: eks.KubernetesVersion.V1_36,
});
secrets_key = kms.Key(self, "SecretsKey")
cluster = eks.Cluster(self, "MyCluster",
secrets_encryption_key=secrets_key,
version=eks.KubernetesVersion.V1_36
)
Key secretsKey = new Key(this, "SecretsKey");
Cluster cluster = Cluster.Builder.create(this, "MyCluster")
.secretsEncryptionKey(secretsKey)
.version(KubernetesVersion.V1_36)
.build();
var secretsKey = new Key(this, "SecretsKey");
var cluster = new Cluster(this, "MyCluster", new ClusterProps {
SecretsEncryptionKey = secretsKey,
Version = KubernetesVersion.V1_36
});
secretsKey := kms.NewKey(this, jsii.String("SecretsKey"))
cluster := eks.NewCluster(this, jsii.String("MyCluster"), &ClusterProps{
SecretsEncryptionKey: secretsKey,
Version: eks.KubernetesVersion_V1_36(),
})
You can also use a similar configuration for running a cluster built using the FargateCluster construct.
const secretsKey = new kms.Key(this, 'SecretsKey');
const cluster = new eks.FargateCluster(this, 'MyFargateCluster', {
secretsEncryptionKey: secretsKey,
version: eks.KubernetesVersion.V1_36,
});
secrets_key = kms.Key(self, "SecretsKey")
cluster = eks.FargateCluster(self, "MyFargateCluster",
secrets_encryption_key=secrets_key,
version=eks.KubernetesVersion.V1_36
)
Key secretsKey = new Key(this, "SecretsKey");
FargateCluster cluster = FargateCluster.Builder.create(this, "MyFargateCluster")
.secretsEncryptionKey(secretsKey)
.version(KubernetesVersion.V1_36)
.build();
var secretsKey = new Key(this, "SecretsKey");
var cluster = new FargateCluster(this, "MyFargateCluster", new FargateClusterProps {
SecretsEncryptionKey = secretsKey,
Version = KubernetesVersion.V1_36
});
secretsKey := kms.NewKey(this, jsii.String("SecretsKey"))
cluster := eks.NewFargateCluster(this, jsii.String("MyFargateCluster"), &FargateClusterProps{
SecretsEncryptionKey: secretsKey,
Version: eks.KubernetesVersion_V1_36(),
})
The Amazon Resource Name (ARN) for that CMK can be retrieved.
declare const cluster: eks.Cluster;
const clusterEncryptionConfigKeyArn = cluster.clusterEncryptionConfigKeyArn;
# cluster: eks.Cluster
cluster_encryption_config_key_arn = cluster.cluster_encryption_config_key_arn
Cluster cluster;
String clusterEncryptionConfigKeyArn = cluster.getClusterEncryptionConfigKeyArn();
Cluster cluster;
var clusterEncryptionConfigKeyArn = cluster.ClusterEncryptionConfigKeyArn;
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:
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';
new eks.Cluster(this, 'Cluster', {
version: eks.KubernetesVersion.V1_36,
remoteNodeNetworks: [
{
cidrs: ['10.0.0.0/16'],
},
],
remotePodNetworks: [
{
cidrs: ['192.168.0.0/16'],
},
],
});
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer
eks.Cluster(self, "Cluster",
version=eks.KubernetesVersion.V1_36,
remote_node_networks=[eks.RemoteNodeNetwork(
cidrs=["10.0.0.0/16"]
)
],
remote_pod_networks=[eks.RemotePodNetwork(
cidrs=["192.168.0.0/16"]
)
]
)
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;
Cluster.Builder.create(this, "Cluster")
.version(KubernetesVersion.V1_36)
.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();
using Amazon.CDK.LambdaLayer.KubectlV36;
new Cluster(this, "Cluster", new ClusterProps {
Version = KubernetesVersion.V1_36,
RemoteNodeNetworks = new [] { new RemoteNodeNetwork {
Cidrs = new [] { "10.0.0.0/16" }
} },
RemotePodNetworks = new [] { new RemotePodNetwork {
Cidrs = new [] { "192.168.0.0/16" }
} }
});
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"
eks.NewCluster(this, jsii.String("Cluster"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
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
bootstrapSelfManagedAddonsafter the EKS cluster creation will result in a replacement of the cluster.
Permissions and Security
In the new EKS module, ConfigMap is deprecated. Clusters created by the new module will use API as authentication mode. Access Entry will be the only way for granting permissions to specific IAM users and roles.
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:
// 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'] } );
# 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"]
)
// 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());
// 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" }
});
// 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:
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 cluster = new eks.Cluster(this, 'Cluster', {
vpc,
mastersRole: clusterAdminRole,
version: eks.KubernetesVersion.V1_36,
kubectlProviderOptions: {
kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
memory: Size.gibibytes(4),
},
});
// 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'],
}),
]);
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")
)
cluster = eks.Cluster(self, "Cluster",
vpc=vpc,
masters_role=cluster_admin_role,
version=eks.KubernetesVersion.V1_36,
kubectl_provider_options=eks.KubectlProviderOptions(
kubectl_layer=KubectlV36Layer(self, "kubectl"),
memory=Size.gibibytes(4)
)
)
# 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"]
)
])
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();
Cluster cluster = Cluster.Builder.create(this, "Cluster")
.vpc(vpc)
.mastersRole(clusterAdminRole)
.version(KubernetesVersion.V1_36)
.kubectlProviderOptions(KubectlProviderOptions.builder()
.kubectlLayer(new KubectlV36Layer(this, "kubectl"))
.memory(Size.gibibytes(4))
.build())
.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())));
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 cluster = new Cluster(this, "Cluster", new ClusterProps {
Vpc = vpc,
MastersRole = clusterAdminRole,
Version = KubernetesVersion.V1_36,
KubectlProviderOptions = new KubectlProviderOptions {
KubectlLayer = new KubectlV36Layer(this, "kubectl"),
Memory = Size.Gibibytes(4)
}
});
// 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" }
}) });
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")),
})
cluster := eks.NewCluster(this, jsii.String("Cluster"), &ClusterProps{
Vpc: Vpc,
MastersRole: clusterAdminRole,
Version: eks.KubernetesVersion_V1_36(),
KubectlProviderOptions: &KubectlProviderOptions{
KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
Memory: awscdk.Size_Gibibytes(jsii.Number(4)),
},
})
// 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"),
},
}),
})
Access Entry Types
You can optionally specify an access entry type when granting access. This is particularly useful for EKS Auto Mode clusters with custom node roles, which require the EC2 type:
declare const cluster: eks.Cluster;
declare const nodeRole: iam.Role;
// Grant access with EC2 type for Auto Mode node role
cluster.grantAccess('nodeAccess', nodeRole.roleArn, [
eks.AccessPolicy.fromAccessPolicyName('AmazonEKSAutoNodePolicy', {
accessScopeType: eks.AccessScopeType.CLUSTER,
}),
], { accessEntryType: eks.AccessEntryType.EC2 });
# cluster: eks.Cluster
# node_role: iam.Role
# Grant access with EC2 type for Auto Mode node role
cluster.grant_access("nodeAccess", node_role.role_arn, [
eks.AccessPolicy.from_access_policy_name("AmazonEKSAutoNodePolicy",
access_scope_type=eks.AccessScopeType.CLUSTER
)
], access_entry_type=eks.AccessEntryType.EC2)
Cluster cluster;
Role nodeRole;
// Grant access with EC2 type for Auto Mode node role
cluster.grantAccess("nodeAccess", nodeRole.getRoleArn(), List.of(AccessPolicy.fromAccessPolicyName("AmazonEKSAutoNodePolicy", AccessPolicyNameOptions.builder()
.accessScopeType(AccessScopeType.CLUSTER)
.build())), GrantAccessOptions.builder().accessEntryType(AccessEntryType.EC2).build());
Cluster cluster;
Role nodeRole;
// Grant access with EC2 type for Auto Mode node role
cluster.GrantAccess("nodeAccess", nodeRole.RoleArn, new [] { AccessPolicy.FromAccessPolicyName("AmazonEKSAutoNodePolicy", new AccessPolicyNameOptions {
AccessScopeType = AccessScopeType.CLUSTER
}) }, new GrantAccessOptions { AccessEntryType = AccessEntryType.EC2 });
var cluster Cluster
var nodeRole Role
// Grant access with EC2 type for Auto Mode node role
cluster.GrantAccess(jsii.String("nodeAccess"), nodeRole.roleArn, []IAccessPolicy{
eks.AccessPolicy_FromAccessPolicyName(jsii.String("AmazonEKSAutoNodePolicy"), &AccessPolicyNameOptions{
AccessScopeType: eks.AccessScopeType_CLUSTER,
}),
}, &GrantAccessOptions{
AccessEntryType: eks.AccessEntryType_EC2,
})
The following access entry types are supported:
STANDARD- Default type for standard IAM principals (default when not specified)FARGATE_LINUX- For Fargate profilesEC2_LINUX- For EC2 Linux worker nodesEC2_WINDOWS- For EC2 Windows worker nodesEC2- For EKS Auto Mode node rolesHYBRID_LINUX- For EKS Hybrid NodesHYPERPOD_LINUX- For Amazon SageMaker HyperPod
Note: Access entries with type HYBRID_LINUX or HYPERPOD_LINUX cannot have access policies attached per AWS EKS API constraints. For these types, use the AccessEntry construct directly with an empty access policies array.
By default, the cluster creator role will be granted the cluster admin permissions. You can disable it by setting bootstrapClusterCreatorAdminPermissions to false.
Note - Switching
bootstrapClusterCreatorAdminPermissionson an existing cluster would cause cluster replacement and should be avoided in production.
Service Accounts
With services account you can provide Kubernetes Pods access to AWS resources.
import * as s3 from 'aws-cdk-lib/aws-s3';
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 });
import aws_cdk.aws_s3 as s3
# 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)
import software.amazon.awscdk.services.s3.*;
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();
using Amazon.CDK.AWS.S3;
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 });
import s3 "github.com/aws/aws-cdk-go/awscdk"
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.
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',
},
});
# 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"
}
)
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());
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" }
}
});
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"),
},
})
You can also add service accounts to existing clusters. To do so, pass the openIdConnectProvider property when you import the cluster into the application.
import * as s3 from 'aws-cdk-lib/aws-s3';
// 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,
});
import { KubectlV36Layer } from '@aws-cdk/lambda-layer-kubectl-v36';
const cluster = eks.Cluster.fromClusterAttributes(this, 'MyCluster', {
clusterName: 'Cluster',
openIdConnectProvider: provider,
kubectlProviderOptions: {
kubectlLayer: new KubectlV36Layer(this, 'kubectl'),
}});
const serviceAccount = cluster.addServiceAccount('MyServiceAccount');
const bucket = new s3.Bucket(this, 'Bucket');
bucket.grantReadWrite(serviceAccount);
import aws_cdk.aws_s3 as s3
# or create a new one using an existing issuer url
# issuer_url: str
from aws_cdk.lambda_layer_kubectl_v36 import KubectlV36Layer
# 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_provider_options=eks.KubectlProviderOptions(
kubectl_layer=KubectlV36Layer(self, "kubectl")
)
)
service_account = cluster.add_service_account("MyServiceAccount")
bucket = s3.Bucket(self, "Bucket")
bucket.grant_read_write(service_account)
import software.amazon.awscdk.services.s3.*;
// or create a new one using an existing issuer url
String issuerUrl;
import software.amazon.awscdk.cdk.lambdalayer.kubectl.v36.KubectlV36Layer;
// 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)
.kubectlProviderOptions(KubectlProviderOptions.builder()
.kubectlLayer(new KubectlV36Layer(this, "kubectl"))
.build())
.build());
ServiceAccount serviceAccount = cluster.addServiceAccount("MyServiceAccount");
Bucket bucket = new Bucket(this, "Bucket");
bucket.grantReadWrite(serviceAccount);
using Amazon.CDK.AWS.S3;
// or create a new one using an existing issuer url
string issuerUrl;
using Amazon.CDK.LambdaLayer.KubectlV36;
// 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,
KubectlProviderOptions = new KubectlProviderOptions {
KubectlLayer = new KubectlV36Layer(this, "kubectl")
}
});
var serviceAccount = cluster.AddServiceAccount("MyServiceAccount");
var bucket = new Bucket(this, "Bucket");
bucket.GrantReadWrite(serviceAccount);
import s3 "github.com/aws/aws-cdk-go/awscdk"
// or create a new one using an existing issuer url
var issuerUrl string
import "github.com/cdklabs/awscdk-kubectl-go/kubectlv36"
// 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,
KubectlProviderOptions: &KubectlProviderOptions{
KubectlLayer: kubectlv36.NewKubectlV36Layer(this, jsii.String("kubectl")),
},
})
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 which requires you to provide kubectlProviderOptions in the cluster props to create the kubectl provider. See Kubectl Support
Migrating from the deprecated 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.
To migrate without temporarily removing the OIDCProvider, follow these steps:
Set the
removalPolicyofcluster.openIdConnectProvidertoRETAIN.tsimport * as cdk from 'aws-cdk-lib'; declare const cluster: eks.Cluster; cdk.RemovalPolicies.of(cluster.openIdConnectProvider).apply(cdk.RemovalPolicy.RETAIN);Run
cdk diffto verify the changes are expected thencdk deploy.Add the following to the
contextfield of yourcdk.jsonto enable the feature flag that creates the native oidc provider.json"@aws-cdk/aws-eks:useNativeOidcProvider": true,Run
cdk diffand ensure the changes are expected. Example of an expected diff:bashResources [-] 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 TestClusterOidcProviderNative0BE3F155Run
cdk import --forceand provide the ARN of the existing OpenIdConnectProvider when prompted. You will get a warning about pending changes to existing resources which is expected.Run
cdk deployto 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:
Set the
removalPolicyof the existingOpenIdConnectProvidertoRemovalPolicy.RETAIN.tsimport * 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 });Deploy with the retain policy to avoid deletion of the underlying resource.
bashcdk deployReplace
OpenIdConnectProviderwithOidcProviderNativein 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', });Run
cdk diffand verify the changes are expected. Example of an expected diff:bashResources [-] 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 TestClusterOidcProviderNative0BE3F155Run
cdk import --forceto import the existing OIDC provider resource by providing the existing ARN.Run
cdk deployto apply any pending changes. This will apply the destroy/orphan operations in the example diff above.
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.
declare const cluster: eks.Cluster;
const clusterSecurityGroupId = cluster.clusterSecurityGroupId;
# cluster: eks.Cluster
cluster_security_group_id = cluster.cluster_security_group_id
Cluster cluster;
String clusterSecurityGroupId = cluster.getClusterSecurityGroupId();
Cluster cluster;
var clusterSecurityGroupId = cluster.ClusterSecurityGroupId;
var cluster Cluster
clusterSecurityGroupId := cluster.ClusterSecurityGroupId
Applying Kubernetes Resources
To apply kubernetes resource, kubectl provider needs to be created for the cluster. You can use kubectlProviderOptions to create the kubectl Provider.
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 usenew KubernetesManifestto create the manifest in the scope of the other stack.
The following examples will deploy the paulbouwer/hello-kubernetes service on the cluster:
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);
# 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)
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);
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);
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 tointernal.
Adding resources from a URL
The following example will deploy the resource manifest hosting on remote server:
// 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():
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`.
# 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)
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);
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);
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:
new eks.Cluster(this, 'MyCluster', {
version: eks.KubernetesVersion.V1_36,
prune: false,
});
eks.Cluster(self, "MyCluster",
version=eks.KubernetesVersion.V1_36,
prune=False
)
Cluster.Builder.create(this, "MyCluster")
.version(KubernetesVersion.V1_36)
.prune(false)
.build();
new Cluster(this, "MyCluster", new ClusterProps {
Version = KubernetesVersion.V1_36,
Prune = false
});
eks.NewCluster(this, jsii.String("MyCluster"), &ClusterProps{
Version: eks.KubernetesVersion_V1_36(),
Prune: jsii.Boolean(false),
})
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.
declare const cluster: eks.Cluster;
new eks.KubernetesManifest(this, 'HelloAppWithoutValidation', {
cluster,
manifest: [{ foo: 'bar' }],
skipValidation: true,
});
# cluster: eks.Cluster
eks.KubernetesManifest(self, "HelloAppWithoutValidation",
cluster=cluster,
manifest=[{"foo": "bar"}],
skip_validation=True
)
Cluster cluster;
KubernetesManifest.Builder.create(this, "HelloAppWithoutValidation")
.cluster(cluster)
.manifest(List.of(Map.of("foo", "bar")))
.skipValidation(true)
.build();
Cluster cluster;
new KubernetesManifest(this, "HelloAppWithoutValidation", new KubernetesManifestProps {
Cluster = cluster,
Manifest = new [] { new Dictionary<string, object> { { "foo", "bar" } } },
SkipValidation = true
});
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 usenew HelmChartto create the chart in the scope of the other stack.
The following example will install the NGINX Ingress Controller to your cluster using Helm.
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',
});
# 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"
)
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());
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"
});
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.
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,
});
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
)
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());
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
});
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:
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
}
},
});
# 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
}
}
)
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());
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 }
} }
}
});
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.
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,
});
# 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
)
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();
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
});
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.
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'
});
# 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"
)
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();
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"
});
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:
declare const cluster: eks.Cluster;
const chart1 = cluster.addHelmChart('MyChart', {
chart: 'foo',
});
const chart2 = cluster.addHelmChart('MyChart', {
chart: 'bar',
});
chart2.node.addDependency(chart1);
# cluster: eks.Cluster
chart1 = cluster.add_helm_chart("MyChart",
chart="foo"
)
chart2 = cluster.add_helm_chart("MyChart",
chart="bar"
)
chart2.node.add_dependency(chart1)
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);
Cluster cluster;
var chart1 = cluster.AddHelmChart("MyChart", new HelmChartOptions {
Chart = "foo"
});
var chart2 = cluster.AddHelmChart("MyChart", new HelmChartOptions {
Chart = "bar"
});
chart2.Node.AddDependency(chart1);
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)
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.
import * as constructs from 'constructs';
import * as cdk8s from 'cdk8s';
import * as kplus from 'cdk8s-plus-25';
interface LoadBalancedWebService {
readonly port: number;
readonly image: string;
readonly replicas: number;
}
const app = new cdk8s.App();
const chart = new cdk8s.Chart(app, 'my-chart');
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,
});
}
}
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
)
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());
}
}
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
});
}
}
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.
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 } },
})
# 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}}
)
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();
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 } } } }
});
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:
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,
},
})
# 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
}
)
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();
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 }
}
});
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:
declare const cluster: eks.Cluster;
const loadBalancerAddress = cluster.getServiceLoadBalancerAddress('my-service');
# cluster: eks.Cluster
load_balancer_address = cluster.get_service_load_balancer_address("my-service")
Cluster cluster;
String loadBalancerAddress = cluster.getServiceLoadBalancerAddress("my-service");
Cluster cluster;
var loadBalancerAddress = cluster.GetServiceLoadBalancerAddress("my-service");
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.
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,
},
});
# 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
}
)
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();
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 }
}
});
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 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 will need to import the kubectl provider and cluster created in another stack
const handlerRole = iam.Role.fromRoleArn(this, 'HandlerRole', 'arn:aws:iam::123456789012:role/lambda-role');
const kubectlProvider = eks.KubectlProvider.fromKubectlProviderAttributes(this, 'KubectlProvider', {
serviceToken: 'arn:aws:lambda:us-east-2:123456789012:function:my-function:1',
role: handlerRole,
});
const cluster = eks.Cluster.fromClusterAttributes(this, 'Cluster', {
clusterName: 'cluster',
kubectlProvider,
});
handler_role = iam.Role.from_role_arn(self, "HandlerRole", "arn:aws:iam::123456789012:role/lambda-role")
kubectl_provider = eks.KubectlProvider.from_kubectl_provider_attributes(self, "KubectlProvider",
service_token="arn:aws:lambda:us-east-2:123456789012:function:my-function:1",
role=handler_role
)
cluster = eks.Cluster.from_cluster_attributes(self, "Cluster",
cluster_name="cluster",
kubectl_provider=kubectl_provider
)
IRole handlerRole = Role.fromRoleArn(this, "HandlerRole", "arn:aws:iam::123456789012:role/lambda-role");
IKubectlProvider kubectlProvider = KubectlProvider.fromKubectlProviderAttributes(this, "KubectlProvider", KubectlProviderAttributes.builder()
.serviceToken("arn:aws:lambda:us-east-2:123456789012:function:my-function:1")
.role(handlerRole)
.build());
ICluster cluster = Cluster.fromClusterAttributes(this, "Cluster", ClusterAttributes.builder()
.clusterName("cluster")
.kubectlProvider(kubectlProvider)
.build());
var handlerRole = Role.FromRoleArn(this, "HandlerRole", "arn:aws:iam::123456789012:role/lambda-role");
var kubectlProvider = KubectlProvider.FromKubectlProviderAttributes(this, "KubectlProvider", new KubectlProviderAttributes {
ServiceToken = "arn:aws:lambda:us-east-2:123456789012:function:my-function:1",
Role = handlerRole
});
var cluster = Cluster.FromClusterAttributes(this, "Cluster", new ClusterAttributes {
ClusterName = "cluster",
KubectlProvider = kubectlProvider
});
handlerRole := iam.Role_FromRoleArn(this, jsii.String("HandlerRole"), jsii.String("arn:aws:iam::123456789012:role/lambda-role"))
kubectlProvider := eks.KubectlProvider_FromKubectlProviderAttributes(this, jsii.String("KubectlProvider"), &KubectlProviderAttributes{
ServiceToken: jsii.String("arn:aws:lambda:us-east-2:123456789012:function:my-function:1"),
Role: handlerRole,
})
cluster := eks.Cluster_FromClusterAttributes(this, jsii.String("Cluster"), &ClusterAttributes{
ClusterName: jsii.String("cluster"),
KubectlProvider: KubectlProvider,
})
Then, you can use addManifest or addHelmChart to define resources inside your Kubernetes cluster.
declare const cluster: eks.Cluster;
cluster.addManifest('Test', {
apiVersion: 'v1',
kind: 'ConfigMap',
metadata: {
name: 'myconfigmap',
},
data: {
Key: 'value',
Another: '123454',
},
});
# cluster: eks.Cluster
cluster.add_manifest("Test", {
"api_version": "v1",
"kind": "ConfigMap",
"metadata": {
"name": "myconfigmap"
},
"data": {
"Key": "value",
"Another": "123454"
}
})
Cluster cluster;
cluster.addManifest("Test", Map.of(
"apiVersion", "v1",
"kind", "ConfigMap",
"metadata", Map.of(
"name", "myconfigmap"),
"data", Map.of(
"Key", "value",
"Another", "123454")));
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" }
} }
});
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"),
},
})
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:
const cluster = new eks.Cluster(this, 'Cluster', {
// ...
version: eks.KubernetesVersion.V1_36,
clusterLogging: [
eks.ClusterLoggingTypes.API,
eks.ClusterLoggingTypes.AUTHENTICATOR,
eks.ClusterLoggingTypes.SCHEDULER,
],
});
cluster = eks.Cluster(self, "Cluster",
# ...
version=eks.KubernetesVersion.V1_36,
cluster_logging=[eks.ClusterLoggingTypes.API, eks.ClusterLoggingTypes.AUTHENTICATOR, eks.ClusterLoggingTypes.SCHEDULER
]
)
Cluster cluster = Cluster.Builder.create(this, "Cluster")
// ...
.version(KubernetesVersion.V1_36)
.clusterLogging(List.of(ClusterLoggingTypes.API, ClusterLoggingTypes.AUTHENTICATOR, ClusterLoggingTypes.SCHEDULER))
.build();
var cluster = new Cluster(this, "Cluster", new ClusterProps {
// ...
Version = KubernetesVersion.V1_36,
ClusterLogging = new [] { ClusterLoggingTypes.API, ClusterLoggingTypes.AUTHENTICATOR, ClusterLoggingTypes.SCHEDULER }
});
cluster := eks.NewCluster(this, jsii.String("Cluster"), &ClusterProps{
// ...
Version: eks.KubernetesVersion_V1_36(),
ClusterLogging: []ClusterLoggingTypes{
eks.ClusterLoggingTypes_API,
eks.ClusterLoggingTypes_AUTHENTICATOR,
eks.ClusterLoggingTypes_SCHEDULER,
},
})
NodeGroup Repair Config
You can enable Managed Node Group auto-repair config using enableNodeAutoRepair property. For example:
declare const cluster: eks.Cluster;
cluster.addNodegroupCapacity('NodeGroup', {
enableNodeAutoRepair:true,
});
# cluster: eks.Cluster
cluster.add_nodegroup_capacity("NodeGroup",
enable_node_auto_repair=True
)
Cluster cluster;
cluster.addNodegroupCapacity("NodeGroup", NodegroupOptions.builder()
.enableNodeAutoRepair(true)
.build());
Cluster cluster;
cluster.AddNodegroupCapacity("NodeGroup", new NodegroupOptions {
EnableNodeAutoRepair = true
});
var cluster Cluster
cluster.AddNodegroupCapacity(jsii.String("NodeGroup"), &NodegroupOptions{
EnableNodeAutoRepair: jsii.Boolean(true),
})
Types
| Name | Kind | Summary |
|---|---|---|
AccessEntry | Construct (L2) | Represents an access entry in an Amazon EKS cluster. |
Addon | Construct (L2) | Represents an Amazon EKS Add-On. |
Cluster | Construct (L2) | A Cluster represents a managed Kubernetes Service (EKS). |
FargateCluster | Construct (L2) | Defines an EKS cluster that runs entirely on AWS Fargate. |
Nodegroup | Construct (L2) | The Nodegroup resource class. |
OidcProviderNative | Construct (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. |
OpenIdConnectProvider | Construct (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. |
CfnAccessEntry | Construct (L1) | Creates an access entry. |
CfnAddon | Construct (L1) | Creates an Amazon EKS add-on. |
CfnCapability | Construct (L1) | An object representing a managed capability in an Amazon EKS cluster. |
CfnCertificateAuthority | Construct (L1) | Resource Type definition for EKS CertificateAuthority. |
CfnCluster | Construct (L1) | Creates an Amazon EKS control plane. |
CfnFargateProfile | Construct (L1) | Creates an AWS Fargate profile for your Amazon EKS cluster. |
CfnIdentityProviderConfig | Construct (L1) | Associates an identity provider configuration to a cluster. |
CfnNodegroup | Construct (L1) | Creates a managed node group for an Amazon EKS cluster. |
CfnPodIdentityAssociation | Construct (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. |
AccessEntryAttributes | Struct | Represents the attributes of an access entry. |
AccessEntryProps | Struct | Represents the properties required to create an Amazon EKS access entry. |
AccessEntryType | Enum | Represents the different types of access entries that can be used in an Amazon EKS cluster. |
AccessPolicy | Class | Represents an Amazon EKS Access Policy that implements the IAccessPolicy interface. |
AccessPolicyArn | Class | Represents an Amazon EKS Access Policy ARN. |
AccessPolicyNameOptions | Struct | Represents the options required to create an Amazon EKS Access Policy using the fromAccessPolicyName() method. |
AccessPolicyProps | Struct | Properties for configuring an Amazon EKS Access Policy. |
AccessScope | Struct | Represents the scope of an access policy. |
AccessScopeType | Enum | Represents the scope type of an access policy. |
AddonAttributes | Struct | Represents the attributes of an addon for an Amazon EKS cluster. |
AddonProps | Struct | Properties for creating an Amazon EKS Add-On. |
AlbController | Construct | Construct for installing the AWS ALB Contoller on EKS clusters. |
AlbControllerOptions | Struct | Options for AlbController. |
AlbControllerProps | Struct | Properties for AlbController. |
AlbControllerVersion | Class | Controller version. |
AlbScheme | Enum | ALB Scheme. |
AutoScalingGroupCapacityOptions | Struct | Options for adding worker nodes. |
AutoScalingGroupOptions | Struct | Options for adding an AutoScalingGroup as capacity. |
BootstrapOptions | Struct | EKS node bootstrapping options. |
CapacityType | Enum | Capacity type of the managed node group. |
CfnAccessEntry.AccessPolicyProperty | Struct | An access policy includes permissions that allow Amazon EKS to authorize an IAM principal to work with Kubernetes objects on your cluster. |
CfnAccessEntry.AccessScopeProperty | Struct | The scope of an AccessPolicy that's associated to an AccessEntry . |
CfnAccessEntryProps | Struct | Properties for defining a CfnAccessEntry. |
CfnAddon.NamespaceConfigProperty | Struct | The custom namespace configuration to use with the add-on. |
CfnAddon.PodIdentityAssociationProperty | Struct | 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. |
CfnAddonProps | Struct | Properties for defining a CfnAddon. |
CfnCapability.AckProperty | Struct | Configuration settings for an ACK (AWS Controllers for Kubernetes) capability. |
CfnCapability.ArgoCdProperty | Struct | Configuration settings for an Argo CD capability. |
CfnCapability.ArgoCdRoleMappingProperty | Struct | A mapping between an Argo CD role and IAM Identity CenterIAM; |
CfnCapability.AwsIdcProperty | Struct | Configuration for integrating Argo CD with IAM Identity Center. |
CfnCapability.CapabilityConfigurationProperty | Struct | Configuration settings for a capability. |
CfnCapability.NetworkAccessProperty | Struct | Configuration for network access to the Argo CD capability's managed API server endpoint. |
CfnCapability.SsoIdentityProperty | Struct | An IAM Identity CenterIAM; |
CfnCapabilityProps | Struct | Properties for defining a CfnCapability. |
CfnCertificateAuthority.ScheduledEventsProperty | Struct | The scheduled auto-activation events for the certificate authority, computed from its validity window. |
CfnCertificateAuthority.ValidityProperty | Struct | The validity period of the certificate authority. |
CfnCertificateAuthorityProps | Struct | Properties for defining a CfnCertificateAuthority. |
CfnCluster.AccessConfigProperty | Struct | The access configuration for the cluster. |
CfnCluster.ActiveCertificateAuthorityProperty | Struct | Identifies the certificate authority currently signing certificates for the cluster. |
CfnCluster.BlockStorageProperty | Struct | Indicates the current configuration of the block storage capability on your EKS Auto Mode cluster. |
CfnCluster.CertificateAuthorityProperty | Struct | The certificate authority information for the cluster, including the trust bundle and the currently active signing certificate authority. |
CfnCluster.ClusterLoggingProperty | Struct | The cluster control plane logging configuration for your cluster. |
CfnCluster.ComputeConfigProperty | Struct | Indicates the current configuration of the compute capability on your EKS Auto Mode cluster. |
CfnCluster.ControlPlanePlacementProperty | Struct | The placement configuration for all the control plane instances of your local Amazon EKS cluster on an AWS Outpost. |
CfnCluster.ControlPlaneScalingConfigProperty | Struct | The control plane scaling tier configuration. |
CfnCluster.ElasticLoadBalancingProperty | Struct | Indicates the current configuration of the load balancing capability on your EKS Auto Mode cluster. |
CfnCluster.EncryptionConfigProperty | Struct | The encryption configuration for the cluster. |
CfnCluster.EtcdPlacementProperty | Struct | The placement configuration for the etcd instances of your local Amazon EKS cluster on an AWS Outpost. |
CfnCluster.HorizontalPodAutoscalerControllerConfigProperty | Struct | The horizontal pod autoscaler controller configuration. |
CfnCluster.KubeApiServerConfigProperty | Struct | The configuration for the Kubernetes API server on an Amazon EKS cluster. |
CfnCluster.KubeControllerManagerConfigProperty | Struct | The configuration for the Kubernetes controller manager on an Amazon EKS cluster. |
CfnCluster.KubernetesNetworkConfigProperty | Struct | The Kubernetes network configuration for the cluster. |
CfnCluster.KubeSchedulerConfigProperty | Struct | The configuration for the Kubernetes scheduler on an Amazon EKS cluster. |
CfnCluster.LoggingProperty | Struct | Enable or disable exporting the Kubernetes control plane logs for your cluster to CloudWatch Logs. |
CfnCluster.LoggingTypeConfigProperty | Struct | The enabled logging type. |
CfnCluster.NodeResourcesFitConfigProperty | Struct | The NodeResourcesFit plugin configuration for the Kubernetes scheduler. |
CfnCluster.OutpostConfigProperty | Struct | The configuration of your local Amazon EKS cluster on an AWS Outpost. |
CfnCluster.PodGcControllerConfigProperty | Struct | The pod garbage collector controller configuration. |
CfnCluster.ProviderProperty | Struct | Identifies the AWS Key Management Service ( AWS ) key used to encrypt the secrets. |
CfnCluster.RemoteNetworkConfigProperty | Struct | The configuration in the cluster for EKS Hybrid Nodes. |
CfnCluster.RemoteNodeNetworkProperty | Struct | A network CIDR that can contain hybrid nodes. |
CfnCluster.RemotePodNetworkProperty | Struct | A network CIDR that can contain pods that run Kubernetes webhooks on hybrid nodes. |
CfnCluster.ResourcesVpcConfigProperty | Struct | An object representing the VPC configuration to use for an Amazon EKS cluster. |
CfnCluster.ResourceWeightProperty | Struct | A resource weight entry for the scheduler scoring strategy. |
CfnCluster.RollbackConfigProperty | Struct | The rollback configuration to use for the cluster version rollback. |
CfnCluster.ScoringStrategyProperty | Struct | The scoring strategy configuration for the NodeResourcesFit scheduler plugin. |
CfnCluster.ServiceNodePortRangeProperty | Struct | The port range for Kubernetes NodePort services. |
CfnCluster.StorageConfigProperty | Struct | Request to update the configuration of the storage capability of your EKS Auto Mode cluster. |
CfnCluster.UpgradePolicyProperty | Struct | The support policy to use for the cluster. |
CfnCluster.ZonalShiftConfigProperty | Struct | The configuration for zonal shift for the cluster. |
CfnClusterProps | Struct | Properties for defining a CfnCluster. |
CfnFargateProfile.LabelProperty | Struct | A key-value pair. |
CfnFargateProfile.SelectorProperty | Struct | An object representing an AWS Fargate profile selector. |
CfnFargateProfileProps | Struct | Properties for defining a CfnFargateProfile. |
CfnIdentityProviderConfig.OidcIdentityProviderConfigProperty | Struct | An object representing the configuration for an OpenID Connect (OIDC) identity provider. |
CfnIdentityProviderConfig.RequiredClaimProperty | Struct | A key-value pair that describes a required claim in the identity token. |
CfnIdentityProviderConfigProps | Struct | Properties for defining a CfnIdentityProviderConfig. |
CfnNodegroup.LaunchTemplateSpecificationProperty | Struct | An object representing a node group launch template specification. |
CfnNodegroup.NodeRepairConfigOverridesProperty | Struct | Specify granular overrides for specific repair actions. |
CfnNodegroup.NodeRepairConfigProperty | Struct | The node auto repair configuration for the node group. |
CfnNodegroup.RemoteAccessProperty | Struct | An object representing the remote access configuration for the managed node group. |
CfnNodegroup.ScalingConfigProperty | Struct | An object representing the scaling configuration details for the Auto Scaling group that is associated with your node group. |
CfnNodegroup.TaintProperty | Struct | A property that allows a node to repel a Pod . |
CfnNodegroup.UpdateConfigProperty | Struct | The update configuration for the node group. |
CfnNodegroup.WarmPoolConfigProperty | Struct | The warm pool configuration for the node group. |
CfnNodegroupProps | Struct | Properties for defining a CfnNodegroup. |
CfnPodIdentityAssociationProps | Struct | Properties for defining a CfnPodIdentityAssociation. |
ClusterAttributes | Struct | Attributes for EKS clusters. |
ClusterCommonOptions | Struct | Options for configuring an EKS cluster. |
ClusterLoggingTypes | Enum | EKS cluster logging types. |
ClusterProps | Struct | Properties for configuring a standard EKS cluster (non-Fargate). |
ComputeConfig | Struct | Options for configuring EKS Auto Mode compute settings. |
ControlPlaneScalingTier | Class | The scaling tier for the EKS cluster provisioned control plane. |
CoreDnsComputeType | Enum | The type of compute resources to use for CoreDNS. |
CpuArch | Enum | CPU architecture. |
DefaultCapacityType | Enum | The default capacity type for the cluster. |
EksOptimizedImage | Class | Construct an Amazon Linux 2 image from the latest EKS Optimized AMI published in SSM. |
EksOptimizedImageProps | Struct | Properties for EksOptimizedImage. |
EndpointAccess | Class | Endpoint access characteristics. |
FargateClusterProps | Struct | Configuration props for EKS Fargate. |
FargateProfile | Construct | Fargate profiles allows an administrator to declare which pods run on Fargate. |
FargateProfileOptions | Struct | Options for defining EKS Fargate Profiles. |
FargateProfileProps | Struct | Configuration props for EKS Fargate Profiles. |
GrantAccessOptions | Struct | Options for granting access to a cluster. |
HelmChart | Construct | Represents a helm chart within the Kubernetes system. |
HelmChartOptions | Struct | Helm Chart options. |
HelmChartProps | Struct | Helm Chart properties. |
IAccessEntry | Interface | Represents an access entry in an Amazon EKS cluster. |
IAccessPolicy | Interface | Represents an access policy that defines the permissions and scope for a user or role to access an Amazon EKS cluster. |
IAddon | Interface | Represents an Amazon EKS Add-On. |
ICluster | Interface | An EKS cluster. |
IdentityType | Enum | Enum representing the different identity types that can be used for a Kubernetes service account. |
IKubectlProvider | Interface | Imported KubectlProvider that can be used in place of the default one created by CDK. |
IngressLoadBalancerAddressOptions | Struct | Options for fetching an IngressLoadBalancerAddress. |
INodegroup | Interface | NodeGroup interface. |
IpFamily | Enum | EKS cluster IP family. |
KubectlProvider | Construct | Implementation of Kubectl Lambda. |
KubectlProviderAttributes | Struct | Kubectl Provider Attributes. |
KubectlProviderOptions | Struct | Options for creating the kubectl provider - a lambda function that executes kubectl and helm against the cluster. |
KubectlProviderProps | Struct | Properties for a KubectlProvider. |
KubernetesManifest | Construct | Represents a manifest within the Kubernetes system. |
KubernetesManifestOptions | Struct | Options for KubernetesManifest. |
KubernetesManifestProps | Struct | Properties for KubernetesManifest. |
KubernetesObjectValue | Construct | Represents a value of a specific object deployed in the cluster. |
KubernetesObjectValueProps | Struct | Properties for KubernetesObjectValue. |
KubernetesPatch | Construct | A CloudFormation resource which applies/restores a JSON patch into a Kubernetes resource. |
KubernetesPatchProps | Struct | Properties for KubernetesPatch. |
KubernetesVersion | Class | Kubernetes cluster version. |
LaunchTemplateSpec | Struct | Launch template property specification. |
MachineImageType | Enum | The machine image type. |
NodegroupAmiType | Enum | The AMI type for your node group. |
NodegroupOptions | Struct | The Nodegroup Options for addNodeGroup() method. |
NodegroupProps | Struct | NodeGroup properties interface. |
NodegroupRemoteAccess | Struct | The remote access (SSH) configuration to use with your node group. |
NodeType | Enum | Whether the worker nodes should support GPU or just standard instances. |
OidcProviderNativeProps | Struct | Initialization properties for OidcProviderNative. |
OpenIdConnectProviderProps | Struct | Initialization properties for OpenIdConnectProvider. |
PatchType | Enum | Values for kubectl patch --type argument. |
RemoteNodeNetwork | Struct | Remote network configuration for hybrid nodes. |
RemotePodNetwork | Struct | Remote network configuration for pods on hybrid nodes. |
Selector | Struct | Fargate profile selector. |
ServiceAccount | Construct | Service Account. |
ServiceAccountOptions | Struct | Options for ServiceAccount. |
ServiceAccountProps | Struct | Properties for defining service accounts. |
ServiceLoadBalancerAddressOptions | Struct | Options for fetching a ServiceLoadBalancerAddress. |
TaintEffect | Enum | Effect types of kubernetes node taint. |
TaintSpec | Struct | Taint interface. |