In my previous post, I have discussed about how to create vpc, subnets, internet gateway, nat gateway using AWS CDK(Python). In this post, we will be discussing about how to create EKS (k8s) cluster. This is requested by many of my followers, so I would like to write a blog to help the community.
Create a new stack called EKSStack and use existing vpc from other stack (vpc: ec2.Vpc)
class EKSStack(core.Stack):
def __init__(self, scope: core.Construct, construct_id: str, vpc: ec2.Vpc, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
Create an IAM role for worker groups and kubernetes RBAC configuration
eks_role = iam.Role(self, "eksadmin", assumed_by=iam.ServicePrincipal(service='ec2.amazonaws.com'),
role_name='eks-cluster-role', managed_policies=
[iam.ManagedPolicy.from_aws_managed_policy_name(managed_policy_name='AdministratorAccess')])
eks_instance_profile = iam.CfnInstanceProfile(self, 'instanceprofile',
roles=[eks_role.role_name],
instance_profile_name='eks-cluster-role')
Once the role is created, let us create EKS Cluster and attach the role as masters_role.
cluster = eks.Cluster(self, 'prod', cluster_name='eks-demo-cluster',
version=eks.KubernetesVersion.V1_19,
vpc=vpc,
vpc_subnets=[ec2.SubnetSelection(subnet_type=ec2.SubnetType.PRIVATE)],
default_capacity=0,
masters_role=eks_role)
In the above code, you can see that I’m using version 1.9, cluster will use private subnets and the IAM role that was created will be used. I used default_capacity = 0 since, I do not want EKS to create default node group. I will be creating node groups separately and will use SPOT instances for worker nodes in this case. This is all about control plane definition.
Now, let us create node group.
nodegroup = cluster.add_nodegroup_capacity('eks-nodegroup',
instance_types=[ec2.InstanceType('t3.large'),
ec2.InstanceType('m5.large'),
ec2.InstanceType('c5.large')],
disk_size=50,
min_size=2,
max_size=2,
desired_size=2,
subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PRIVATE),
remote_access=eks.NodegroupRemoteAccess(
ssh_key_name='eks-ssh-keypair'),
capacity_type=eks.CapacityType.SPOT)
I’ve used multiple instance types for SPOT for different worker nodes. Disk size for the nodes to set to 50GB and two nodes will be created. you can adjust these parameters based on your requirement.
Now, let’s call the stack.
from mycdkproject.eks_stack import EKSStack
app = core.App()
vpc_stack = VPCStack(app, "mycdkproject")
eks_stack = EKSStack(app,'eks',vpc=vpc_stack.vpc)
app.synth()
you need to update requirements.txt file to import modules for iam and eks. you can install these modules using pip install -r requirements.txt
aws-cdk.aws-iam
aws-cdk.aws-eks
now, let’s run cdk ls to see howmany stacks we can see. we should expect to see two stacks.

Now, let’s Synthesize an AWS CloudFormation template for the app, as follows.
(.venv) C:\Rama\MyProjects\mycdkproject>cdk synth
Successfully synthesized to C:\Rama\MyProjects\mycdkproject\cdk.out
Supply a stack id (mycdkproject, eks) to display its template.
Now, let’s deploy
cdk deploy eks --profile cdkprofile
if you have encountered an error as eks failed: Error: This stack uses assets, so the toolkit stack must be deployed to the environment (Run “cdk bootstrap aws://unknown-account/unknown-region”)
you can fix this by running cdk bootstrap aws://accountnumber/region
(.venv) C:\Rama\MyProjects\mycdkproject>cdk deploy eks --profile cdkprofile
Including dependency stacks: mycdkproject
mycdkproject
mycdkproject: deploying...
✅ mycdkproject (no changes)
Outputs:
mycdkproject.ExportsOutputRefdemovpcF2DCF540F486AA93 = vpc-0a28afc429ef12e7d
mycdkproject.ExportsOutputRefdemovpcPrivateSubnetSubnet1Subnet7F4868328AE74887 = subnet-01d19280e1eca62f5
mycdkproject.ExportsOutputRefdemovpcPrivateSubnetSubnet2Subnet4FD8659B8BB1175E = subnet-05cedf7ea3c9a2d1a
Stack ARN:
arn:aws:cloudformation:eu-west-1:604035856224:stack/mycdkproject/ced8f3a0-7791-11eb-8a02-02fbd2f362b9
eks
eks: deploying...
[0%] start: Publishing 50e10880d134a01b440991fc77d217f39f01c2d56945215ee9a3b81187c6f3b1:current
[14%] success: Published 50e10880d134a01b440991fc77d217f39f01c2d56945215ee9a3b81187c6f3b1:current
[14%] start: Publishing c691172cdeefa2c91b5a2907f9d81118e47597634943344795f1a844192dd49c:current
[28%] success: Published c691172cdeefa2c91b5a2907f9d81118e47597634943344795f1a844192dd49c:current
[28%] start: Publishing 299e5262386e9f084fcd72906b8282f5c3cb1885c39d0912db1670b472873fb5:current
[42%] success: Published 299e5262386e9f084fcd72906b8282f5c3cb1885c39d0912db1670b472873fb5:current
[42%] start: Publishing e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68:current
[57%] success: Published e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68:current
[57%] start: Publishing 844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0:current
[71%] success: Published 844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0:current
[71%] start: Publishing 7f9c09e9e3ebb8ac17932545378ebfc732075bc1529c3f5294d1220ab938c1a8:current
[85%] success: Published 7f9c09e9e3ebb8ac17932545378ebfc732075bc1529c3f5294d1220ab938c1a8:current
[85%] start: Publishing 53fdcf13d33580b64d5d493f823aa68fa304539df464fedc629ad48b949244ad:current
[100%] success: Published 53fdcf13d33580b64d5d493f823aa68fa304539df464fedc629ad48b949244ad:current
eks: creating CloudFormation changeset...
0/15 | 12:50:35 PM | REVIEW_IN_PROGRESS | AWS::CloudFormation::Stack | eks User Initiated
0/15 | 12:50:40 PM | CREATE_IN_PROGRESS | AWS::CloudFormation::Stack | eks User Initiated
0/15 | 12:51:09 PM | CREATE_IN_PROGRESS | AWS::EC2::SecurityGroup | prod/ControlPlaneSecurityGroup (prodControlPlaneSecurityGroup7CE46782)
0/15 | 12:51:09 PM | CREATE_IN_PROGRESS | AWS::IAM::Role | eksadmin (eksadminBEE25D8E)
0/15 | 12:51:09 PM | CREATE_IN_PROGRESS | AWS::IAM::Role | prod/Resource/CreationRole (prodCreationRole5E247585)
0/15 | 12:51:09 PM | CREATE_IN_PROGRESS | AWS::CDK::Metadata | @aws-cdk--aws-eks.ClusterResourceProvider/CDKMetadata/Default (CDKMetadata)
0/15 | 12:51:09 PM | CREATE_IN_PROGRESS | AWS::IAM::Role | prod/Role (prodRoleD997707D)
0/15 | 12:51:09 PM | CREATE_IN_PROGRESS | AWS::IAM::Role | prod/Nodegroupeks-nodegroup/NodeGroupRole (prodNodegroupeksnodegroupNodeGroupRoleB635EB43)
0/15 | 12:51:09 PM | CREATE_IN_PROGRESS | AWS::IAM::Role | prod/Resource/CreationRole (prodCreationRole5E247585) Resource creation Initiated
0/15 | 12:51:09 PM | CREATE_IN_PROGRESS | AWS::IAM::Role | prod/Role (prodRoleD997707D) Resource creation Initiated
0/15 | 12:51:09 PM | CREATE_IN_PROGRESS | AWS::IAM::Role | prod/Nodegroupeks-nodegroup/NodeGroupRole (prodNodegroupeksnodegroupNodeGroupRoleB635EB43)
Resource creation Initiated
0/15 | 12:51:10 PM | CREATE_IN_PROGRESS | AWS::IAM::Role | eksadmin (eksadminBEE25D8E) Resource creation Initiated
2/15 | 12:51:11 PM | CREATE_IN_PROGRESS | AWS::CDK::Metadata | @aws-cdk--aws-eks.ClusterResourceProvider/CDKMetadata/Default (CDKMetadata) Resource creati
on Initiated
2/15 | 12:51:12 PM | CREATE_COMPLETE | AWS::CDK::Metadata | @aws-cdk--aws-eks.ClusterResourceProvider/CDKMetadata/Default (CDKMetadata)
2/15 | 12:51:13 PM | CREATE_IN_PROGRESS | AWS::EC2::SecurityGroup | prod/ControlPlaneSecurityGroup (prodControlPlaneSecurityGroup7CE46782) Resource creation In
itiated
2/15 | 12:51:15 PM | CREATE_COMPLETE | AWS::EC2::SecurityGroup | prod/ControlPlaneSecurityGroup (prodControlPlaneSecurityGroup7CE46782)
6/15 | 12:51:26 PM | CREATE_COMPLETE | AWS::IAM::Role | prod/Resource/CreationRole (prodCreationRole5E247585)
6/15 | 12:51:26 PM | CREATE_COMPLETE | AWS::IAM::Role | prod/Role (prodRoleD997707D)
6/15 | 12:51:27 PM | CREATE_COMPLETE | AWS::IAM::Role | prod/Nodegroupeks-nodegroup/NodeGroupRole (prodNodegroupeksnodegroupNodeGroupRoleB635EB43)
6/15 | 12:51:27 PM | CREATE_COMPLETE | AWS::IAM::Role | eksadmin (eksadminBEE25D8E)
6/15 | 12:51:30 PM | CREATE_IN_PROGRESS | AWS::CloudFormation::Stack | @aws-cdk--aws-eks.ClusterResourceProvider.NestedStack/@aws-cdk--aws-eks.ClusterResourceProv
ider.NestedStackResource (awscdkawseksClusterResourceProviderNestedStackawscdkawseksClusterResourceProviderNestedStackResource9827C454)
6/15 | 12:51:30 PM | CREATE_IN_PROGRESS | AWS::IAM::InstanceProfile | instanceprofile
6/15 | 12:51:31 PM | CREATE_IN_PROGRESS | AWS::IAM::Policy | prod/Resource/CreationRole/DefaultPolicy (prodCreationRoleDefaultPolicy46633E70)
6/15 | 12:51:31 PM | CREATE_IN_PROGRESS | AWS::CloudFormation::Stack | @aws-cdk--aws-eks.ClusterResourceProvider.NestedStack/@aws-cdk--aws-eks.ClusterResourceProv
ider.NestedStackResource (awscdkawseksClusterResourceProviderNestedStackawscdkawseksClusterResourceProviderNestedStackResource9827C454) Resource creation Initiated
6/15 | 12:51:31 PM | CREATE_IN_PROGRESS | AWS::IAM::InstanceProfile | instanceprofile Resource creation Initiated
6/15 | 12:51:32 PM | CREATE_IN_PROGRESS | AWS::IAM::Policy | prod/Resource/CreationRole/DefaultPolicy (prodCreationRoleDefaultPolicy46633E70) Resource c
reation Initiated
7/15 | 12:51:48 PM | CREATE_COMPLETE | AWS::IAM::Policy | prod/Resource/CreationRole/DefaultPolicy (prodCreationRoleDefaultPolicy46633E70)
7/15 Currently in progress: eks, awscdkawseksClusterResourceProviderNestedStackawscdkawseksClusterResourceProviderNestedStackResource9827C454, instanceprofile
8/15 | 12:53:32 PM | CREATE_COMPLETE | AWS::IAM::InstanceProfile | instanceprofile
8/15 Currently in progress: eks, awscdkawseksClusterResourceProviderNestedStackawscdkawseksClusterResourceProviderNestedStackResource9827C454
9/15 | 12:54:09 PM | CREATE_COMPLETE | AWS::CloudFormation::Stack | @aws-cdk--aws-eks.ClusterResourceProvider.NestedStack/@aws-cdk--aws-eks.ClusterResourceProv
ider.NestedStackResource (awscdkawseksClusterResourceProviderNestedStackawscdkawseksClusterResourceProviderNestedStackResource9827C454)
9/15 | 12:54:13 PM | CREATE_IN_PROGRESS | Custom::AWSCDK-EKS-Cluster | prod/Resource/Resource/Default (prod3363F4D9)
9/15 Currently in progress: eks, prod3363F4D9
10/15 | 1:07:36 PM | CREATE_IN_PROGRESS | Custom::AWSCDK-EKS-Cluster | prod/Resource/Resource/Default (prod3363F4D9) Resource creation Initiated
10/15 | 1:07:37 PM | CREATE_COMPLETE | Custom::AWSCDK-EKS-Cluster | prod/Resource/Resource/Default (prod3363F4D9)
11/15 | 1:07:41 PM | CREATE_IN_PROGRESS | AWS::SSM::Parameter | prod/KubectlReadyBarrier (prodKubectlReadyBarrier3183289D)
11/15 | 1:07:41 PM | CREATE_IN_PROGRESS | AWS::CloudFormation::Stack | @aws-cdk--aws-eks.KubectlProvider.NestedStack/@aws-cdk--aws-eks.KubectlProvider.NestedStackR
esource (awscdkawseksKubectlProviderNestedStackawscdkawseksKubectlProviderNestedStackResourceA7AEBA6B)
11/15 | 1:07:42 PM | CREATE_IN_PROGRESS | AWS::EKS::Nodegroup | prod/Nodegroupeks-nodegroup (prodNodegroupeksnodegroupE8147B13)
11/15 | 1:07:42 PM | CREATE_IN_PROGRESS | AWS::CloudFormation::Stack | @aws-cdk--aws-eks.KubectlProvider.NestedStack/@aws-cdk--aws-eks.KubectlProvider.NestedStackR
esource (awscdkawseksKubectlProviderNestedStackawscdkawseksKubectlProviderNestedStackResourceA7AEBA6B) Resource creation Initiated
11/15 | 1:07:42 PM | CREATE_IN_PROGRESS | AWS::SSM::Parameter | prod/KubectlReadyBarrier (prodKubectlReadyBarrier3183289D) Resource creation Initiated
11/15 | 1:07:43 PM | CREATE_COMPLETE | AWS::SSM::Parameter | prod/KubectlReadyBarrier (prodKubectlReadyBarrier3183289D)
11/15 | 1:07:46 PM | CREATE_IN_PROGRESS | AWS::EKS::Nodegroup | prod/Nodegroupeks-nodegroup (prodNodegroupeksnodegroupE8147B13) Resource creation Initiated
11/15 Currently in progress: eks, awscdkawseksKubectlProviderNestedStackawscdkawseksKubectlProviderNestedStackResourceA7AEBA6B, prodNodegroupeksnodegroupE8147B13
12/15 | 1:10:13 PM | CREATE_COMPLETE | AWS::EKS::Nodegroup | prod/Nodegroupeks-nodegroup (prodNodegroupeksnodegroupE8147B13)
12/15 Currently in progress: eks, awscdkawseksKubectlProviderNestedStackawscdkawseksKubectlProviderNestedStackResourceA7AEBA6B
13/15 | 1:12:57 PM | CREATE_COMPLETE | AWS::CloudFormation::Stack | @aws-cdk--aws-eks.KubectlProvider.NestedStack/@aws-cdk--aws-eks.KubectlProvider.NestedStackR
esource (awscdkawseksKubectlProviderNestedStackawscdkawseksKubectlProviderNestedStackResourceA7AEBA6B)
13/15 | 1:13:02 PM | CREATE_IN_PROGRESS | Custom::AWSCDK-EKS-KubernetesResource | prod/AwsAuth/manifest/Resource/Default (prodAwsAuthmanifest0B7F1A5F)
15/15 | 1:13:10 PM | CREATE_IN_PROGRESS | Custom::AWSCDK-EKS-KubernetesResource | prod/AwsAuth/manifest/Resource/Default (prodAwsAuthmanifest0B7F1A5F) Resource creation Initi
ated
15/15 | 1:13:10 PM | CREATE_COMPLETE | Custom::AWSCDK-EKS-KubernetesResource | prod/AwsAuth/manifest/Resource/Default (prodAwsAuthmanifest0B7F1A5F)
15/15 | 1:13:13 PM | CREATE_COMPLETE | AWS::CloudFormation::Stack | eks
✅ eks
Outputs:
eks.prodConfigCommand1B086551 = aws eks update-kubeconfig --name eks-cluster-demo --region eu-west-1 --role-arn arn:aws:iam::accountnumber:role/eks-cluster-role
eks.prodGetTokenCommand0CE8FF77 = aws eks get-token --cluster-name eks-cluster-demo --region eu-west-1 --role-arn arn:aws:iam::accountnumber:role/eks-cluster-role
Stack ARN:
arn:aws:cloudformation:eu-west-1:accountnumber:stack/eks/57cd8dd0-779a-11eb-9605-06cacdbbe755





That’s it. You can see that EKS cluster has been setup using AWS CDK (Python). You can find the EKSStack on my github repository here.
Hope you enjoyed the post.
Cheers
Ramasankar Molleti