Skip to content

Commit

Permalink
feat: add VPC, EC2 Python Example (aws-samples#204)
Browse files Browse the repository at this point in the history
This pull request is aimed at creating the following Python Examples:
- Import VPC
- Create new VPC with 2 AZs and 6 Subnets, Public, Private and DataBase.
- Sharing VPC between two stacks
- Create ALB/EC2/AutoscalingGroup in the VPC
- How to define EC2 to specify AMI or auto-selection
- Define property override for EC2 Instance with BlockStorageMapping
- Define userdata for EC2 to setup httpd
- Create NAT GW and Bastion 
- Chain the Security Groups
  • Loading branch information
huangzbaws authored and NGL321 committed Jan 21, 2020
1 parent 9ba1cf1 commit a88f312
Show file tree
Hide file tree
Showing 18 changed files with 409 additions and 0 deletions.
1 change: 1 addition & 0 deletions python/existing-vpc-new-ec2-ebs-userdata/DO_NOT_AUTOTEST
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

17 changes: 17 additions & 0 deletions python/existing-vpc-new-ec2-ebs-userdata/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Create EC2 in an existing VPC with AWS CDK Python

This is a project to create a new EC2 in an existing VPC on AWS with the AWS Cloud Development Kit.

This project also demonstrates:
* Using customized user data of EC2
* Customize multiple EBS volume
* Specify AMI id
* Security groups allow SSH access from internet

## Useful commands

* `cdk ls` list all stacks in the app
* `cdk synth` emits the synthesized CloudFormation template
* `cdk deploy` deploy this stack to your default AWS account/region
* `cdk diff` compare deployed stack with current state
* `cdk docs` open CDK documentation
13 changes: 13 additions & 0 deletions python/existing-vpc-new-ec2-ebs-userdata/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env python3

from aws_cdk import core

from cdk_vpc_ec2.cdk_vpc_ec2_stack import CdkVpcEc2Stack

# Define your account id to make import vpc work
env_cn = core.Environment(account="YOUR_ACCOUNT_ID_WITHOUT_HYPHEN", region="cn-northwest-1")

app = core.App()
CdkVpcEc2Stack(app, "cdk-vpc-ec2", env=env_cn)

app.synth()
3 changes: 3 additions & 0 deletions python/existing-vpc-new-ec2-ebs-userdata/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"app": "python3 app.py"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from aws_cdk import core
import aws_cdk.aws_ec2 as ec2

vpc_id = "MY-VPC-ID" # Import an Exist VPC
ec2_type = "t2.micro"
key_name = "id_rsa"
linux_ami = ec2.GenericLinuxImage({
"cn-northwest-1": "AMI-ID-IN-cn-northwest-1-REGION", # Refer to an Exist AMI
"eu-west-1": "AMI-ID-IN-eu-west-1-REGION"
})
with open("./user_data/user_data.sh") as f:
user_data = f.read()


class CdkVpcEc2Stack(core.Stack):

def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)

# The code that defines your stack goes here
vpc = ec2.Vpc.from_lookup(self, "VPC", vpc_id=vpc_id)

host = ec2.Instance(self, "myEC2",
instance_type=ec2.InstanceType(
instance_type_identifier=ec2_type),
instance_name="mySingleHost",
machine_image=linux_ami,
vpc=vpc,
key_name=key_name,
vpc_subnets=ec2.SubnetSelection(
subnet_type=ec2.SubnetType.PUBLIC),
user_data=ec2.UserData.custom(user_data)
)
# ec2.Instance has no property of BlockDeviceMappings, add via lower layer cdk api:
host.instance.add_property_override("BlockDeviceMappings", [{
"DeviceName": "/dev/xvda",
"Ebs": {
"VolumeSize": "10",
"VolumeType": "io1",
"Iops": "150",
"DeleteOnTermination": "true"
}
}, {
"DeviceName": "/dev/sdb",
"Ebs": {"VolumeSize": "30"}
}
]) # by default VolumeType is gp2, VolumeSize 8GB
host.connections.allow_from_any_ipv4(
ec2.Port.tcp(22), "Allow ssh from internet")
host.connections.allow_from_any_ipv4(
ec2.Port.tcp(80), "Allow ssh from internet")

core.CfnOutput(self, "Output",
value=host.instance_public_ip)
1 change: 1 addition & 0 deletions python/existing-vpc-new-ec2-ebs-userdata/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-e .
46 changes: 46 additions & 0 deletions python/existing-vpc-new-ec2-ebs-userdata/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import setuptools


with open("README.md") as fp:
long_description = fp.read()


setuptools.setup(
name="Import_VPC_Create_EC2",
version="1.0.0",

description="Import VPC and Create EC2 on it with two EBS and EC2 UserData",
long_description=long_description,
long_description_content_type="text/markdown",

author="Huang, Zhuobin (James)",

package_dir={"": "cdk_vpc_ec2"},
packages=setuptools.find_packages(where="cdk_vpc_ec2"),

install_requires=[
"aws-cdk.core",
"aws-cdk.aws-ec2"
],

python_requires=">=3.6",

classifiers=[
"Development Status :: 4 - Beta",

"Intended Audience :: Developers",

"License :: OSI Approved :: Apache Software License",

"Programming Language :: JavaScript",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",

"Topic :: Software Development :: Code Generators",
"Topic :: Utilities",

"Typing :: Typed",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash
sudo yum update -y
sudo yum -y install httpd php
sudo chkconfig httpd on
sudo service httpd start
24 changes: 24 additions & 0 deletions python/new-vpc-alb-asg-mysql/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Create VPC, EC2 ASG and RDS with AWS CDK Python

This is a project to create a new VPC, EC2 autoscaling group and RDS on AWS with the AWS Cloud Development Kit.

This project also demonstrates:
* Create VPC in 3 tier layers of subnets: PUBLIC, PRIVATE and ISOLATED, you can specify the number of AZ and the CIDR.
* Create Bastion instance, NAT Gateway and S3 endpoint
* Create ALB, EC2 Autoscaling group with scaling policy and customize EBS volume
* Creat RDS MySQL M-AZs Database or Aurora
* Create security group and allow access from the other security group: Internet -> ALB -> EC2ASG -> RDS
* Using customized user data of EC2 and specify generation AMI property and do not need to specify the AMI id in every region

## Architeture
![Architecture](./img_demo_cdk_vpc.png)

This project create the new VPC part of the architeture. For the existing VPC part, please refer to the project in aws-cdk-examples/existing-vpc-new-ec2-ebs-userdata

## Useful commands

* `cdk ls` list all stacks in the app
* `cdk synth` emits the synthesized CloudFormation template
* `cdk deploy` deploy this stack to your default AWS account/region
* `cdk diff` compare deployed stack with current state
* `cdk docs` open CDK documentation
18 changes: 18 additions & 0 deletions python/new-vpc-alb-asg-mysql/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env python3

from aws_cdk import core

from cdk_vpc_ec2.cdk_vpc_stack import CdkVpcStack
from cdk_vpc_ec2.cdk_ec2_stack import CdkEc2Stack
from cdk_vpc_ec2.cdk_rds_stack import CdkRdsStack

app = core.App()

vpc_stack = CdkVpcStack(app, "cdk-vpc")
ec2_stack = CdkEc2Stack(app, "cdk-ec2",
vpc=vpc_stack.vpc)
rds_stack = CdkRdsStack(app, "cdk-rds",
vpc=vpc_stack.vpc,
asg_security_groups=ec2_stack.asg.connections.security_groups)

app.synth()
3 changes: 3 additions & 0 deletions python/new-vpc-alb-asg-mysql/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"app": "python3 app.py"
}
82 changes: 82 additions & 0 deletions python/new-vpc-alb-asg-mysql/cdk_vpc_ec2/cdk_ec2_stack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
from aws_cdk import core
import aws_cdk.aws_ec2 as ec2
import aws_cdk.aws_elasticloadbalancingv2 as elb
import aws_cdk.aws_autoscaling as autoscaling

ec2_type = "t2.micro"
key_name = "id_rsa" # Setup key_name for EC2 instance login
linux_ami = ec2.AmazonLinuxImage(generation=ec2.AmazonLinuxGeneration.AMAZON_LINUX,
edition=ec2.AmazonLinuxEdition.STANDARD,
virtualization=ec2.AmazonLinuxVirt.HVM,
storage=ec2.AmazonLinuxStorage.GENERAL_PURPOSE
) # Indicate your AMI, no need a specific id in the region
with open("./user_data/user_data.sh") as f:
user_data = f.read()


class CdkEc2Stack(core.Stack):

def __init__(self, scope: core.Construct, id: str, vpc, **kwargs) -> None:
super().__init__(scope, id, **kwargs)

# Create Bastion
bastion = ec2.BastionHostLinux(self, "myBastion",
vpc=vpc,
subnet_selection=ec2.SubnetSelection(
subnet_type=ec2.SubnetType.PUBLIC),
instance_name="myBastionHostLinux",
instance_type=ec2.InstanceType(instance_type_identifier="t2.micro"))

# Setup key_name for EC2 instance login if you don't use Session Manager
# bastion.instance.instance.add_property_override("KeyName", key_name)

bastion.connections.allow_from_any_ipv4(
ec2.Port.tcp(22), "Internet access SSH")

# Create ALB
alb = elb.ApplicationLoadBalancer(self, "myALB",
vpc=vpc,
internet_facing=True,
load_balancer_name="myALB"
)
alb.connections.allow_from_any_ipv4(
ec2.Port.tcp(80), "Internet access ALB 80")
listener = alb.add_listener("my80",
port=80,
open=True)

# Create Autoscaling Group with fixed 2*EC2 hosts
self.asg = autoscaling.AutoScalingGroup(self, "myASG",
vpc=vpc,
vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PRIVATE),
instance_type=ec2.InstanceType(instance_type_identifier=ec2_type),
machine_image=linux_ami,
key_name=key_name,
user_data=ec2.UserData.custom(user_data),
desired_capacity=2,
min_capacity=2,
max_capacity=2,
# block_devices=[
# autoscaling.BlockDevice(
# device_name="/dev/xvda",
# volume=autoscaling.BlockDeviceVolume.ebs(
# volume_type=autoscaling.EbsDeviceVolumeType.GP2,
# volume_size=12,
# delete_on_termination=True
# )),
# autoscaling.BlockDevice(
# device_name="/dev/sdb",
# volume=autoscaling.BlockDeviceVolume.ebs(
# volume_size=20)
# # 20GB, with default volume_type gp2
# )
# ]
)

self.asg.connections.allow_from(alb, ec2.Port.tcp(80), "ALB access 80 port of EC2 in Autoscaling Group")
listener.add_targets("addTargetGroup",
port=80,
targets=[self.asg])

core.CfnOutput(self, "Output",
value=alb.load_balancer_dns_name)
53 changes: 53 additions & 0 deletions python/new-vpc-alb-asg-mysql/cdk_vpc_ec2/cdk_rds_stack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from aws_cdk import core
import aws_cdk.aws_ec2 as ec2
import aws_cdk.aws_rds as rds


class CdkRdsStack(core.Stack):

def __init__(self, scope: core.Construct, id: str, vpc, asg_security_groups, **kwargs) -> None:
super().__init__(scope, id, **kwargs)

# Ceate Aurora Cluster with 2 instances with CDK High Level API
# Secrets Manager auto generate and keep the password, don't put password in cdk code directly
# db_Aurora_cluster = rds.DatabaseCluster(self, "MyAurora",
# default_database_name="MyAurora",
# engine=rds.DatabaseClusterEngine.AURORA_MYSQL,
# engine_version="5.7.12",
# master_user=rds.Login(username="admin"),
# instance_props=rds.InstanceProps(
# vpc=vpc,
# vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.ISOLATED),
# instance_type=ec2.InstanceType(instance_type_identifier="t2.small")
# ),
# instances=2,
# parameter_group=rds.ClusterParameterGroup.from_parameter_group_name(
# self, "para-group-aurora",
# parameter_group_name="default.aurora-mysql5.7"
# ),
# )
# for asg_sg in asg_security_groups:
# db_Aurora_cluster.connections.allow_default_port_from(asg_sg, "EC2 Autoscaling Group access Aurora")

# Alternatively, create MySQL RDS with CDK High Level API
db_mysql_easy = rds.DatabaseInstance(self, "MySQL_DB_easy",
engine=rds.DatabaseInstanceEngine.MYSQL,
engine_version="5.7.22",
instance_class=ec2.InstanceType.of(
ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL),
master_username="admin",
vpc=vpc,
multi_az=True,
allocated_storage=100,
storage_type=rds.StorageType.GP2,
cloudwatch_logs_exports=["audit", "error", "general", "slowquery"],
deletion_protection=False,
delete_automated_backups=False,
backup_retention=core.Duration.days(7),
parameter_group=rds.ParameterGroup.from_parameter_group_name(
self, "para-group-mysql",
parameter_group_name="default.mysql5.7"
)
)
for asg_sg in asg_security_groups:
db_mysql_easy.connections.allow_default_port_from(asg_sg, "EC2 Autoscaling Group access MySQL")
34 changes: 34 additions & 0 deletions python/new-vpc-alb-asg-mysql/cdk_vpc_ec2/cdk_vpc_stack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from aws_cdk import core
import aws_cdk.aws_ec2 as ec2


class CdkVpcStack(core.Stack):

def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)

# The code that defines your stack goes here

self.vpc = ec2.Vpc(self, "VPC",
max_azs=2,
cidr="10.10.0.0/16",
# configuration will create 3 groups in 2 AZs = 6 subnets.
subnet_configuration=[ec2.SubnetConfiguration(
subnet_type=ec2.SubnetType.PUBLIC,
name="Public",
cidr_mask=24
), ec2.SubnetConfiguration(
subnet_type=ec2.SubnetType.PRIVATE,
name="Private",
cidr_mask=24
), ec2.SubnetConfiguration(
subnet_type=ec2.SubnetType.ISOLATED,
name="DB",
cidr_mask=24
)
],
# nat_gateway_provider=ec2.NatProvider.gateway(),
nat_gateways=2,
)
core.CfnOutput(self, "Output",
value=self.vpc.vpc_id)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions python/new-vpc-alb-asg-mysql/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-e .
Loading

0 comments on commit a88f312

Please sign in to comment.