Skip to content

Instantly share code, notes, and snippets.

@Kailashcj
Last active June 29, 2020 14:24
Show Gist options
  • Select an option

  • Save Kailashcj/9a6bd2c5e76aff7e60c8e5ece78e7ae9 to your computer and use it in GitHub Desktop.

Select an option

Save Kailashcj/9a6bd2c5e76aff7e60c8e5ece78e7ae9 to your computer and use it in GitHub Desktop.
AWSTemplateFormatVersion: '2010-09-09'
Description: >-
This template deploys a new VPC and creates a new subnet and custom route table.
It uses a cloudformation custom resource which invokes a lambda function to
set the custom route table as the defaul MAIN route table.
Lambda function is using boto3(python sdk) function 'replace_route_table_association(**kwargs)' to replace the main routetable.
replace_route_table_association(**kwargs) function itself needs two parameters to make this change.
parameter1 # routetableassociationId of the existing main routetable. This is identified by iterating each route table in VPC
parameter2 # routetableId of the custom routetable. This is a reference to the new route table resource created in this template.
Parameters:
VpcName:
Description: Name of the New VPC
Type: String
AvailabilityZone:
Description: AvailabilityZone for this deployment
Type: AWS::EC2::AvailabilityZone::Name
VpcCIDR:
Description: CIDR for this VPC. For example, 192.168.0.0/20
Type: String
AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([2][0]))$ # only /20 allowed
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCIDR
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Ref VpcName
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${VpcName}-InternetGateway
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
MySubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Ref AvailabilityZone
CidrBlock: 192.168.0.0/22
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${VpcName}-Subnet1
MyCustomRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${VpcName}-MyCustomRouteTable
MyCustomRoute:
Type: AWS::EC2::Route
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId: !Ref MyCustomRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
MySubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref MyCustomRouteTable
SubnetId: !Ref MySubnet
NoIngressSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: "no-ingress-sg"
GroupDescription: "Security group with no ingress rule"
VpcId: !Ref VPC
CustomFunction:
Type: Custom::ReplaceMainRouteTable
Properties:
ServiceToken: !GetAtt 'ReplaceMainRouteTableLambdaFunction.Arn' #reference to lambda function
vpcid: !Ref VPC #parameter1 for lambda function (vpcid of new vpc)
routetable_id: !Ref MyCustomRouteTable #parameter 2 for lambda function(routetableid of custom routetable)
ReplaceMainRouteTableLambdaFunction:
Type: AWS::Lambda::Function
Properties:
Role: !GetAtt CustomResourceLambdaExecutionRole.Arn #permissions to execute lambda function
FunctionName: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:my-lambda-${VpcName}'
Handler: "index.lambda_handler"
Timeout: 20
Runtime: python2.7
Code:
ZipFile: |
import logging
import boto3
import cfnresponse
logger = logging.getLogger()
logger.setLevel(logging.INFO)
ec2 = boto3.resource('ec2')
client = boto3.client('ec2')
def replacert(vpcid, routetable_id): #function defined to replace RT
vpc = ec2.Vpc(vpcid)
route_tables = vpc.route_tables.all()
newid = dict()
for r in route_tables: #query each routetable in vpc
for eachline in ec2.RouteTable(r.id).associations_attribute:
if eachline['Main'] is True: # if this routetable is Main routetable, replace using below code
newid= client.replace_route_table_association(AssociationId=eachline.get('RouteTableAssociationId'), RouteTableId=routetable_id)
return newid.get('NewAssociationId')
def get_default_rtid(vpcid): #Capture route tableId of VPC Default route table. This is needed during stack deletion.
vpc = ec2.Vpc(vpcid)
route_tables = vpc.route_tables.all()
for r in route_tables:
if not r.tags:
return r.id
def set_main(vpcid, routetable_id):
vpc = ec2.Vpc(vpcid)
route_tables = vpc.route_tables.all()
newid = dict()
for r in route_tables:
for eachline in ec2.RouteTable(r.id).associations_attribute:
if eachline['Main'] is True:
newid= client.replace_route_table_association(AssociationId=eachline.get('RouteTableAssociationId'), RouteTableId=routetable_id)
return newid.get('NewAssociationId')
def lambda_handler(event, context):
try:
logger.info('got event {}'.format(event))
responseData = {}
vpcid = event['ResourceProperties'].get('vpcid') #get parameter1
routetable_id = event['ResourceProperties'].get('routetable_id') #get parameter2
if event['RequestType'] == 'Create': #Create Resource
logger.info('vpcid is {} and routetable_id is {}'.format(vpcid,routetable_id))
res = replacert(vpcid, routetable_id) #calling function replacert from lambda_handler
logger.info('value returned by replacert {}'.format(res))
responseData['NewAssociationId'] = res
logger.info('value of responseData is {}'.format(responseData['NewAssociationId']))
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, responseData['NewAssociationId'])
if event['RequestType'] == 'Delete':
logger.info('vpcid is {}'.format(vpcid))
default_id = get_default_rtid(vpcid)
logger.info('default_route_table_id is {}'.format(default_id))
unset = set_main(vpcid,default_id)
logger.info('value returned by set_main {}'.format(unset))
responseData['NewAssociationId'] = unset
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)
except:
logger.info('FAILED!')
cfnresponse.send(event, context, "FAILED", {"Message": "Exception during processing"})
CustomResourceLambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Condition: {}
Path: /
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- arn:aws:iam::aws:policy/AmazonVPCFullAccess
- arn:aws:iam::aws:policy/AmazonEC2FullAccess
Outputs:
MainRouteTable:
Description: A reference to the new main route table
Value: !Ref MyCustomRouteTable
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment