In this section, we present the recipe to create and configure a VPC. You can assign a single Classless Inter-Domain Routing (CIDR) block to the VPC. The allowed block size is between a /28 (16 IP addresses) net mask and /16 (65536 IP addresses) net mask. Public and private subnets are specified to build multitier applications. To access the Internet from a private subnet, we have to use Network Address Translation (NAT) instance in the public subnet. Each subnet must be associated with a routing table. Each route in the routing table contains the destination CIDR network range and a target Internet gateway/virtual private gateway.
To access the Internet the EC2 instance must either have an Elastic IP (EIP) address or a public IP address. You can also use a NAT instance, which will have a public IP address and perform the natting for your instances. Your subnet's route table must contain the route that directs the Internet bound traffic to the Internet gateway. You have to ensure that network Access Control List (ACL) and security groups allow the Internet traffic to and from your instance. To access the on-premise servers or other AWS instances outside your VPC, you can add public IP addresses of those servers as destination and the Internet gateway as the target in your subnet routing table.
Network ACL operates at a subnet level. Network ACL controls the subnet's inbound and outbound traffic. We can configure inbound and outbound rules for network ACL, which are then evaluated in order. As network ACLs are stateless, you have to explicitly add rules for return traffic. We can use network ACL as an additional layer of security along with the security groups. Each subnet must be associated with the network ACL. If you don't specify the network ACL, then it will be automatically associated with the default network ACL.
Here, we present specific commands to use for creating and configuring a VPC, including creating a VPC with a CIDR block and dedicated tenancy, adding tags for your VPC, and viewing the details of the newly created VPC. We then create private and public subnets, security group for the NAT instance, a NAT instance, an Internet gateway, and route tables for the private and public subnets. Finally, we test our VPC setup.
The following command creates a VPC setup with a CIDR network range of 10.0.0.0/16 in dedicated tenancy. Record the VPC ID for further use.
$ aws ec2 create-vpc --cidr-block 10.0.0.0/16 --instance-tenancy dedicated
Add tags to the VPC.
$ aws ec2 create-tags --resources vpc-0214e967 --tags Key=Name,Value=TestVPC
$ aws ec2 describe-vpcs --vpc-ids vpc-0214e9670214e9670214e967
PublicSubnet
. This subnet provides 256 private IP addresses. Please select availability zone as per your requirement.$ aws ec2 create-subnet --vpc-id vpc-0214e967 --cidr-block 10.0.0.0/24 --availability-zone ap-southeast-1a
$ aws ec2 create-tags --resources subnet-0240b575 --tags Key=Name,Value=PublicSubnet
PrivateSubnet
. Provide the availability zone as per your requirements.$ aws ec2 create-subnet --vpc-id vpc-0214e967 --cidr-block 10.0.1.0/24 --availability-zone ap-southeast-1a
$ aws ec2 create-tags --resources subnet-49ca1b2c --tags Key=Name,Value=PrivateSubnet
$ aws ec2 create-security-group --group-name NATSecurityGroup --description "NAT Server Security Group" --vpc-id vpc-0214e967
PrivateSubnet
and 22 from the public IP range of your network. WhatsMyIp
will return the public IP address of your workstation. Allow access only from the specified IP address, by specifying /32 with the IP address.$ aws ec2 authorize-security-group-ingress --group-id sg-5173a334 --protocol tcp --port 22 --cidr 123.252.223.114/32 $ aws ec2 authorize-security-group-ingress --group-id sg-5173a334 --protocol tcp --port 80 --cidr 10.0.1.0/24 $ aws ec2 authorize-security-group-ingress --group-id sg-5173a334 --protocol tcp --port 443 --cidr 10.0.1.0/24
$ aws ec2 authorize-security-group-egress --group-id sg-5173a334 --protocol tcp --port 80 --cidr 0.0.0.0/0 $ aws ec2 authorize-security-group-egress --group-id sg-5173a334 --protocol tcp --port 443 --cidr 0.0.0.0/0
$ aws ec2 describe-images --filter Name="owner-alias",Values="amazon" --filter Name="name",Values="amzn-ami-vpc-nat*"
$ aws ec2 create-key-pair --key-name NATServerKeyPair
$ aws ec2 run-instances --image-id ami-70a38222 --count 1 --instance-type t1.micro --key-name NATServerKeyPair --security-group-ids sg-5173a334 --subnet-id subnet-0240b575
$ aws ec2 create-tags --resources i-1634e7da --tags Key=Name,Value=NATInstance
$ aws ec2 modify-instance-attribute --instance-id i-1634e7da --source-dest-check "{"Value": false}"
$ aws ec2 allocate-address --domain vpc
$ aws ec2 associate-address --instance-id i-1634e7da --allocation-id eipalloc-37302855
PrivateSubnet
.$ aws ec2 create-route-table --vpc-id vpc-0214e967
$ aws ec2 create-tags --resources rtb-7c18d919 --tags Key=Name,Value=PrivateRouteTable
$ aws ec2 create-route --route-table-id rtb-7c18d919 --destination-cidr-block 0.0.0.0/0 --instance-id i-1634e7da
$ aws ec2 associate-route-table --route-table-id rtb-7c18d919 --subnet-id subnet-49ca1b2c
PublicSubnet
.$ aws ec2 create-route-table --vpc-id vpc-0214e967
$ aws ec2 create-tags --resources rtb-7f1bda1a --tags Key=Name,Value=PublicRouteTable
$ aws ec2 create-route --route-table-id rtb-7f1bda1a --destination-cidr-block 0.0.0.0/0 --gateway-id igw-95c22df0
$ aws ec2 associate-route-table --route-table-id rtb-7f1bda1a --subnet-id subnet-0240b575
PublicSubnet
with a security group that allows SSH traffic from the public IP range of your network.$ aws ec2 create-security-group --group-name PublicServerSecurityGroup --description "Public Server Security Group" --vpc-id vpc-0214e967
$ aws ec2 authorize-security-group-ingress --group-id sg-0a76a66f --protocol tcp --port 22 --cidr 123.252.223.114/32
$ aws ec2 create-key-pair --key-name PublicServerKeyPair
PublicSubnet
.$ aws ec2 run-instances --image-id ami-7e2c612c --count 1 --instance-type t1.micro --key-name PublicServerKeyPair --security-group-ids sg-0a76a66f --subnet-id subnet-0240b575
$ aws ec2 create-security-group --group-name PrivateServerSecurityGroup --description "Private Server SecurityGroup" --vpc-id vpc-0214e967
PublicSubnet
only.$ aws ec2 authorize-security-group-ingress --group-id sg-d476a6b1 --protocol tcp --port 22 --cidr 10.0.0.0/24
$ aws ec2 create-key-pair --key-name PrivateServerKeyPair
PrivateSubnet
.$ aws ec2 run-instances --image-id ami-7e2c612c --count 1 --instance-type t1.micro --key-name PrivateServerKeyPair --security-group-ids sg-d476a6b1 --subnet-id subnet-49ca1b2c
We first created a VPC with dedicated tenancy by specifying the network range for the VPC, in the CIDR notation. Each instance launched in a VPC has a tenancy attribute and each VPC has a related instance tenancy attribute. As we created our VPC with the dedicated attribute, all instances launched in this VPC will be dedicated instances irrespective of the value of the instance's tenancy attribute. Note that some instance types cannot be launched in a VPC with the tenancy attribute set to dedicated. The tenancy information for the VPC is displayed on the VPC console.
Next, we created a tag to help us identify our VPC resource by associating our own metadata with it. Tags are specified as key/value pairs. For our VPC, we specify the key as name and assign TestVPC
as the value. You can specify other tags to categorize your VPC or other AWS resources, for example, you can specify the owner or primary contact for the resource and the environment (dev, test, staging, production, and so on), to better manage the resources in your account.
Then, we retrieved the description of our VPC to verify the VPC ID, tenancy (default or dedicated), tags specified (both key and value), current state of the VPC (pending or available), the CIDR block, and an indicator whether the VPC is the default VPC (true or false). In addition, executing the describe-vpcs
command also returns the ID of the DHCP options set associated with your VPC. To assign your own domain name to your instances, you must specify the appropriate DHCP options to use with the VPC. The DHCP option sets are associated with your AWS account.
We created public and private subnets by dividing the VPC CIDR block, and specifying the appropriate availability zone. In our example, we ensure that the CIDR blocks for our subnets do not overlap. If you are defining a single subnet in your VPC, then the CIDR block of your subnet is the same as that for the VPC. Though VPCs can span multiple availability zones, each subnet must reside entirely within one availability zone. In our example, we placed our subnets in the same availability zone, however, you can choose to place your subnets in separate availability zones to protect against availability zone failures. In this step, we also create tags to name our subnets PublicSubnet
and PrivateSubnet
.
Next, we create a security group for our NAT instance with inbound and outbound rules for specific ports. The inbound rules allow inbound HTTP traffic on port 80 from servers in the private subnet, HTTPS traffic on port 443 from servers in the private subnet, and inbound SSH access to the NAT instance from a specific IP your network. The outbound rules allow outbound HTTP and HTTPS access to the Internet on ports 80 and 443, respectively.
In the next step, we create and launch a NAT instance. We use the NAT instance in the public subnet of our VPC to enable instances in the private subnet to initiate outbound traffic to the Internet while preventing inbound traffic (from the Internet). We start by executing the describe-images
command that describes one or more images, that is, AMIs available for you to launch. We filter the results of this command to retrieve Amazon AMIs that are configured to run as NAT instances. The AMI names contain the amzn-ami-vpc-nat
string with a version number, so we filter the results using this string and choose the most recent AMI for our use. At this stage, we create a key pair and launch our NAT instance in the public subnet with the previously created security group.
We have chosen a micro instance in our example, however, you should choose the NAT instance type based on your intended load. If your application connects to the Internet occasionally and does not require high-network bandwidth, then a micro instance might suffice. However, if your application communicates with the Internet constantly and requires higher bandwidth, then a medium or a large instance may be required.
We can use the describe-instances
command to check the state of our instance. It should be in a running state before we proceed with the next steps. As EC2 instances perform source/destination checks by default, that is, an instance must be the source or destination of any traffic it sends or receives. However, a NAT server must be able to send and receive traffic from sources or destinations other than itself. Therefore, we use the modify-instance-attribute
command to disable the source/destination checks for our NAT instance.
In the next step, we allocate an EIP address for use with instances in our VPC. The EIP is associated with our AWS account, and helps in dynamically remapping the address to another instance in our account in case of instance failures. We associate this EIP address with our NAT instance.
The next set of commands creates an Internet gateway and attaches the same to our VPC. This step enables Internet access for instances in our subnets. The NAT instance is connected to the Internet using the Internet gateway. For example, an instance in the private subnet can connect to the Internet via the NAT instance, which routes traffic from/to the instance using the Internet gateway.
In the next two steps we create custom route tables for our private and public subnets. These route tables explicitly control the routing for each subnet's outbound traffic. After creating a route table we define routes and associate them with our subnets. Each route specifies a destination CIDR and a target (for example, we route all traffic to the NAT instance in the private subnet and to the Internet gateway in the public subnet). As a practice, we should use the most specific route when determining how to route our traffic. For example, if we have two routes to the same destination then the route that covers a smaller number of IP addresses is used.
Finally, we test our VPC by creating instances in our public and private subnets.