AWS VPC peering in Cloudformation
Thu, 02 Apr 2020

I was in the process of migrating a mobile backend application into a custom Amazon VPC. Unfortunately, the mobile client was directly connected to a single EC2 instance public IP in a default VPC 🤬. The mobile team couldn't force the update for all the clients, therefore I could not possibly terminate the old instance and redirect all the traffic to a new load balancer. I needed to "slowly" migrate to the new architecture piece by piece. To make the matter worse, the database and the redis cache where all running in that single EC2 server 😵.

So, the first thing to do, was to move out the database and the cache into their own instance, but these had to be placed inside the new VPC and still be able to communicate with the EC2 instance in the default VPC. The "final temporary" architecture would look something like this:

How is this possible to achieve? Well AWS offer VPC Peering to allow VPC-to-VPC communication. A VPC peering connection simply is a networking connection between two VPCs that enables you to route traffic between them using private IP addresses. In the AWS terminology we have a requester VPC (custom, which will request for peering connection) and an accepter VPC (default, which will accept the connection).

To send private IP traffic from your instance to an instance in the accepter VPC, you must add a route to the route table that is associated with your subnet in which your instance resides. The route points to the CIDR block of the accepter VPC. In my case the communication was happening in between the default and custom VPC, so I put everything in one template as it was temporary. If you have two custom VPCs you should make two distinct templates.

In Cloudformation, the route that points to the accepter would be the following:

  ToAccepterVPCRoute:    
   Type: AWS::EC2::Route    
   Properties:      
     DestinationCidrBlock: !Ref AccepterVPCCIDR # Custom VPC CIDR block or subnet    
     RouteTableId: !Ref RequesterVPCRouteTable  # Route table in the default VPC    
     VpcPeeringConnectionId: !Ref VPCPeeringConnection

Vice versa, there should be another route on the accepter VPC side that points to the requester:

  ToRequesterVPCRoute:    
    Type: AWS::EC2::Route    
    Properties:      
      DestinationCidrBlock: !Ref RequesterVPCCIDR # Default VPC CIDR block or subnet     
      RouteTableId: !Ref AccepterVPCRouteTable # Route table in the custom VPC     
      VpcPeeringConnectionId: !Ref VPCPeeringConnection

And of course we must create the VPC peering connection itself, but only on one side:

  VPCPeeringConnection: # From the requester side    
   Type: AWS::EC2::VPCPeeringConnection    
   Properties:      
     VpcId: !Ref VPC      
     PeerVpcId: !Ref AccepterVPC # The custom VPC

Even though we have specified only one peering resource, the connection is not one sided, both VPCs' services can receive and send requests to each others, but only if the route tables are configured accordingly.

You need to associate respectively the route tables from the two VPCs with the appropriate subnets in which your resources you want to connect reside. Let's say your RDS instance is in a private subnet A, then you need a route table associated with subnet A, this table will have a route which will point to the other VPC CIDR block.

  RequesterVPCRouteTable:    
    Type: AWS::EC2::RouteTable    
    Properties:      
      VpcId: !Ref VPC      

  RequesterVPCSubnetARouteTableAssociation:    
    Type: AWS::EC2::SubnetRouteTableAssociation    
    Properties:      
      RouteTableId: !Ref RequesterVPCRouteTable      
      SubnetId: !Ref PrivateSubnetA

You should to the same thing for the other VPC.

  AccepterVPCRouteTable:    
    Type: AWS::EC2::RouteTable    
    Properties:      
      VpcId: !Ref AccepterVPC      

  AccepterVPCSubnetARouteTableAssociation:    
    Type: AWS::EC2::SubnetRouteTableAssociation    
    Properties:      
      RouteTableId: !Ref AccepterVPCRouteTable      
      SubnetId: !Ref PrivateSubnetInTheAccepterVPC

One more interesting thing, as in my case, if you want to connect the ALB with the EC2 instance in the default VPC through private IP, you will need to set the parameter AvailabilityZone to all.

  TargetGroup:    
    Type: AWS::ElasticLoadBalancingV2::TargetGroup 
    Properties:      
      Port: !Ref EC2InAccepterVPCPort      
      Protocol: HTTP      
      Targets: 
        - Id: !Ref EC2InAccepterVPCPrivateIp          
          AvailabilityZone: all          
          Port: !Ref EC2InAccepterVPCPort      
          TargetType: ip      
          VpcId: !Ref VPC

I hope you got something from this tutorial! Certainly to achieve this it took me a while as the Cloudformation tutorials are really rare these days 🤔 . Anyway I have made a template out of everything so you can check the various parts assembled together.

Cheers!


Thanks for reading!

This website use cookies for statistics purposes. By visiting it you will accept our privacy policy
OK