├── stacks ├── __init__.py ├── compute_stack │ ├── __init__.py │ ├── compute_stack.py │ └── eks.py ├── data_stack │ ├── __init__.py │ ├── data_stack.py │ └── elasticsearch.py └── network_stack │ ├── __init__.py │ ├── security_group.py │ ├── network_stack.py │ └── vpc.py ├── tests ├── __init__.py └── unit │ ├── __init__.py │ └── test_network_stack.py ├── utils ├── __init__.py ├── stack_util.py └── config_util.py ├── cdk.version ├── requirements.txt ├── .flake8 ├── config ├── pro.yaml ├── dev.yaml └── common.yaml ├── cdk.context.json ├── .gitignore ├── cdk.json ├── Makefile ├── setup.py ├── app.py └── README.md /stacks/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cdk.version: -------------------------------------------------------------------------------- 1 | 1.82.0 2 | -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stacks/compute_stack/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stacks/data_stack/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stacks/network_stack/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | -e . 2 | pytest 3 | flake8 4 | autopep8 -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude = .git, __pycache__ 3 | max-line-length = 100 -------------------------------------------------------------------------------- /config/pro.yaml: -------------------------------------------------------------------------------- 1 | awsAccount: "558791111127" 2 | awsRegion: eu-west-3 3 | 4 | network: 5 | vpc: 6 | cidr: 10.11.128.0/18 7 | -------------------------------------------------------------------------------- /cdk.context.json: -------------------------------------------------------------------------------- 1 | { 2 | "availability-zones:account=558791111127:region=eu-west-2": [ 3 | "eu-west-2a", 4 | "eu-west-2b", 5 | "eu-west-2c" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | package-lock.json 3 | .pytest_cache 4 | *.egg-info 5 | 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | 11 | # Environments 12 | .env 13 | .venv 14 | env/ 15 | venv/ 16 | ENV/ 17 | env.bak/ 18 | venv.bak/ 19 | 20 | # CDK Context & Staging files 21 | .cdk.staging/ 22 | cdk.out/ -------------------------------------------------------------------------------- /cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "python3 app.py", 3 | "context": { 4 | "@aws-cdk/core:enableStackNameDuplicates": "true", 5 | "aws-cdk:enableDiffNoFail": "true", 6 | "@aws-cdk/core:stackRelativeExports": "true", 7 | "@aws-cdk/aws-ecr-assets:dockerIgnoreSupport": true, 8 | "@aws-cdk/aws-secretsmanager:parseOwnedSecretName": true, 9 | "@aws-cdk/aws-kms:defaultKeyPolicies": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/unit/test_network_stack.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from aws_cdk import core 4 | from stacks.network_stack import network_stack 5 | from utils import config_util 6 | 7 | 8 | def get_template(): 9 | app = core.App() 10 | network_stack.NetworkStack(app, "cdk-template", config_util.load_config("dev")) 11 | return json.dumps(app.synth().get_stack("cdk-template").template) 12 | 13 | 14 | def test_vpc_created(): 15 | assert("AWS::EC2::VPC" in get_template()) 16 | -------------------------------------------------------------------------------- /utils/stack_util.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | from aws_cdk.core import ( 3 | Tags, 4 | Stack 5 | ) 6 | 7 | 8 | # Add tags to each element of the stack. 9 | def add_tags_to_stack(stack: Stack, config: Dict) -> None: 10 | # Add common tags 11 | for tag_key in config['tags']: 12 | Tags.of(stack).add(key=tag_key, value=config['tags'][tag_key]) 13 | 14 | # Add environment in the tags 15 | Tags.of(stack).add(key='stage', value=config['stage']) 16 | -------------------------------------------------------------------------------- /config/dev.yaml: -------------------------------------------------------------------------------- 1 | awsAccount: "558791111127" 2 | awsRegion: eu-west-2 3 | 4 | network: 5 | vpc: 6 | cidr: 10.10.128.0/18 7 | 8 | data: 9 | elasticsearch: 10 | capacity: 11 | masterNodes: 12 | instanceType: t2.small.elasticsearch 13 | count: 3 14 | dataNodes: 15 | instanceType: t2.small.elasticsearch 16 | count: 3 17 | ebs: 18 | volumeSize: 10 19 | zoneAwareness: 20 | enabled: true 21 | count: 3 22 | logging: 23 | appLogEnabled: false 24 | auditLogEnabled: false 25 | slowIndexLogEnabled: false 26 | slowIearchLogEnabled: false 27 | -------------------------------------------------------------------------------- /stacks/network_stack/security_group.py: -------------------------------------------------------------------------------- 1 | from aws_cdk import ( 2 | core, 3 | aws_ec2 as ec2 4 | ) 5 | 6 | 7 | class SecurityGourp(core.Construct): 8 | _vpc: ec2.Vpc 9 | es_sg: ec2.SecurityGroup 10 | 11 | def __init__(self, scope: core.Construct, id: str, vpc: ec2.Vpc) -> None: 12 | super().__init__(scope, id) 13 | 14 | self._vpc = vpc 15 | self.__create_es_sg() 16 | 17 | # Create elasticsearch security group 18 | def __create_es_sg(self) -> ec2.SecurityGroup: 19 | self.es_sg = ec2.SecurityGroup( 20 | self, 'Elasticsearch', 21 | security_group_name='ElasticsearchSg', 22 | vpc=self._vpc, 23 | description='Elasticsearch security group', 24 | ) 25 | -------------------------------------------------------------------------------- /stacks/network_stack/network_stack.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from aws_cdk import ( 4 | core, 5 | aws_ec2 as ec2, 6 | ) 7 | from utils.stack_util import add_tags_to_stack 8 | from .vpc import Vpc 9 | from .security_group import SecurityGourp 10 | 11 | 12 | class NetworkStack(core.Stack): 13 | vpc: ec2.IVpc 14 | es_sg_id: str 15 | 16 | def __init__(self, scope: core.Construct, id: str, config: Dict, **kwargs) -> None: 17 | super().__init__(scope, id, **kwargs) 18 | 19 | # Apply common tags to stack resources. 20 | add_tags_to_stack(self, config) 21 | 22 | vpcConstruct = Vpc(self, 'Vpc', config) 23 | self.vpc = vpcConstruct.vpc 24 | 25 | sg = SecurityGourp(self, "SecurityGroups", self.vpc) 26 | self.es_sg_id = sg.es_sg.security_group_id 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LOCAL_VENV_NAME=.venv 2 | PYTHON=python3 3 | STAGE?=dev 4 | STACKS?=NetworkStack ComputeStack DataStack 5 | 6 | 7 | .PHONY: all test lint synth diff deploy 8 | 9 | local-venv: 10 | $(PYTHON) -m venv .venv 11 | 12 | install-dependencies: 13 | pip install -r requirements.txt 14 | 15 | lint: 16 | flake8 $(shell git ls-files '*.py') 17 | 18 | test: 19 | pytest 20 | 21 | synth: 22 | @cdk synth -c stage=$(STAGE) --output=cdk.out/$(STAGE) $(STACKS) 23 | 24 | deploy: synth 25 | @cdk deploy --app=cdk.out/$(STAGE) $(STACKS) 26 | 27 | diff: 28 | @cdk diff -c stage=$(STAGE) $(STACKS) 29 | 30 | destroy: 31 | @cdk destroy -c stage=$(STAGE) $(STACKS) 32 | 33 | bootstrapp-cdk-toolkit: 34 | @cdk bootstrap aws://$(shell cat config/$(STAGE).yaml | yq -r '.awsAccount')/$(shell cat config/$(STAGE).yaml | yq -r '.awsRegion') -c stage=$(STAGE) -------------------------------------------------------------------------------- /utils/config_util.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from benedict import benedict 4 | 5 | 6 | # Load configuration and merge common conf and specific stage config. 7 | def load_config(stage: str) -> Dict: 8 | # Load common file 9 | try: 10 | common_config = benedict.from_yaml('config/common.yaml') 11 | except ValueError: 12 | print('No config found in config/common.yaml.') 13 | common_config = benedict([]) 14 | 15 | # Load stage specific file 16 | try: 17 | env_config = benedict.from_yaml('config/' + stage + '.yaml') 18 | except ValueError: 19 | print('No config found in config/' + stage + '.yaml') 20 | env_config = benedict([]) 21 | 22 | # merge the configs 23 | common_config.merge(env_config) 24 | 25 | # extract stage to set env 26 | common_config['stage'] = stage 27 | 28 | return common_config 29 | -------------------------------------------------------------------------------- /stacks/compute_stack/compute_stack.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from aws_cdk import ( 4 | core, 5 | aws_ec2 as ec2, 6 | ) 7 | from utils.stack_util import add_tags_to_stack 8 | from .eks import Eks 9 | 10 | 11 | class ComputeStack(core.Stack): 12 | 13 | def __init__(self, scope: core.Construct, id: str, 14 | config: Dict, 15 | vpc: ec2.IVpc, 16 | es_sg_id: str, 17 | ** kwargs) -> None: 18 | super().__init__(scope, id, **kwargs) 19 | 20 | # Apply common tags to stack resources. 21 | add_tags_to_stack(self, config) 22 | 23 | # Get elasticsearch security group created in tne network stack 24 | es_sg = ec2.SecurityGroup.from_security_group_id( 25 | self, 26 | "ElasticsearchSG", 27 | security_group_id=es_sg_id 28 | ) 29 | 30 | # create the kubernetes cluster 31 | Eks(self, 'Eks', config, vpc, es_sg) 32 | -------------------------------------------------------------------------------- /stacks/data_stack/data_stack.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from aws_cdk import ( 4 | core, 5 | aws_ec2 as ec2, 6 | ) 7 | from utils.stack_util import add_tags_to_stack 8 | from .elasticsearch import Elasticsearch 9 | 10 | 11 | class DataStack(core.Stack): 12 | vpc: ec2.IVpc 13 | 14 | def __init__(self, scope: core.Construct, id: str, 15 | config: Dict, 16 | vpc: ec2.Vpc, 17 | es_sg_id: str, 18 | ** kwargs) -> None: 19 | super().__init__(scope, id, **kwargs) 20 | 21 | # Apply common tags to stack resources. 22 | add_tags_to_stack(self, config) 23 | 24 | # Get elasticsearch security group created in tne network stack 25 | es_sg = ec2.SecurityGroup.from_security_group_id( 26 | self, 27 | "ElasticsearchSG", 28 | security_group_id=es_sg_id 29 | ) 30 | 31 | # Create Elasticsearch cluster 32 | Elasticsearch(self, 'Vpc', config, vpc, es_sg) 33 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md") as fp: 4 | long_description = fp.read() 5 | 6 | 7 | def __read_cdk_version() -> str: 8 | f = open('cdk.version') 9 | return f.read() 10 | 11 | 12 | CDK_VERSION = __read_cdk_version() 13 | 14 | setuptools.setup( 15 | name="cdk_template", 16 | version="0.0.1", 17 | 18 | description="A sample CDK Python app", 19 | long_description=long_description, 20 | long_description_content_type="text/markdown", 21 | 22 | author="author", 23 | 24 | install_requires=[ 25 | "aws-cdk.core==" + CDK_VERSION, 26 | "aws-cdk.aws-ec2==" + CDK_VERSION, 27 | "aws-cdk.aws-eks==" + CDK_VERSION, 28 | "aws-cdk.aws-elasticsearch==" + CDK_VERSION, 29 | "python-benedict==0.22.4" 30 | ], 31 | 32 | python_requires=">=3.6", 33 | 34 | classifiers=[ 35 | "Development Status :: 4 - Beta", 36 | 37 | "Intended Audience :: Developers", 38 | 39 | "License :: OSI Approved :: Apache Software License", 40 | 41 | "Programming Language :: JavaScript", 42 | "Programming Language :: Python :: 3 :: Only", 43 | "Programming Language :: Python :: 3.6", 44 | "Programming Language :: Python :: 3.7", 45 | "Programming Language :: Python :: 3.8", 46 | 47 | "Topic :: Software Development :: Code Generators", 48 | "Topic :: Utilities", 49 | 50 | "Typing :: Typed", 51 | ], 52 | ) 53 | -------------------------------------------------------------------------------- /stacks/network_stack/vpc.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List 2 | 3 | from aws_cdk import ( 4 | core, 5 | aws_ec2 as ec2 6 | ) 7 | 8 | 9 | class Vpc(core.Construct): 10 | config: Dict 11 | vpc: ec2.Vpc 12 | subnet_configuration: List[ec2.SubnetConfiguration] = [] 13 | 14 | def __init__(self, scope: core.Construct, id: str, config: Dict) -> None: 15 | super().__init__(scope, id) 16 | self.config = config 17 | 18 | self.__build_subnets_config() 19 | self.__create_vpc() 20 | 21 | def __create_vpc(self): 22 | vpc_config = self.config['network']['vpc'] 23 | self.vpc = ec2.Vpc( 24 | scope=self, 25 | id=self.config['name'], 26 | subnet_configuration=self.subnet_configuration, 27 | max_azs=vpc_config['maxAzs'], 28 | cidr=vpc_config['cidr'], 29 | nat_gateway_subnets=ec2.SubnetSelection( 30 | subnet_group_name=vpc_config['natGatewaySubnetName'] 31 | ), 32 | enable_dns_hostnames=True, 33 | enable_dns_support=True, 34 | ) 35 | 36 | def __build_subnets_config(self): 37 | for subnet in self.config['network']['subnets']: 38 | self.subnet_configuration.append(ec2.SubnetConfiguration( 39 | name=subnet['name'], 40 | subnet_type=ec2.SubnetType[subnet['subnetType']], 41 | cidr_mask=subnet['cidrMask'] 42 | )) 43 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | from aws_cdk import core 5 | 6 | from stacks.network_stack.network_stack import NetworkStack 7 | from stacks.compute_stack.compute_stack import ComputeStack 8 | from stacks.data_stack.data_stack import DataStack 9 | from utils import config_util 10 | 11 | app = core.App() 12 | 13 | # Get target stage from cdk context 14 | stage = app.node.try_get_context('stage') 15 | if stage is None or stage == "unknown": 16 | sys.exit('You need to set the target stage.' 17 | ' USAGE: cdk -c stage=dev ') 18 | 19 | # Load stage config and set cdk environment 20 | config = config_util.load_config(stage) 21 | env = core.Environment(account=config['awsAccount'], 22 | region=config["awsRegion"], 23 | ) 24 | 25 | network_stack = NetworkStack(app, 26 | "NetworkStack", 27 | config=config, 28 | env=env 29 | ) 30 | 31 | ComputeStack(app, 32 | "ComputeStack", 33 | config=config, 34 | vpc=network_stack.vpc, 35 | es_sg_id=network_stack.es_sg_id, 36 | env=env 37 | ) 38 | 39 | DataStack(app, 40 | "DataStack", 41 | config=config, 42 | vpc=network_stack.vpc, 43 | es_sg_id=network_stack.es_sg_id, 44 | env=env 45 | ) 46 | 47 | app.synth() 48 | -------------------------------------------------------------------------------- /config/common.yaml: -------------------------------------------------------------------------------- 1 | # Common tags applied to all resources 2 | tags: 3 | cdk-stack: cdk-template 4 | team: my-team 5 | 6 | name: cdkTemplate 7 | 8 | network: 9 | vpc: 10 | natGatewaySubnetName: Public 11 | maxAzs: 3 12 | 13 | subnets: 14 | # Backend subnet 15 | - cidrMask: 21 16 | name: Private 17 | subnetType: PRIVATE 18 | # Data subnet 19 | - cidrMask: 22 20 | name: Data 21 | subnetType: PRIVATE 22 | # Public subnet 23 | - cidrMask: 21 24 | name: Public 25 | subnetType: PUBLIC 26 | 27 | compute: 28 | eks: 29 | eksOptimizedImage: STANDARD 30 | version: "1.18" 31 | nodeGroup: 32 | name: NodeGroup01 33 | instanceType: t3.medium 34 | maxCapacity: 3 35 | minCapacity: 1 36 | desiredCapacity: 1 37 | subnetGroupName: Private 38 | subnetGroupNames: 39 | - Private 40 | - Public 41 | 42 | data: 43 | elasticsearch: 44 | domainName: cdk-template 45 | version: "7.9" 46 | capacity: 47 | masterNodes: 48 | instanceType: m5.large.elasticsearch 49 | count: 3 50 | dataNodes: 51 | instanceType: c5.xlarge.elasticsearch 52 | count: 3 53 | ebs: 54 | volumeSize: 32 55 | zoneAwareness: 56 | enabled: true 57 | count: 3 58 | subnetGroupName: Data 59 | logging: 60 | appLogEnabled: true 61 | auditLogEnabled: true 62 | slowIndexLogEnabled: true 63 | slowIearchLogEnabled: true 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CDK template application 2 | 3 | This repository hosts a CDK project that provison a multi-stage AWS infrastructure composed of a VPC, a kubrnetes cluster and an elasticsearch cluster. 4 | 5 | Learn more about this project setup by reading this [article](https://medium.com/better-programming/how-to-organize-your-aws-cdk-project-f1c463aa966e). 6 | 7 | ## Dev tools: 8 | 9 | - aws cli 10 | - aws account 11 | - cdk cli 12 | - python3 13 | 14 | ## Stage bootstrapping 15 | 16 | Before trying to deploy a stage, go through these steps: 17 | 18 | - Create your stage config file under `config` dir. Then update its content according to your context (account id, cluster size, ...) 19 | 20 | - Allow ES AWS service to access your vpc resources by running this command: 21 | 22 | ```bash 23 | aws iam create-service-linked-role --aws-service-name es.amazonaws.com 24 | ``` 25 | 26 | - Bootstrap the cdk toolkit. This step is needed for the ComputeStack as we are using assets. 27 | 28 | ```bash 29 | make bootstrapp-cdk-toolkit 30 | ``` 31 | 32 | - Create a python virualenv and install dependencies using this command: 33 | 34 | ```bash 35 | make local-venv 36 | source .venv/bin/activate 37 | make install-dependencies 38 | ``` 39 | 40 | That's it! Now you are ready to provision your stage. 41 | 42 | ## Stage lifecycle 43 | 44 | - `make diff STAGE=pro` display cdk diff of all stacks of the pro stage 45 | - `make deploy STAGE=dev STACKS=NetworkStack` synthesize cloudformation template then deploy the NetworkStack to the dev stage 46 | - `make destroy` destroy the 3 stacks of the dev stage 47 | -------------------------------------------------------------------------------- /stacks/data_stack/elasticsearch.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from aws_cdk import ( 4 | core, 5 | aws_elasticsearch as es, 6 | aws_ec2 as ec2, 7 | aws_iam as iam, 8 | ) 9 | 10 | 11 | class Elasticsearch(core.Construct): 12 | 13 | def __init__(self, scope: core.Construct, id: str, 14 | config: Dict, 15 | vpc: ec2.Vpc, 16 | es_sg: ec2.SecurityGroup) -> None: 17 | super().__init__(scope, id) 18 | 19 | es_config = config['data']['elasticsearch'] 20 | 21 | # Build ES domain construct parameter 22 | capacity_config = es.CapacityConfig( 23 | master_node_instance_type=es_config['capacity']['masterNodes']['instanceType'], 24 | master_nodes=es_config['capacity']['masterNodes']['count'], 25 | data_node_instance_type=es_config['capacity']['dataNodes']['instanceType'], 26 | data_nodes=es_config['capacity']['dataNodes']['count'], 27 | ) 28 | 29 | vpc_options = es.VpcOptions( 30 | security_groups=[es_sg], 31 | subnets=vpc.select_subnets(subnet_group_name=es_config['subnetGroupName']).subnets, 32 | ) 33 | 34 | ebs_options = es.EbsOptions(volume_size=es_config['ebs']['volumeSize']) 35 | 36 | zone_awareness = es.ZoneAwarenessConfig( 37 | availability_zone_count=es_config['zoneAwareness']['count'], 38 | enabled=es_config['zoneAwareness']['enabled'], 39 | ) 40 | 41 | logging_options = es.LoggingOptions( 42 | app_log_enabled=es_config['logging']['appLogEnabled'], 43 | audit_log_enabled=es_config['logging']['auditLogEnabled'], 44 | slow_index_log_enabled=es_config['logging']['slowIndexLogEnabled'], 45 | slow_search_log_enabled=es_config['logging']['slowIearchLogEnabled'] 46 | ) 47 | 48 | access_policy = iam.PolicyStatement( 49 | effect=iam.Effect.ALLOW, 50 | principals=[iam.AnyPrincipal()], 51 | actions=['es:*'], 52 | resources=["arn:aws:es:" + config['awsRegion'] + ":" + 53 | config['awsAccount'] + ":domain/" + es_config['domainName'] + "/*"] 54 | 55 | ) 56 | 57 | # Create ES domain 58 | es.Domain(self, 'Domain', 59 | domain_name=es_config['domainName'], 60 | version=es.ElasticsearchVersion.of(es_config['version']), 61 | capacity=capacity_config, 62 | ebs=ebs_options, 63 | zone_awareness=zone_awareness, 64 | vpc_options=vpc_options, 65 | logging=logging_options, 66 | access_policies=[access_policy], 67 | ) 68 | -------------------------------------------------------------------------------- /stacks/compute_stack/eks.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | from http.client import HTTPS_PORT, HTTP_PORT 3 | 4 | from aws_cdk import ( 5 | core, 6 | aws_ec2 as ec2, 7 | aws_eks as eks, 8 | aws_iam as iam, 9 | ) 10 | 11 | 12 | class Eks(core.Construct): 13 | _config: Dict 14 | _cluster: eks.Cluster 15 | _node_group: eks.Nodegroup 16 | 17 | def __init__(self, scope: core.Construct, id: str, 18 | config: Dict, 19 | vpc: ec2.Vpc, 20 | es_sg: ec2.ISecurityGroup, 21 | ) -> None: 22 | super().__init__(scope, id) 23 | self._config = config 24 | 25 | # Create cluster control plane 26 | self.__create_eks_control_plane(vpc) 27 | 28 | # Create cluster worker nodes 29 | self.__create_eks_worker_nodes() 30 | self._node_group.node.add_dependency(self._cluster) 31 | 32 | # Update ES ingress security group by allowing traffic from EKS on port 443 and 80 33 | self.__update_es_sg(es_sg) 34 | 35 | def __create_eks_control_plane(self, vpc: ec2.Vpc) -> eks.Cluster: 36 | # This role is used to connect to the cluster with admin access 37 | # It is be associated to system:masters kubernetes RBAC group 38 | masters_role = iam.Role( 39 | self, 40 | 'eksClusterAdmin', 41 | role_name='eks-cluster-admin-'+self._config['stage'], 42 | assumed_by=iam.AccountRootPrincipal() 43 | ) 44 | 45 | # Control plane role 46 | # It provides permissions for the Kubernetes control plane 47 | # to make calls to AWS API operations on your behalf. 48 | role = self.__create_eks_control_plane_role() 49 | 50 | eks_config = self._config['compute']['eks'] 51 | self._cluster = eks.Cluster( 52 | scope=self, 53 | id="ControlPlane", 54 | cluster_name=self._config['name'], 55 | role=role, 56 | masters_role=masters_role, 57 | version=eks.KubernetesVersion.of(eks_config['version']), 58 | vpc=vpc, 59 | vpc_subnets=list( 60 | map(lambda group_name: ec2.SubnetSelection(subnet_group_name=group_name), 61 | eks_config['subnetGroupNames']) 62 | ), 63 | default_capacity=0, 64 | ) 65 | 66 | def __create_eks_worker_nodes(self): 67 | ng_config = self._config['compute']['eks']['nodeGroup'] 68 | self._node_group = eks.Nodegroup( 69 | scope=self, 70 | id=ng_config['name'], 71 | cluster=self._cluster, 72 | nodegroup_name=ng_config['name'], 73 | instance_types=[ec2.InstanceType(ng_config['instanceType'])], 74 | ami_type=eks.NodegroupAmiType.AL2_X86_64, 75 | min_size=ng_config['minCapacity'], 76 | max_size=ng_config['maxCapacity'], 77 | desired_size=ng_config['desiredCapacity'], 78 | subnets=ec2.SubnetSelection(subnet_group_name=ng_config['subnetGroupName']), 79 | node_role=self.__create_eks_nodegroup_role(), 80 | ) 81 | 82 | def __create_eks_nodegroup_role(self) -> iam.Role: 83 | worker_role = iam.Role( 84 | self, 85 | 'NodeGroupRole', 86 | role_name='eks-nodegroup-role-'+self._config['stage'], 87 | description='Allows EC2 instances to call AWS services on your behalf.', 88 | assumed_by=iam.ServicePrincipal('ec2.amazonaws.com') 89 | ) 90 | worker_role.add_managed_policy( 91 | iam.ManagedPolicy.from_aws_managed_policy_name('AmazonEKSWorkerNodePolicy')) 92 | worker_role.add_managed_policy( 93 | iam.ManagedPolicy.from_aws_managed_policy_name('AmazonEC2ContainerRegistryReadOnly')) 94 | worker_role.add_managed_policy( 95 | iam.ManagedPolicy.from_aws_managed_policy_name('AmazonEKS_CNI_Policy')) 96 | 97 | return worker_role 98 | 99 | def __create_eks_control_plane_role(self) -> iam.Role: 100 | eks_role = iam.Role( 101 | self, 102 | 'eksRole', 103 | role_name='eks-role-'+self._config['stage'], 104 | assumed_by=iam.ServicePrincipal('eks.amazonaws.com') 105 | ) 106 | eks_role.add_managed_policy( 107 | iam.ManagedPolicy.from_aws_managed_policy_name('AmazonEKSServicePolicy')) 108 | eks_role.add_managed_policy( 109 | iam.ManagedPolicy.from_aws_managed_policy_name('AmazonEKSClusterPolicy')) 110 | 111 | return eks_role 112 | 113 | def __update_es_sg(self, es_sg: ec2.ISecurityGroup): 114 | cluster_sg = ec2.SecurityGroup.from_security_group_id( 115 | self, 116 | "ClusterSg", 117 | security_group_id=self._cluster.cluster_security_group_id 118 | ) 119 | 120 | es_sg.add_ingress_rule( 121 | peer=cluster_sg, 122 | connection=ec2.Port.tcp(HTTPS_PORT), 123 | description='Accept traffic from the eks cluster in https' 124 | ) 125 | es_sg.add_ingress_rule( 126 | peer=cluster_sg, 127 | connection=ec2.Port.tcp(HTTP_PORT), 128 | description='Accept traffic from the eks cluster in http' 129 | ) 130 | --------------------------------------------------------------------------------