Create a VPC - Manual

In this blogpost we are going to create a Virtual Private Cloud in Amazon Web Services that will host all the shared services generally deployed by an IT team. Since networking is fundamental to allow communication between servers and computers, every cloud service providers use a dedicated network construct. AWS and Google Cloud call it a VPC, while Azure for example calls it a VNET (virtual network).

Network constructs are different between cloud providers. For example, a VPC is global in Google Cloud while it is a regional construct in AWS. This blogpost is not targeted to highlight the networking differences between AWS and GCP (more on that in a dedicated blogpost).

As AWS mentions, "A region is a physical location around the world where we cluster data centers. We call each group of logical data centers an Availability Zone. Each AWS Region consists of multiple, isolated, and physically separate AZs within a geographic area. Unlike other cloud providers, who often define a region as a single data center, the multiple AZ design of every AWS Region offers advantages for customers. Each AZ has independent power, cooling, and physical security and is connected via redundant, ultra-low-latency networks. AWS customers focused on high availability can design their applications to run in multiple AZs to achieve even greater fault-tolerance. AWS infrastructure Regions meet the highest levels of security, compliance, and data protection.

What we are trying to achieve is the following:

AWS VPC

That's the diagram of a very general VPC that can created using the AWS Console. We will also cover how to create a VPC using several automation tools like the AWS CLI and Hashicorp Terraform and here is an example

The AWS console offers the choice to create juste a simple VPC without any other additionnal network construct or the entiore VPC with all the objects needed to allow communication between your entities (Route table, Security Group, NAT and Internet gateways). In our example we will be using the options that allows us to create all networking construct at once.

  • CIDR:

    • This VPC has a CIDR or 10.0.10.0/24 and AWS will automatically create 2 publics subnets and 2 private subnets.
    • These public and private subnets will be shared between the availability chosen during the creation process.
  • No IPv6 is required for now.

  • Number of Availability zones: 2 is sufficient

  • Number of public and private subnets: 2 each (even though only 1 public is sufficient, our VPC might need additional private and public subnets later on.)

  • NAT Gateway: None.

The following picture shows how to create your VPC:

AWS VPC Console

For now we do not have to take care of any Elastic IP Address as we will ask AWS to assign a public IP to our controller when we deploy it.

Finally the AWS Console will confirm that all the objects within the VPC have been created:

AWS VPC Created

The next picture will demonstrate the automatically created subnets and route tables for the specifically mentionned availability zones.

AWS VPC Subnets

If we have a look at the route tables associated with that VPC, we can see that each private subnet has its own routing table. The public subnets share the same public route table:

AWS VPC Subnets

When you dig into the "AWS-UE1-VPC-MGMT-rtb-public" routing table, we can see that it has two routes for now :

  • 10.0.10.0/24, that is local
  • 0.0.0.0/0, the default route that points to an internet gateway.

Now if we deploy an EC2 instance into any public subnet that belongs to the Region/AZ created previously, we should have connectivity to reach the internet. If we want to authorize inbound traffic to that EC2 instance, we need to allow that traffic using the stateful AWS Security Groups.

Our VPC should be ready to host our Aviatrix Controller to start out multicloud journey.

Bonus

If you want to verify using the CLI (some of us are still network engineers right ?) you can configure your computer following this doc.

(I will reduce the amount of information in the outputs below to focus on what's important. For example, this output should also list the default VPCs but I will not list it)

1
2$ aws ec2 describe-vpcs | jq
 1{
 2  "Vpcs": [
 3    {
 4      "CidrBlock": "10.0.10.0/24",
 5      "DhcpOptionsId": "dopt-0a943645c8bc81085",
 6      "State": "available",
 7      "VpcId": "vpc-08aee6f377637f9ae",
 8      "OwnerId": "",
 9      "InstanceTenancy": "default",
10      "CidrBlockAssociationSet": [
11        {
12          "AssociationId": "vpc-cidr-assoc-04f586cab46d8b000",
13          "CidrBlock": "10.0.10.0/24",
14          "CidrBlockState": {
15            "State": "associated"
16          }
17        }
18      ],
19      "IsDefault": false,
20      "Tags": [
21        {
22          "Key": "Name",
23          "Value": "AWS-UE1-VPC-MGMT"
24        }
25      ]
26    },
27  ]
28}

We can also check for the subnets created.

1$ aws ec2 describe-subnets --filters "Name=vpc-id,Values=vpc-08aee6f377637f9ae" | jq
  1
  2{
  3  "Subnets": [
  4    {
  5      "AvailabilityZone": "us-east-1b",
  6      "AvailabilityZoneId": "use1-az1",
  7      "AvailableIpAddressCount": 11,
  8      "CidrBlock": "10.0.10.16/28",
  9      "DefaultForAz": false,
 10      "MapPublicIpOnLaunch": false,
 11      "MapCustomerOwnedIpOnLaunch": false,
 12      "State": "available",
 13      "SubnetId": "subnet-07c81d6d42836a8f9",
 14      "VpcId": "vpc-08aee6f377637f9ae",
 15      "OwnerId": " ",
 16      "AssignIpv6AddressOnCreation": false,
 17      "Ipv6CidrBlockAssociationSet": [],
 18      "Tags": [
 19        {
 20          "Key": "Name",
 21          "Value": "AWS-UE1-VPC-MGMT -subnet-public2-us-east-1b"
 22        }
 23      ],
 24      "SubnetArn": "arn:aws:ec2:us-east-1: :subnet/subnet-07c81d6d42836a8f9",
 25      "EnableDns64": false,
 26      "Ipv6Native": false,
 27      "PrivateDnsNameOptionsOnLaunch": {
 28        "HostnameType": "ip-name",
 29        "EnableResourceNameDnsARecord": false,
 30        "EnableResourceNameDnsAAAARecord": false
 31      }
 32    },
 33    {
 34      "AvailabilityZone": "us-east-1b",
 35      "AvailabilityZoneId": "use1-az1",
 36      "AvailableIpAddressCount": 11,
 37      "CidrBlock": "10.0.10.144/28",
 38      "DefaultForAz": false,
 39      "MapPublicIpOnLaunch": false,
 40      "MapCustomerOwnedIpOnLaunch": false,
 41      "State": "available",
 42      "SubnetId": "subnet-0f75d35ae1da469cc",
 43      "VpcId": "vpc-08aee6f377637f9ae",
 44      "OwnerId": " ",
 45      "AssignIpv6AddressOnCreation": false,
 46      "Ipv6CidrBlockAssociationSet": [],
 47      "Tags": [
 48        {
 49          "Key": "Name",
 50          "Value": "AWS-UE1-VPC-MGMT -subnet-private2-us-east-1b"
 51        }
 52      ],
 53      "SubnetArn": "arn:aws:ec2:us-east-1: :subnet/subnet-0f75d35ae1da469cc",
 54      "EnableDns64": false,
 55      "Ipv6Native": false,
 56      "PrivateDnsNameOptionsOnLaunch": {
 57        "HostnameType": "ip-name",
 58        "EnableResourceNameDnsARecord": false,
 59        "EnableResourceNameDnsAAAARecord": false
 60      }
 61    },
 62    {
 63      "AvailabilityZone": "us-east-1a",
 64      "AvailabilityZoneId": "use1-az6",
 65      "AvailableIpAddressCount": 11,
 66      "CidrBlock": "10.0.10.128/28",
 67      "DefaultForAz": false,
 68      "MapPublicIpOnLaunch": false,
 69      "MapCustomerOwnedIpOnLaunch": false,
 70      "State": "available",
 71      "SubnetId": "subnet-0c363777a7f301bc2",
 72      "VpcId": "vpc-08aee6f377637f9ae",
 73      "OwnerId": " ",
 74      "AssignIpv6AddressOnCreation": false,
 75      "Ipv6CidrBlockAssociationSet": [],
 76      "Tags": [
 77        {
 78          "Key": "Name",
 79          "Value": "AWS-UE1-VPC-MGMT -subnet-private1-us-east-1a"
 80        }
 81      ],
 82      "SubnetArn": "arn:aws:ec2:us-east-1: :subnet/subnet-0c363777a7f301bc2",
 83      "EnableDns64": false,
 84      "Ipv6Native": false,
 85      "PrivateDnsNameOptionsOnLaunch": {
 86        "HostnameType": "ip-name",
 87        "EnableResourceNameDnsARecord": false,
 88        "EnableResourceNameDnsAAAARecord": false
 89      }
 90    },
 91    {
 92      "AvailabilityZone": "us-east-1a",
 93      "AvailabilityZoneId": "use1-az6",
 94      "AvailableIpAddressCount": 11,
 95      "CidrBlock": "10.0.10.0/28",
 96      "DefaultForAz": false,
 97      "MapPublicIpOnLaunch": false,
 98      "MapCustomerOwnedIpOnLaunch": false,
 99      "State": "available",
100      "SubnetId": "subnet-0f2351620e76a54d7",
101      "VpcId": "vpc-08aee6f377637f9ae",
102      "OwnerId": " ",
103      "AssignIpv6AddressOnCreation": false,
104      "Ipv6CidrBlockAssociationSet": [],
105      "Tags": [
106        {
107          "Key": "Name",
108          "Value": "AWS-UE1-VPC-MGMT -subnet-public1-us-east-1a"
109        }
110      ],
111      "SubnetArn": "arn:aws:ec2:us-east-1: :subnet/subnet-0f2351620e76a54d7",
112      "EnableDns64": false,
113      "Ipv6Native": false,
114      "PrivateDnsNameOptionsOnLaunch": {
115        "HostnameType": "ip-name",
116        "EnableResourceNameDnsARecord": false,
117        "EnableResourceNameDnsAAAARecord": false
118      }
119    }
120  ]
121}

Then we can have a look at the route tables that are associated with the VPC created previously.

1
2$ aws ec2 describe-route-tables --filters "Name=vpc-id,Values=vpc-08aee6f377637f9ae" | jq
  1{
  2  "RouteTables": [
  3    {
  4      "Associations": [
  5        {
  6          "Main": false,
  7          "RouteTableAssociationId": "rtbassoc-02429aa65c6ca4696",
  8          "RouteTableId": "rtb-04083da70542085d7",
  9          "SubnetId": "subnet-0f75d35ae1da469cc",
 10          "AssociationState": {
 11            "State": "associated"
 12          }
 13        }
 14      ],
 15      "PropagatingVgws": [],
 16      "RouteTableId": "rtb-04083da70542085d7",
 17      "Routes": [
 18        {
 19          "DestinationCidrBlock": "10.0.10.0/24",
 20          "GatewayId": "local",
 21          "Origin": "CreateRouteTable",
 22          "State": "active"
 23        },
 24        {
 25          "DestinationPrefixListId": "pl-63a5400a",
 26          "GatewayId": "vpce-07c5489eece54ac5c",
 27          "Origin": "CreateRoute",
 28          "State": "active"
 29        }
 30      ],
 31      "Tags": [
 32        {
 33          "Key": "Name",
 34          "Value": "AWS-UE1-VPC-MGMT -rtb-private2-us-east-1b"
 35        }
 36      ],
 37      "VpcId": "vpc-08aee6f377637f9ae",
 38      "OwnerId": "   "
 39    },
 40    {
 41      "Associations": [
 42        {
 43          "Main": true,
 44          "RouteTableAssociationId": "rtbassoc-032f087a7405940dd",
 45          "RouteTableId": "rtb-050a2ad500f057f3b",
 46          "AssociationState": {
 47            "State": "associated"
 48          }
 49        }
 50      ],
 51      "PropagatingVgws": [],
 52      "RouteTableId": "rtb-050a2ad500f057f3b",
 53      "Routes": [
 54        {
 55          "DestinationCidrBlock": "10.0.10.0/24",
 56          "GatewayId": "local",
 57          "Origin": "CreateRouteTable",
 58          "State": "active"
 59        }
 60      ],
 61      "Tags": [],
 62      "VpcId": "vpc-08aee6f377637f9ae",
 63      "OwnerId": "   "
 64    },
 65    {
 66      "Associations": [
 67        {
 68          "Main": false,
 69          "RouteTableAssociationId": "rtbassoc-049270142f38cc317",
 70          "RouteTableId": "rtb-0726a61879b6886c8",
 71          "SubnetId": "subnet-07c81d6d42836a8f9",
 72          "AssociationState": {
 73            "State": "associated"
 74          }
 75        },
 76        {
 77          "Main": false,
 78          "RouteTableAssociationId": "rtbassoc-0f4a7bb9b742f8840",
 79          "RouteTableId": "rtb-0726a61879b6886c8",
 80          "SubnetId": "subnet-0f2351620e76a54d7",
 81          "AssociationState": {
 82            "State": "associated"
 83          }
 84        }
 85      ],
 86      "PropagatingVgws": [],
 87      "RouteTableId": "rtb-0726a61879b6886c8",
 88      "Routes": [
 89        {
 90          "DestinationCidrBlock": "10.0.10.0/24",
 91          "GatewayId": "local",
 92          "Origin": "CreateRouteTable",
 93          "State": "active"
 94        },
 95        {
 96          "DestinationCidrBlock": "0.0.0.0/0",
 97          "GatewayId": "igw-08cb7b3514b815ae0",
 98          "Origin": "CreateRoute",
 99          "State": "active"
100        }
101      ],
102      "Tags": [
103        {
104          "Key": "Name",
105          "Value": "AWS-UE1-VPC-MGMT -rtb-public"
106        }
107      ],
108      "VpcId": "vpc-08aee6f377637f9ae",
109      "OwnerId": "   "
110    },
111    {
112      "Associations": [
113        {
114          "Main": false,
115          "RouteTableAssociationId": "rtbassoc-0a9141e0861dbca9c",
116          "RouteTableId": "rtb-0bbeb2d8088d29033",
117          "SubnetId": "subnet-0c363777a7f301bc2",
118          "AssociationState": {
119            "State": "associated"
120          }
121        }
122      ],
123      "PropagatingVgws": [],
124      "RouteTableId": "rtb-0bbeb2d8088d29033",
125      "Routes": [
126        {
127          "DestinationCidrBlock": "10.0.10.0/24",
128          "GatewayId": "local",
129          "Origin": "CreateRouteTable",
130          "State": "active"
131        },
132        {
133          "DestinationPrefixListId": "pl-63a5400a",
134          "GatewayId": "vpce-07c5489eece54ac5c",
135          "Origin": "CreateRoute",
136          "State": "active"
137        }
138      ],
139      "Tags": [dzoo
140        {
141          "Key": "Name",
142          "Value": "AWS-UE1-VPC-MGMT -rtb-private1-us-east-1a"
143        }
144      ],
145      "VpcId": "vpc-08aee6f377637f9ae",
146      "OwnerId": "   "
147    }
148  ]
149}

Finally, let's dig deeper and have a look at the public routing tables entries and how it is associated.

We can see that this routing table is shared with 2 subnets: subnet-07c81d6d42836a8f9 and subnet-0f2351620e76a54d7 (both public subnets in the VPC on different AZ).

The routing table has the 2 entries mentionned above:

  • 10.0.10.0/24 for the local subnet
  • 0.0.0.0/0 that points to the internet gateway (igw-08cb7b3514b815ae0) used by the public subnet in the VPC
1aws ec2 describe-route-tables --filters "Name=tag:Name,Values=AWS-UE1-VPC-MGMT -rtb-public" | jq
 1{
 2  "RouteTables": [
 3    {
 4      "Associations": [
 5        {
 6          "Main": false,
 7          "RouteTableAssociationId": "rtbassoc-049270142f38cc317",
 8          "RouteTableId": "rtb-0726a61879b6886c8",
 9          "SubnetId": "subnet-07c81d6d42836a8f9",
10          "AssociationState": {
11            "State": "associated"
12          }
13        },
14        {
15          "Main": false,
16          "RouteTableAssociationId": "rtbassoc-0f4a7bb9b742f8840",
17          "RouteTableId": "rtb-0726a61879b6886c8",
18          "SubnetId": "subnet-0f2351620e76a54d7",
19          "AssociationState": {
20            "State": "associated"
21          }
22        }
23      ],
24      "PropagatingVgws": [],
25      "RouteTableId": "rtb-0726a61879b6886c8",
26      "Routes": [
27        {
28          "DestinationCidrBlock": "10.0.10.0/24",
29          "GatewayId": "local",
30          "Origin": "CreateRouteTable",
31          "State": "active"
32        },
33        {
34          "DestinationCidrBlock": "0.0.0.0/0",
35          "GatewayId": "igw-08cb7b3514b815ae0",
36          "Origin": "CreateRoute",
37          "State": "active"
38        }
39      ],
40      "Tags": [
41        {
42          "Key": "Name",
43          "Value": "AWS-UE1-VPC-MGMT -rtb-public"
44        }
45      ],
46      "VpcId": "vpc-08aee6f377637f9ae",
47      "OwnerId": "   "
48    }
49  ]
50}

last edited: October 28th 2022