Skip to content

Instantly share code, notes, and snippets.

@kalda341
Created April 6, 2020 03:12
Show Gist options
  • Select an option

  • Save kalda341/dcab98ef96907b61ffccac3f85ed0a03 to your computer and use it in GitHub Desktop.

Select an option

Save kalda341/dcab98ef96907b61ffccac3f85ed0a03 to your computer and use it in GitHub Desktop.
import string
import ipaddress
from troposphere import (
AWS_REGION,
Join,
Ref,
GetAtt,
Output,
Template,
Export,
Sub,
)
from troposphere.ec2 import (
VPC, Subnet, SecurityGroup,
SecurityGroupIngress,
RouteTable, VPCEndpoint, SubnetRouteTableAssociation,
Route, InternetGateway, Instance,
VPCGatewayAttachment, EIP
)
def build(vpc_cidr=ipaddress.ip_network('10.0.0.0/16'), subnet_prefix=24, zones=2, include_public_subnet=True):
template = Template()
vpc = template.add_resource(VPC(
'VPC',
CidrBlock=str(vpc_cidr),
InstanceTenancy='default',
EnableDnsSupport=True,
EnableDnsHostnames=True
))
# Internet gateway
internet_gateway = template.add_resource(InternetGateway(
'InternetGateway'
))
template.add_resource(VPCGatewayAttachment(
'VPCGatewayAttachment',
VpcId=Ref(vpc),
InternetGatewayId=Ref(internet_gateway),
))
current_address = vpc_cidr[0]
private_subnets = []
private_cidrs = []
public_subnets = []
public_cidrs = []
for zone_index in range(zones):
zone = string.ascii_lowercase[zone_index]
##########################################
# Private
##########################################
private_cidr = ipaddress.ip_network('/'.join([str(current_address), str(subnet_prefix)]))
current_address = current_address + 2 ** (32 - private_cidr.prefixlen)
private_subnet_name = 'PrivateSubnetZone' + zone.upper()
private_subnet = template.add_resource(Subnet(
private_subnet_name,
VpcId=Ref(vpc),
CidrBlock=str(private_cidr),
AvailabilityZone=Join('', [
Ref(AWS_REGION),
zone,
])
))
private_subnets.append(private_subnet)
private_cidrs.append(private_cidr)
# Assign route table
private_route_table = template.add_resource(RouteTable(
private_subnet_name + 'RouteTable',
VpcId=Ref(vpc)
))
template.add_resource(SubnetRouteTableAssociation(
private_subnet_name + 'RouteTableAssociation',
SubnetId=Ref(private_subnet),
RouteTableId=Ref(private_route_table)
))
# Allows us to access S3 from our private subnet without an internet connection
template.add_resource(VPCEndpoint(
private_subnet_name + 'S3VpcEndpoint',
RouteTableIds=[Ref(private_route_table)],
ServiceName=Join('', [
'com.amazonaws.',
Ref(AWS_REGION),
'.s3'
]),
VpcId=Ref(vpc),
))
##########################################
# Public
##########################################
if include_public_subnet:
public_cidr = ipaddress.ip_network('/'.join([str(current_address), str(subnet_prefix)]))
current_address = current_address + 2 ** (32 - public_cidr.prefixlen)
public_subnet_name = 'PublicSubnetZone' + zone.upper()
public_subnet = template.add_resource(Subnet(
public_subnet_name,
VpcId=Ref(vpc),
CidrBlock=str(public_cidr),
AvailabilityZone=Join('', [
Ref(AWS_REGION),
zone,
])
))
public_subnets.append(public_subnet)
public_cidrs.append(public_cidr)
# Public route table
public_route_table = template.add_resource(RouteTable(
public_subnet_name + 'RouteTable',
VpcId=Ref(vpc)
))
default_public_route = template.add_resource(Route(
public_subnet_name + 'DefaultRoute',
GatewayId=Ref(internet_gateway),
DestinationCidrBlock='0.0.0.0/0',
RouteTableId=Ref(public_route_table),
# Required because AWS is dodgy :P - https://forums.aws.amazon.com/thread.jspa?threadID=100750
DependsOn=[internet_gateway.title]
))
template.add_resource(SubnetRouteTableAssociation(
public_subnet_name + 'RouteTableAssociation',
SubnetId=Ref(public_subnet),
RouteTableId=Ref(public_route_table)
))
nat_name = public_subnet_name + 'NatInstance'
# Adds an internet connection to our public subnet
nat_sg = template.add_resource(SecurityGroup(
public_subnet_name + 'NatSG',
VpcId=Ref(vpc),
GroupDescription='Security group for NAT host'
))
nat = template.add_resource(Instance(
nat_name,
SourceDestCheck='false',
SecurityGroupIds=[Ref(nat_sg)],
SubnetId=Ref(public_subnet),
# amzn-ami-vpc-nat-hvm-2018.03.0.20180811-x86_64-ebs
ImageId='ami-00c1445796bc0a29f',
InstanceType='t2.micro'
))
# Elastic public IP for our nat instance
template.add_resource(EIP(
nat_name + 'EIP',
InstanceId=Ref(nat),
Domain='vpc',
))
##########################################
# Private -> Public
##########################################
# Add an entry in the private route table to allow using this NAT instance
template.add_resource(Route(
public_subnet_name + 'DefaultRouteThrough' + nat_name,
InstanceId=Ref(nat),
DestinationCidrBlock='0.0.0.0/0',
RouteTableId=Ref(private_route_table),
# Required because AWS is dodgy :P - https://forums.aws.amazon.com/thread.jspa?threadID=100750
DependsOn=[internet_gateway.title]
))
# Allow this subnet to access the following ports on the public internet
for port, protocol in [(25, 'tcp'), (80, 'tcp'), (443, 'tcp')]:
template.add_resource(SecurityGroupIngress(
'{}NatPrivate{}Port{}Ingress'.format(private_subnet_name, protocol, port),
ToPort=str(port),
FromPort=str(port),
IpProtocol=protocol,
GroupId=Ref(nat_sg),
CidrIp=str(private_cidr)
))
template.add_output(Output(
'VPC',
Value=Ref(vpc),
Description='The VPC',
Export=Export(Sub('${AWS::StackName}-VPC')),
))
template.add_output(Output(
'PublicSubnets',
Value=Join(',', [Ref(x) for x in public_subnets]),
Description='The public subnets',
Export=Export(Sub('${AWS::StackName}-PublicSubnets')),
))
template.add_output(Output(
'PublicCIDRs',
Value=Join(',', [str(x) for x in public_cidrs]),
Description='The public subnets blocks',
Export=Export(Sub('${AWS::StackName}-PublicCIDRs')),
))
template.add_output(Output(
'PrivateSubnets',
Value=Join(',', [Ref(x) for x in private_subnets]),
Description='The private subnets',
Export=Export(Sub('${AWS::StackName}-PrivateSubnets')),
))
template.add_output(Output(
'PrivateCIDRs',
Value=Join(',', [str(x) for x in private_cidrs]),
Description='The private subnet blocks',
Export=Export(Sub('${AWS::StackName}-PrivateCIDRs')),
))
return template
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment