Connecting VPCs in AWS
In the scope of networking in AWS cloud, VPC is one of the most popular services. As technology improves and the world moves forward, connecting two VPCs together has become a mandatory need for every DevOps engineer and cloud architect you are up to meet. As I’ve started learning about this topic, I realized that you may have many solutions in order to connect two VPCs together, so I’ve decided to sum it all up in the following article. In this article, I’m going to describe all the solutions and show their differences and use cases.
Let’s start with the first and most easy solution for connecting VPCs, which is the VPC peering connection. VPC peering connection has many benefits: it allows you to connect two VPCs from different regions or even two different accounts, and there is no limitation for peering connections!
However, you should consider some problems before using it.
- Overlapping between two VPCs: You can’t connect two VPCs with the same CIDR, as they can accidentally have the same private IP addresses. Meaning that once you have two VPCs with the CIDR of 192.168.0.0/16, two EC2 instances can have the same private IP, for example, 192.168.10.12/32 and then there will be a collision in your network.
- “Chaining” is not possible with VPC peering. This means that if you have a peering connection between VPC-A and VPC-B, and you have a peering connection between VPC-B and VPC-C, then you will NOT have a peering connection between VPC-A and VPC-C.
Some DevOps engineers may find this to be an advantage because it’s useful when you don’t want to connect all the peered VPCs in a chaining strategy.
To explain this, let’s assume we are going to use two environments, development and production, and we are going to create an RDS database in each one of them. Now, you are probably familiar with AWS best practices and know that they should be created in VPC private subnets, so in order to enable our developers to connect to both databases, we are planning to create a VPN instance and from it to create a connection with the RDS instances. But in order to prevent mistakes and increase the security between the environments, no network connection is allowed between the development environment and the production environment. One solution for this scenario is to create the VPN instance in a third VPC, and from this VPC, you can create 2 peering connections to both other environments.
- Creating a peering connection is not so difficult, yet some configurations need to be done:
- First, you need to create a request for a peering connection in the source account (the requester account) and send it to the accepter account.
- Accept the peering connection invitation in the accepter account.
- Update routing tables in both VPCs to allow network traffic between them.
- Update security groups if not updated to allow servers communication using their private IPs. If you still want communication over the internet you need to specify the public IPs in the security group, else you can use private IPs only.
- Note – if you wish, you can even create a peering connection using Infrastructure as Code tools like Terraform to create peering connections. Some of them may have additional functionalities, for example, in Terraform, you can “auto-accept” the peering connection if both VPCs are in the same account and same region.
Now let’s take a look at the other scenario when you need to connect all VPCs in a mesh and create one big network out of all VPCs, so if you have 3 VPCs then you’ll have to create 3 connections: A-B, A-C, B-C. And if you have 4 VPCs you need to create 6 connections A-B, A-C, A-D, B-C, B-D, C-D. But what if you have 100 VPCs? We must think of a more efficient solution…
You can create a Transit Gateway to connect to multiple Amazon Virtual Private Clouds (Amazon VPCs), Direct Connect, VPNs, and other Software-Defined Network appliances. And, Transit Gateway can work across accounts. Actually, you can attach as many VPCs as you would like and create only one Transit Gateway for all of them.
The VPC account owner is billed hourly for each hour that their Amazon VPCs are attached to an AWS Transit Gateway and the data transferred. That makes the billing the biggest disadvantage of the Transit Gateway, it costs a little bit more. But again, you are able to connect multiple VPCs, VPNs and even Direct Connect (DX) to Transit Gateway.
In order to provision a Transit Gateway infrastructure architecture, you should follow these steps:
- First, create a Transit Gateway in the AWS VPC console of the desired account.
- Then create a Transit Gateway attachment to your Transit Gateway for each connection you want to create(for every VPC you want in every account or VPN or Direct Connect). Note that if you want to connect to cross-account resources you need to create a Transit Gateway also in the accepter account.
- Then, after creating the attachments, the last step will be again, modifying the route tables to route the traffic to their correct destination.
Now let’s talk about a scenario where you don’t want to expose your entire VPC, just a single application/endpoint, but share this application with many VPCs. We all agree that in this use case, there is no need for Transit Gateway, and a smaller solution can be enough. Well, that’s just the purpose of PrivateLink. The concept and workflow are really simple:
- You have an application running on EC2 instances (or other services).
- Create a Network Load Balancer (NLB) that redirects to those instances.
- Create an “Endpoint Service” in the AWS VPC console. Select your newly created Network Load Balancer (or Gateway Load Balancer).
- Create other “Endpoints” in any VPC you wish to connect (even in others accounts) by specifying “Other endpoint services” in the service category section and put your Endpoint Service in the service name section. Then you only need to select the VPC where you want to create this endpoint.
- The last step is simply to modify the security groups if needed to allow the new connection.
Let’s take a look at another example where we want to connect to AWS RDS Aurora postgres cluster in one account with ‘psql’ CLI tool from an EC2 instance in another account.
On the first account
- We will create the RDS cluster in private subnets, following AWS best practices. Now, we are going to create a target group based on the IP addresses of the RDS cluster. We can find the IP addresses in the EC2 console in the ‘Network interfaces’ tab. As for the ports, specify the Postgres port 5432 to allow connections to the database.
- Assign this target group to a NLB, and again open port 5432.
- In the VPC console, create an “Endpoint Service” that points to the NLB.
- Navigate to the “Allow principals” tab and add the second account arn.
On the second account
- Choose a VPC or create a new one.
- Create an EC2 instance with a public IP address that will allow you to connect to it.
- You can choose an AMI with pre-installed Postgres CLI or install it with the instructions here.
- Don’t forget to open port 5432 on the instance security group.
- In the VPC console, create an “Endpoint”. Specify the service from the first account and verify the connection. Select the VPC where you created the instance.
Now, when your infrastructure is up and running, you can ssh or use instance connect into the instance, and run the psql command where the host is presented as your VPC endpoint (name):
‘psql -h <vpc_endpoint> -p 5432 -U postgres’.
Once you enter the password, you are fully connected to the RDS cluster in another account with a private and secure connection and without exposing your entire network but only a single endpoint.
This one last service of AWS is not so common, yet very important. AWS Resource Access Manager or RAM is a great solution in AWS to share AWS resources between different accounts. It provides many features including sharing Transit Gateways, Route53 Resolver rules, Aurora DB clusters, Image Builder Images etc’… But among them, we are also allowed to share our VPC subnets with other accounts inside our organization. That means that this feature doesn’t allow us to connect our VPC to another VPC, but share our VPC subnets themself with others. When we share our subnet, two different instances in two different accounts can be placed together in this subnet and then communicate using their private IPs without access to the internet. AWS RAM allows resource sharing outside of the organization, but currently, subnets are allowed only within your organization.
The process is quite simple:
- From the first account, enter the RAM console and create a resource share.
- Select subnets as the resource type, and then select the subnets that you want to share.
- Select the principals that you want to give access to.
- From the RAM console of the other account, approve the sharing request.
And that’s it! You can enter the VPC console and see the other accounts subnet in the subnets tab.
When you need to connect many VPCs in AWS, you have to consider many properties such as how many VPCs you wish to connect, do they have the same or different CIDRs, if they need to be connected in a “mesh” structure or with single tunnels, if they are located in the same account, same organization, different accounts? As you’ll start exploring this topic you most likely find the solutions that I’ve mentioned in this article. Each and every one of them has its own unique value and its own advantages and disadvantages. Based on your situation you can decide what is the best approach in your scenario. In order to help you choose the correct solution for your architecture, here is a summarized table of all the solutions described in the article:
|Number of VPCs
|Expose entire VPC
|Expose only endpoint
|Modify routing tables