Skip to content

Instantly share code, notes, and snippets.

@atheiman
Last active November 3, 2020 06:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atheiman/70679d300b8d94ee6414a6629b88a9cc to your computer and use it in GitHub Desktop.
Save atheiman/70679d300b8d94ee6414a6629b88a9cc to your computer and use it in GitHub Desktop.
VPC with generated CIDR block determined by AWS account ID + Region. 4 subnets (Public/Private, 2 AZs).
Description: >
Builds a basic /24 2x2 VPC (Public/Private, 2 AZs). The VPC CIDR block is determined by a combination
of the account ID and Region, giving a _very strong_ probability of a unique range within an
Organization.
Parameters:
VpcNameTag:
Type: String
Default: 2x2
VpcCidrSuffix:
Type: String
Default: '.0/24'
Description: >
CIDR block to assign to the VPC (first three octets will be prepended).
AvailabilityZone1NameSuffix:
Type: String
Default: 'a'
AllowedValues: [a, b, c, d, e, f]
Description: >
Will be appended to ${AWS::Region} to build an Availability Zone name. Example: 'a' in Region
'us-east-1' builds the Availability Zone name 'us-east-1a'.
AvailabilityZone2NameSuffix:
Type: String
Default: 'b'
AllowedValues: [a, b, c, d, e, f]
Description: >
Will be appended to ${AWS::Region} to build an Availability Zone name. Example: 'b' in Region
'us-east-1' builds the Availability Zone name 'us-east-1b'.
SubnetPrivate1CidrSuffix:
Type: String
Default: '.0/26'
Description: >
CIDR block to assign to the Private subnet in Availability Zone 1 (first three octets will be prepended).
SubnetPrivate2CidrSuffix:
Type: String
Default: '.64/26'
Description: >
CIDR block to assign to the Private subnet in Availability Zone 2 (first three octets will be prepended).
SubnetPublic1CidrSuffix:
Type: String
Default: '.128/27'
Description: >
CIDR block to assign to the Public subnet in Availability Zone 1 (first three octets will be prepended).
SubnetPublic2CidrSuffix:
Type: String
Default: '.160/27'
Description: >
CIDR block to assign to the Public subnet in Availability Zone 2 (first three octets will be prepended).
MultiAzNatGateway:
Type: String
Default: 'False'
AllowedValues: ['True', 'False']
Description: >
If True, a NAT Gateway will be deployed in both subnets. This adds cost, but adds high availability to the
network.
TransitGatewayId:
Type: String
Default: ''
Description: >
Transit Gateway ID to attach to the VPC. If left blank, no Transit Gateway will be attached to the VPC.
TransitGatewayRouteCidr:
Type: String
Default: '10.0.0.0/8'
AllowedPattern: '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}'
Description: >
CIDR block to route traffic to the attached Transit Gateway.
Conditions:
TransitGatewayIdProvided: !Not [!Equals [!Ref TransitGatewayId, '']]
cMultiAzNatGateway: !Equals [!Ref MultiAzNatGateway, 'True']
TransitGatewayIdProvidedAndMultiAzNatGateway: !And
- Condition: TransitGatewayIdProvided
- Condition: cMultiAzNatGateway
Resources:
#######
# VPC #
#######
Vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Sub '${CidrUtil.CidrPrefix}${VpcCidrSuffix}'
EnableDnsHostnames: True
EnableDnsSupport: True
Tags:
- Key: Name
Value: !Sub '${VpcNameTag}-${CidrUtil.CidrPrefix}${VpcCidrSuffix}'
###########
# Subnets #
###########
SubnetPublic1:
Type: AWS::EC2::Subnet
Properties:
# TODO: Availability Zone name parameter rather than hardcoded a / b
AvailabilityZone: !Sub '${AWS::Region}${AvailabilityZone1NameSuffix}'
CidrBlock: !Sub '${CidrUtil.CidrPrefix}${SubnetPublic1CidrSuffix}'
MapPublicIpOnLaunch: True
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: !Sub '${VpcNameTag}-Public-${AvailabilityZone1NameSuffix}-${CidrUtil.CidrPrefix}${SubnetPublic1CidrSuffix}'
SubnetPublic2:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Sub '${AWS::Region}${AvailabilityZone2NameSuffix}'
CidrBlock: !Sub '${CidrUtil.CidrPrefix}${SubnetPublic2CidrSuffix}'
MapPublicIpOnLaunch: True
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: !Sub '${VpcNameTag}-Public-${AvailabilityZone2NameSuffix}-${CidrUtil.CidrPrefix}${SubnetPublic2CidrSuffix}'
SubnetPrivate1:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Sub '${AWS::Region}${AvailabilityZone1NameSuffix}'
CidrBlock: !Sub '${CidrUtil.CidrPrefix}${SubnetPrivate1CidrSuffix}'
MapPublicIpOnLaunch: False
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: !Sub '${VpcNameTag}-Private-${AvailabilityZone1NameSuffix}-${CidrUtil.CidrPrefix}${SubnetPrivate1CidrSuffix}'
SubnetPrivate2:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Sub '${AWS::Region}${AvailabilityZone2NameSuffix}'
CidrBlock: !Sub '${CidrUtil.CidrPrefix}${SubnetPrivate2CidrSuffix}'
MapPublicIpOnLaunch: False
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: !Sub '${VpcNameTag}-Private-${AvailabilityZone2NameSuffix}-${CidrUtil.CidrPrefix}${SubnetPrivate2CidrSuffix}'
###############
# NAT Gateway #
###############
NatGateway1Eip:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Tags:
- Key: Name
Value: !Sub '${VpcNameTag}-NatGateway-${AvailabilityZone1NameSuffix}'
NatGateway1:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !Sub '${NatGateway1Eip.AllocationId}'
SubnetId: !Ref SubnetPublic1
Tags:
- Key: Name
Value: !Sub '${VpcNameTag}-${AvailabilityZone1NameSuffix}'
NatGateway2Eip:
Type: AWS::EC2::EIP
Condition: cMultiAzNatGateway
Properties:
Domain: vpc
Tags:
- Key: Name
Value: !Sub '${VpcNameTag}-NatGateway-${AvailabilityZone2NameSuffix}'
NatGateway2:
Type: AWS::EC2::NatGateway
Condition: cMultiAzNatGateway
Properties:
AllocationId: !Sub '${NatGateway2Eip.AllocationId}'
SubnetId: !Ref SubnetPublic2
Tags:
- Key: Name
Value: !Sub '${VpcNameTag}-${AvailabilityZone2NameSuffix}'
####################
# Internet Gateway #
####################
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Ref VpcNameTag
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref Vpc
######################
# Public Route Table #
######################
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: !Sub '${VpcNameTag}-Public'
PublicDefaultRoute:
Type: AWS::EC2::Route
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: '0.0.0.0/0'
GatewayId: !Ref InternetGateway
SubnetPublic1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref SubnetPublic1
SubnetPublic2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref SubnetPublic2
########################
# Private Route Tables #
########################
Private1RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: !Sub '${VpcNameTag}-Private-${AvailabilityZone1NameSuffix}'
Private1DefaultRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref Private1RouteTable
DestinationCidrBlock: '0.0.0.0/0'
NatGatewayId: !Ref NatGateway1
SubnetPrivate1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref Private1RouteTable
SubnetId: !Ref SubnetPrivate1
Private2RouteTable:
Type: AWS::EC2::RouteTable
Condition: cMultiAzNatGateway
Properties:
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: !Sub '${VpcNameTag}-Private-${AvailabilityZone2NameSuffix}'
Private2DefaultRoute:
Type: AWS::EC2::Route
Condition: cMultiAzNatGateway
Properties:
RouteTableId: !Ref Private2RouteTable
DestinationCidrBlock: '0.0.0.0/0'
NatGatewayId: !Ref NatGateway2
SubnetPrivate2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !If [cMultiAzNatGateway, !Ref Private2RouteTable, !Ref Private1RouteTable]
SubnetId: !Ref SubnetPrivate2
###################
# Transit Gateway #
###################
TransitGatewayAttachment:
Type: AWS::EC2::TransitGatewayAttachment
Condition: TransitGatewayIdProvided
Properties:
VpcId: !Ref Vpc
TransitGatewayId: !Ref TransitGatewayId
SubnetIds: [!Ref SubnetPrivate1, !Ref SubnetPrivate2]
Tags:
- Key: Name
Value: !Sub '${VpcNameTag}-${TransitGatewayId}'
Private1TransitGatewayRoute:
Type: AWS::EC2::Route
DependsOn: TransitGatewayAttachment
Condition: TransitGatewayIdProvided
Properties:
RouteTableId: !Ref Private1RouteTable
DestinationCidrBlock: !Ref TransitGatewayRouteCidr
TransitGatewayId: !Ref TransitGatewayId
Private2TransitGatewayRoute:
Type: AWS::EC2::Route
DependsOn: TransitGatewayAttachment
Condition: TransitGatewayIdProvidedAndMultiAzNatGateway
Properties:
RouteTableId: !Ref Private2RouteTable
DestinationCidrBlock: !Ref TransitGatewayRouteCidr
TransitGatewayId: !Ref TransitGatewayId
#######
# RDS #
#######
DBSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupName: !Sub 'vpc-${VpcNameTag}-private'
DBSubnetGroupDescription: !Sub '${VpcNameTag} VPC private subnets'
SubnetIds: [!Ref SubnetPrivate1, !Ref SubnetPrivate2]
###################
# Security Groups #
###################
SecurityGroupNoInbound:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: NoInbound
GroupDescription: No inbound traffic open
VpcId: !Ref Vpc
SecurityGroupEgress:
- IpProtocol: '-1'
CidrIp: 0.0.0.0/0
SecurityGroupPrivateInbound:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: PrivateInbound
GroupDescription: Allows inbound traffic from private IP address ranges
VpcId: !Ref Vpc
SecurityGroupIngress:
- IpProtocol: '-1'
CidrIp: 10.0.0.0/8
- IpProtocol: '-1'
CidrIp: 172.16.0.0/12
- IpProtocol: '-1'
CidrIp: 192.168.0.0/16
SecurityGroupEgress:
- IpProtocol: '-1'
CidrIp: 0.0.0.0/0
#############
# CIDR Util #
#############
CidrUtil:
Type: Custom::CidrUtil
Properties:
ServiceToken: !Sub '${CidrUtilLambdaFunction.Arn}'
CidrUtilLambdaFunction:
Type: AWS::Lambda::Function
Properties:
Role: !Sub '${CidrUtilLambdaExecutionRole.Arn}'
Handler: index.handler
Timeout: 20
Runtime: python3.7
Code:
ZipFile: !Sub |
import boto3
import cfnresponse
def handler(event, context):
try:
print(event)
ec2 = boto3.client('ec2', region_name='${AWS::Region}')
region_splay = [r['RegionName'] for r in ec2.describe_regions()['Regions']].index('${AWS::Region}')
print('Region, region_splay: ${AWS::Region}', region_splay)
account_id = '${AWS::AccountId}'
cidr_prefix = f'10.{account_id[:2]}.{int(account_id[-2:]) + region_splay}'
print('Account Id: ${AWS::AccountId}, CIDR Prefix:', cidr_prefix)
cfnresponse.send(event, context, cfnresponse.SUCCESS, {"CidrPrefix": cidr_prefix})
except Exception as e:
print('Error:', repr(e))
cfnresponse.send(event, context, cfnresponse.FAILED, {"Message": "Exception during processing"})
CidrUtilLambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: ['sts:AssumeRole']
ManagedPolicyArns: ['arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole']
Policies:
- PolicyName: root
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: 'ec2:DescribeRegions'
Resource: '*'
Outputs:
DBSubnetGroupName:
Value: !Ref DBSubnetGroup
Export:
Name: !Sub '${VpcNameTag}:DBSubnetGroupName'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment