You already know the components of an Amazon VPC network. In this topic, we'll get our hands dirty by creating an actual virtual network on AWS. This will give you hands-on practice with creating virtual networks to host your resources. We'll start by defining a hypothetical architecture with all the necessary resources and then proceed to create each of the network components. To do this, we'll use both the Console and CLI commands.
Architecture
Imagine you have a simple AWS VPC architecture for a basic web application. This setup includes one VPC, four subnets—two public and two private—gateways, specifically an Internet and NAT gateway, one route table, security groups, and access control lists (ACLs). You've deployed web servers, like Nginx or Apache, in an EC2 autoscaling group within the public subnets to ensure high availability. In the private subnets, you have an Amazon RDS database for data storage and an Amazon ElastiCache service for Redis handling caching.
Here's an architecture diagram representing this setup:
Before creating these components, you need to log in to your account as an IAM user with the necessary permissions. This user needs the AWS-managed policy AmazonVPCFullAccess attached to them, giving the user full control over Amazon VPC actions. The policy's ARN is as follows:
arn:aws:iam::aws:policy/AmazonVPCFullAccessOnce logged in as this user, you can proceed to create the resources.
The VPC
Let's start by creating an Amazon VPC network. Navigate to the VPC dashboard and do the following:
Click on "Your VPCs" in the left sidebar;
Click "Create VPC";
Type in the VPC Name and the CIDR block
10.0.0.0/16;Click "Create";
Alternatively, you can run the following CLI command from AWS CloudShell or the AWS CLI:
# if you use PowerShell as your shell, replace backslashes (\) with backticks (`) as line seperators
aws ec2 create-vpc \
--cidr-block 10.0.0.0/16Boto3
import boto3
ec2 = boto3.resource('ec2')
vpc = ec2.create_vpc(CidrBlock='10.0.0.0/16')This will provide a JSON output that looks like this:
{
"Vpc": {
"CidrBlock": "10.0.0.0/16",
"DhcpOptionsId": "dopt-xxxxxxxxxxxxxxxxx",
"State": "pending",
"VpcId": "vpc-xxxxxxxxxxxxxxxxx",
"OwnerId": "123456789012",
"InstanceTenancy": "default",
"Ipv6CidrBlockAssociationSet": [],
"CidrBlockAssociationSet": [
{
"AssociationId": "vpc-cidr-assoc-xxxxxxxxxxxxxxxxx",
"CidrBlock": "10.0.0.0/16",
"CidrBlockState": {
"State": "associated"
}
}
],
"IsDefault": false
}
}You cannot add tags, such as the name when you create the VPC from the CLI. Once it's created, copy the VPC ID and run the following command to add a tag, like the name tag:
# if you use PowerShell as your shell, replace backslashes (\) with backticks (`) as line seperators
aws ec2 create-tags \
--resources vpc-xxxxxxxxxxxxxxxxx \
--tags Key=Name,Value=MyVPCNameBoto3
import boto3
ec2 = boto3.client('ec2')
ec2.create_tags(
Resources=['vpc-xxxxxxxxxxxxxxxxx'],
Tags=[{'Key': 'Name', 'Value': 'MyVPCName'}]
)That's it. We now have a VPC network. You can see all the details about it in your Amazon VPC dashboard or use CLI commands. You can even use filters to retrieve a specific VPC. Below are several commands you can use with comments to explain what they do:
# all VPCs in the default region of AWS account
aws ec2 describe-vpcs
# VPCs with the '10.0.0.0/16' CIDR block
aws ec2 describe-vpcs --filters "Name=cidr,Values=10.0.0.0/16"
# all VPCs in the 'available' state
aws ec2 describe-vpcs --filters "Name=state,Values=available"
# List all VPCs but filter the output to show only the VPC IDs and their associated CIDR blocks.
# The --query parameter uses JMESPath to specify what information to include in the output.
aws ec2 describe-vpcs --query 'Vpcs[*].{ID:VpcId, CIDR:CidrBlock}'Feel free to run these commands in your AWS account for practice.
Subnets
Next, let's create 4 subnets in the VPC. Navigate to the VPC dashboard and do the following:
Go to "Subnets" on the left sidebar;
Click "Create subnet";
Select the VPC you created;
Enter the Subnet Name, choose an AZ, and enter the CIDR block (e.g.,
10.0.1.0/24);At the bottom, add a subnet or save it and repeat the steps for the other three subnets with their respective CIDR blocks as outlined in the diagram;
Alternatively, you can use the following CLI command:
# if you use PowerShell as your shell, replace backslashes (\) with backticks (`) as line seperators
aws ec2 create-subnet \
--vpc-id VPC_ID \
--cidr-block 10.0.1.0/24 \
--availability-zone us-east-1aBoto3
import boto3
ec2 = boto3.resource('ec2')
subnet = ec2.create_subnet(
VpcId='VPC_ID',
CidrBlock='10.0.1.0/24',
AvailabilityZone='us-east-1a'
)Note that you need to provide the VPC ID for the VPC your subnets are associated with. Once created, repeat the steps for the other subnets, changing the CIDR and AZ if necessary. Finally, you can view your subnets from the console or using CLI commands:
# subnets in the 'us-east-1' region
aws ec2 describe-subnets --region us-east-1
# subnets that have a tag 'Name' with the value 'MySubnet'
aws ec2 describe-subnets --filters "Name=tag:Name,Values=MySubnet"
# subnets in a specific VPC with ID 'vpc-xxxxxxxxxxxxxxxxx'
aws ec2 describe-subnets --filters "Name=vpc-id,Values=vpc-xxxxxxxxxxxxxxxxx"
# subnets within the 'us-east-1a' availability zone
aws ec2 describe-subnets --filters "Name=availability-zone,Values=us-east-1a"
# display only their IDs and CIDR blocks
aws ec2 describe-subnets --query 'Subnets[*].{ID:SubnetId, CIDR:CidrBlock}'That's it, we now have subnets that we can use to house our resources.
Gateways
Our setup includes an Internet and a NAT gateway to handle different types of traffic. Let's go ahead and create these resources, starting with the internet gateway:
Go to "Internet Gateways" on your VPC dashboard;
Click "Create internet gateway";
Enter the name and click "Create";
Attach it to your VPC by selecting the IGW, clicking "Actions," and then "Attach to VPC";
You can also run the following CLI commands:
# create an internet gateway
aws ec2 create-internet-gateway
# attach the gateway to your VPC
aws ec2 attach-internet-gateway --vpc-id VPC_ID --internet-gateway-id IGW_IDBoto3
import boto3
ec2 = boto3.resource('ec2')
internet_gateway = ec2.create_internet_gateway()
vpc = ec2.Vpc('VPC_ID')
vpc.attach_internet_gateway(InternetGatewayId=internet_gateway.id)Next, create a NAT gateway which resources in private subnets will use:
Go to "NAT Gateways";
Click "Create NAT gateway";
Select a public subnet and allocate an Elastic IP (a NAT gateway needs an Elastic IP address);
Click "Create a NAT Gateway";
To create a NAT gateway using the AWS CLI, you need to perform two main steps:
Allocate an Elastic IP address;
Create the NAT gateway and associate that Elastic IP with it;
Here are the commands for each step:
# allocate an elastic IP address
aws ec2 allocate-address --domain vpc
# create a NAT gateway
aws ec2 create-nat-gateway --subnet-id SUBNET_ID --allocation-id EIP_ALLOCATION_IDBoto3
import boto3
ec2 = boto3.client('ec2')
# Allocate an Elastic IP address for the NAT Gateway
eip_response = ec2.allocate_address(Domain='vpc')
# Get the Allocation ID from the response
allocation_id = eip_response['AllocationId']
# Create a NAT Gateway and associate the allocated Elastic IP address with it
nat_gateway_response = ec2.create_nat_gateway(
SubnetId='SUBNET_ID', # Replace 'SUBNET_ID' with your actual Subnet ID
AllocationId=allocation_id # Use the Allocation ID from the previous response
)Route tables
A route table for a VPC is normally created when you create a VPC, so you don't need to create one manually. All you need to do is add routes to that table. However, if needed, you can do the following to create a route table:
Go to "Route Tables".
Click "Create route table".
Enter the name, select your VPC, and click "Create".
In the next step, we need to create routes in this route table. Let's start with routes to the internet gateway:
Select the route table you want to modify;
In the details pane, click on the "Routes" tab;
Click on "Edit routes";
Click on "Add route";
For Destination, enter
0.0.0.0/0to cover all IPv4 addresses or::/0for all IPv6 addresses;For Target, select "Internet Gateway" and then choose the ID of your internet gateway from the list;
Click on "Save routes";
For the NAT gateway, repeat the same steps but this time:
For Target, select "NAT Gateway" and then choose the ID of your NAT gateway;
The local route for communication within the VPC is added by default when the VPC is created. It allows resources within your VPC to communicate with each other. It typically has the destination set to the VPC's CIDR block and the target set to "local." You normally don't need to add this route manually, but to verify its existence:
In the VPC console, go to "Route Tables".
Select your route table.
Click on the "Routes" tab.
Confirm there is a route with the destination set to your VPC's CIDR (e.g.,
10.0.0.0/16) and the target set to "local".
If you'd rather use the CLI, AWS provides the following commands:
# if you use PowerShell as your shell, replace backslashes (\) with backticks (`) as line seperators
# create a route table in a VPC
aws ec2 create-route-table \
--vpc-id VPC_ID
# add a route for internet-bound traffic to go through the Internet Gateway
aws ec2 create-route \
--route-table-id rtb-xxxxxxxx \
--destination-cidr-block 0.0.0.0/0 \
--gateway-id igw-xxxxxxxx
use
# while not necessary, add a local route for communication within the VPC
aws ec2 create-route \
--route-table-id rtb-xxxxxxxx \
--destination-cidr-block 10.0.0.0/16 \
--gateway-id localBoto3
import boto3
# Create an EC2 client
ec2 = boto3.client('ec2')
# Replace 'VPC_ID' with your actual VPC ID
vpc_id = 'VPC_ID'
# Create a route table
route_table_response = ec2.create_route_table(VpcId=vpc_id)
route_table_id = route_table_response['RouteTable']['RouteTableId']
# Replace 'IGW_ID' with your actual Internet Gateway ID
internet_gateway_id = 'IGW_ID'
# Add a route for internet-bound traffic to go through the Internet Gateway
ec2.create_route(
RouteTableId=route_table_id,
DestinationCidrBlock='0.0.0.0/0',
GatewayId=internet_gateway_id
)
# Replace 'NAT_ID' with your actual NAT Gateway ID
nat_gateway_id = 'NAT_ID'
# Add a route for private subnet instances to access the internet via the NAT Gateway
ec2.create_route(
RouteTableId=route_table_id,
DestinationCidrBlock='0.0.0.0/0',
NatGatewayId=nat_gateway_id
)
# The local route for communication within the VPC is added by default.
# However, if you need to add it manually or to a new route table, you can do so like this:
ec2.create_route(
RouteTableId=route_table_id,
DestinationCidrBlock='10.0.0.0/16',
GatewayId='local'
)Now, traffic will be routed based on the rules defined in this route table.
Security groups and ACLs
Finally, let's secure our environment with security groups and access control lists! Let's begin by creating a security group:
Navigate to the VPC Dashboard;
Go to "Security Groups".
Click "Create security group".
Enter a descriptive "Name tag" for the security group, like "WebServerSG" for a web server security group.
Provide a "Description" for your security group that indicates its purpose.
Select the VPC where this security group will be used.
Click "Create" to create the security group.
With the security group created, select it from the list and click on the "Inbound Rules" tab.
Click "Edit rules" to add new inbound rules. For a web server, you might allow TCP traffic on port 80 (HTTP) and port 443 (HTTPS) from any source, which would be
0.0.0.0/0;If necessary, add outbound rules by selecting the "Outbound Rules" tab and clicking "Edit Rules";
Next, let's create an ACL:
Go to the VPC Dashboard in the console;
Select "Network ACLs";
Click the "Create network ACL";
Provide a "Name" for the ACL and select the VPC where it will be associated;
Click "Create" to create the Network ACL;
Once created, select it and click the "Inbound Rules" tab.
Click "Edit rules" to add new rules. For example, you may want to allow inbound HTTP traffic (port 80) from any IP address; set the rule number, protocol, port range, and source CIDR block accordingly.
Similarly, add outbound rules by selecting the "Outbound Rules" tab.
You can also use CLI commands to perform these operations. These are:
# if you use PowerShell as your shell, replace backslashes (\) with backticks (`) as line seperators
# Create a security group named "WebServerSG" in the specified VPC
aws ec2 create-security-group \
--group-name WebServerSG \
--description "Web Server Security Group" \
--vpc-id VPC_ID
# Authorize inbound HTTP access on port 80 to the "WebServerSG" security group
aws ec2 authorize-security-group-ingress \
--group-id SG_ID \
--protocol tcp \
--port 80 \
--cidr 0.0.0.0/0
# Create a Network ACL in the specified VPC
aws ec2 create-network-acl --vpc-id VPC_ID
# Add an inbound rule to allow HTTP traffic on port 80 to the Network ACL
aws ec2 create-network-acl-entry \
--network-acl-id ACL_ID \
--ingress \
--rule-number 100 \
--protocol tcp \
--port-range From=80,To=80 \
--cidr-block 0.0.0.0/0 \
--rule-action allowBoto3
import boto3
# Create an EC2 client
ec2 = boto3.client('ec2')
# Replace 'VPC_ID' with the actual ID of your VPC
vpc_id = 'VPC_ID'
# Create a security group named "WebServerSG" in the specified VPC
security_group_response = ec2.create_security_group(
GroupName='WebServerSG',
Description='Web Server Security Group',
VpcId=vpc_id
)
security_group_id = security_group_response['GroupId']
# Authorize inbound HTTP access on port 80 to the "WebServerSG" security group
ec2.authorize_security_group_ingress(
GroupId=security_group_id,
IpPermissions=[
{
'IpProtocol': 'tcp',
'FromPort': 80,
'ToPort': 80,
'IpRanges': [{'CidrIp': '0.0.0.0/0'}]
}
]
)
# Create a Network ACL in the specified VPC
network_acl_response = ec2.create_network_acl(VpcId=vpc_id)
network_acl_id = network_acl_response['NetworkAcl']['NetworkAclId']
# Add an inbound rule to allow HTTP traffic on port 80 to the Network ACL
ec2.create_network_acl_entry(
NetworkAclId=network_acl_id,
Ingress=True,
RuleNumber=100,
Protocol='tcp',
PortRange={
'From': 80,
'To': 80
},
CidrBlock='0.0.0.0/0',
RuleAction='allow'
)Conclusion
And there you have it—a journey through the AWS networking labyrinth! We've acted as modern-day explorers, mapping the flow of data across VPCs, subnets, and various gateways. We've protected our valuable information with security groups and network ACLs, ensuring only approved packets from authorized sources reach their intended destinations. Now, whether you're using the console or CLI, you're equipped to build networks to support your applications, no matter the size.
While creating a VPC does not incur charges, you will incur fees for additional resources like NAT Gateways and Elastic IPs not attached to a running instance. Be sure to delete these resources once you finish practicing.