Getting started with Amazon VPC using the AWS CLI - Amazon Virtual Private Cloud

Getting started with Amazon VPC using the AWS CLI

This tutorial guides you through creating a Virtual Private Cloud (VPC) using the AWS Command Line Interface (AWS CLI). You'll learn how to set up a VPC with public and private subnets, configure internet connectivity, and deploy EC2 instances to demonstrate a common web application architecture.

Prerequisites

Before you begin this tutorial, make sure you have the following:

  1. The AWS CLI. If you need to install it, follow the AWS CLI installation guide.

  2. Configured your AWS CLI with appropriate credentials. Run aws configure if you haven't set up your credentials yet.

  3. Basic understanding of networking concepts.

  4. Identity and access management for Amazon VPC to create and manage VPC resources in your AWS account.

Cost considerations

This tutorial creates AWS resources that may incur costs in your account. The primary cost comes from the NAT Gateway ($0.045 per hour plus data processing charges) and EC2 instances (t2.micro, approximately $0.0116 per hour each). If you complete this tutorial in one hour and then clean up all resources, the total cost will be approximately $0.07. For cost optimization in development environments, consider using a NAT Instance instead of a NAT Gateway, which can reduce costs significantly.

Let's verify that your AWS CLI is properly configured before proceeding.

aws configure list

You should see your AWS access key, secret key, and default region. Also, verify that you have the necessary permissions to create VPC resources.

aws sts get-caller-identity

This command displays your AWS account ID, user ID, and ARN, confirming that your credentials are valid.

Create a VPC

A Virtual Private Cloud (VPC) is a virtual network dedicated to your AWS account. In this section, you'll create a VPC with a CIDR block of 10.0.0.0/16, which provides up to 65,536 IP addresses.

Create the VPC

The following command creates a new VPC and assigns it a name tag.

aws ec2 create-vpc --cidr-block 10.0.0.0/16 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=MyVPC}]'

Take note of the VPC ID in the output. You'll need it for subsequent commands. For the purpose of this tutorial, we'll use "vpc-0123456789abcdef0" as an example VPC ID. Replace this with your actual VPC ID in all commands.

Enable DNS support and hostnames

By default, DNS resolution and DNS hostnames are disabled in a new VPC. Enable these features to allow instances in your VPC to resolve domain names.

aws ec2 modify-vpc-attribute --vpc-id vpc-0123456789abcdef0 --enable-dns-support aws ec2 modify-vpc-attribute --vpc-id vpc-0123456789abcdef0 --enable-dns-hostnames

These commands don't produce output if successful. Your VPC now has DNS support and hostname resolution enabled.

Create subnets

Subnets are segments of a VPC's IP address range where you can place groups of isolated resources. In this section, you'll create public and private subnets in two Availability Zones for high availability.

Get available Availability Zones

First, retrieve the Availability Zones available in your region.

aws ec2 describe-availability-zones

For this tutorial, we'll use the first two Availability Zones. Note their names from the output (e.g., "us-east-1a" and "us-east-1b").

Create public subnets

Public subnets are used for resources that need to be accessible from the internet, such as web servers.

aws ec2 create-subnet \ --vpc-id vpc-0123456789abcdef0 \ --cidr-block 10.0.0.0/24 \ --availability-zone us-east-1a \ --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=Public-Subnet-AZ1}]'

Note the subnet ID from the output. For this tutorial, we'll use "subnet-0123456789abcdef0" as an example for the first public subnet.

aws ec2 create-subnet \ --vpc-id vpc-0123456789abcdef0 \ --cidr-block 10.0.1.0/24 \ --availability-zone us-east-1b \ --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=Public-Subnet-AZ2}]'

Note the subnet ID from the output. For this tutorial, we'll use "subnet-0123456789abcdef1" as an example for the second public subnet.

Create private subnets

Private subnets are used for resources that should not be directly accessible from the internet, such as databases.

aws ec2 create-subnet \ --vpc-id vpc-0123456789abcdef0 \ --cidr-block 10.0.2.0/24 \ --availability-zone us-east-1a \ --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=Private-Subnet-AZ1}]'

Note the subnet ID from the output. For this tutorial, we'll use "subnet-0123456789abcdef2" as an example for the first private subnet.

aws ec2 create-subnet \ --vpc-id vpc-0123456789abcdef0 \ --cidr-block 10.0.3.0/24 \ --availability-zone us-east-1b \ --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=Private-Subnet-AZ2}]'

Note the subnet ID from the output. For this tutorial, we'll use "subnet-0123456789abcdef3" as an example for the second private subnet.

You now have four subnets: two public subnets and two private subnets, distributed across two Availability Zones.

Tip: When planning your CIDR blocks, ensure they don't overlap with your existing networks. For production environments, allocate enough IP addresses for future growth while keeping subnets reasonably sized for security and management.

Configure internet connectivity

To allow resources in your VPC to communicate with the internet, you need to create and attach an Internet Gateway. In this section, you'll set up internet connectivity for your VPC.

Create an Internet Gateway

An Internet Gateway enables communication between your VPC and the internet.

aws ec2 create-internet-gateway \ --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=MyIGW}]'

Note the Internet Gateway ID from the output. For this tutorial, we'll use "igw-0123456789abcdef0" as an example.

Attach the Internet Gateway to your VPC

After creating the Internet Gateway, attach it to your VPC.

aws ec2 attach-internet-gateway --internet-gateway-id igw-0123456789abcdef0 --vpc-id vpc-0123456789abcdef0

Create and configure route tables

Route tables contain rules (routes) that determine where network traffic is directed. First, create a route table for your public subnets.

aws ec2 create-route-table \ --vpc-id vpc-0123456789abcdef0 \ --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=Public-RT}]'

Note the route table ID from the output. For this tutorial, we'll use "rtb-0123456789abcdef0" as an example for the public route table.

Add a route to the Internet Gateway in the public route table.

aws ec2 create-route --route-table-id rtb-0123456789abcdef0 --destination-cidr-block 0.0.0.0/0 --gateway-id igw-0123456789abcdef0

Associate the public subnets with the public route table.

aws ec2 associate-route-table --route-table-id rtb-0123456789abcdef0 --subnet-id subnet-0123456789abcdef0 aws ec2 associate-route-table --route-table-id rtb-0123456789abcdef0 --subnet-id subnet-0123456789abcdef1

Now, create a route table for your private subnets.

aws ec2 create-route-table \ --vpc-id vpc-0123456789abcdef0 \ --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=Private-RT}]'

Note the route table ID from the output. For this tutorial, we'll use "rtb-0123456789abcdef1" as an example for the private route table.

Associate the private subnets with the private route table.

aws ec2 associate-route-table --route-table-id rtb-0123456789abcdef1 --subnet-id subnet-0123456789abcdef2 aws ec2 associate-route-table --route-table-id rtb-0123456789abcdef1 --subnet-id subnet-0123456789abcdef3

Create a NAT Gateway

A NAT Gateway allows instances in private subnets to initiate outbound traffic to the internet while preventing inbound traffic from the internet. This is essential for instances that need to download updates or access external services.

Allocate an Elastic IP

First, allocate an Elastic IP address for your NAT Gateway.

aws ec2 allocate-address --domain vpc

Note the Allocation ID from the output. For this tutorial, we'll use "eipalloc-0123456789abcdef0" as an example.

Create the NAT Gateway

Create a NAT Gateway in one of your public subnets using the allocated Elastic IP.

aws ec2 create-nat-gateway \ --subnet-id subnet-0123456789abcdef0 \ --allocation-id eipalloc-0123456789abcdef0 \ --tag-specifications 'ResourceType=natgateway,Tags=[{Key=Name,Value=MyNATGateway}]'

Note the NAT Gateway ID from the output. For this tutorial, we'll use "nat-0123456789abcdef0" as an example.

Wait for the NAT Gateway to become available before proceeding.

aws ec2 wait nat-gateway-available --nat-gateway-ids nat-0123456789abcdef0

Add a route to the NAT Gateway

Add a route to the NAT Gateway in the private route table to allow instances in private subnets to access the internet.

aws ec2 create-route --route-table-id rtb-0123456789abcdef1 --destination-cidr-block 0.0.0.0/0 --nat-gateway-id nat-0123456789abcdef0

Note: For production environments, consider creating a NAT Gateway in each Availability Zone where you have private subnets to eliminate single points of failure.

Configure subnet settings

Configure your public subnets to automatically assign public IP addresses to instances launched in them.

aws ec2 modify-subnet-attribute --subnet-id subnet-0123456789abcdef0 --map-public-ip-on-launch aws ec2 modify-subnet-attribute --subnet-id subnet-0123456789abcdef1 --map-public-ip-on-launch

This ensures that instances launched in your public subnets receive a public IP address by default, making them accessible from the internet.

Create security groups

Security groups act as virtual firewalls for your instances to control inbound and outbound traffic. In this section, you'll create security groups for web servers and database servers.

Create a security group for web servers

aws ec2 create-security-group \ --group-name WebServerSG \ --description "Security group for web servers" \ --vpc-id vpc-0123456789abcdef0

Note the security group ID from the output. For this tutorial, we'll use "sg-0123456789abcdef0" as an example for the web server security group.

Allow HTTP and HTTPS traffic to your web servers.

aws ec2 authorize-security-group-ingress --group-id sg-0123456789abcdef0 --protocol tcp --port 80 --cidr 0.0.0.0/0 aws ec2 authorize-security-group-ingress --group-id sg-0123456789abcdef0 --protocol tcp --port 443 --cidr 0.0.0.0/0

Note: For production environments, restrict inbound traffic to specific IP ranges rather than allowing traffic from 0.0.0.0/0 (any IP address).

Create a security group for database servers

aws ec2 create-security-group \ --group-name DBServerSG \ --description "Security group for database servers" \ --vpc-id vpc-0123456789abcdef0

Note the security group ID from the output. For this tutorial, we'll use "sg-0123456789abcdef1" as an example for the database server security group.

Allow MySQL/Aurora traffic from web servers only.

aws ec2 authorize-security-group-ingress --group-id sg-0123456789abcdef1 --protocol tcp --port 3306 --source-group sg-0123456789abcdef0

This configuration ensures that only instances in the web server security group can connect to your database servers on port 3306, following the principle of least privilege.

Verify your VPC configuration

After creating all the necessary components, verify your VPC configuration to ensure everything is set up correctly.

Check your VPC

aws ec2 describe-vpcs --vpc-id vpc-0123456789abcdef0

Check your subnets

aws ec2 describe-subnets --filters "Name=vpc-id,Values=vpc-0123456789abcdef0"

Check your route tables

aws ec2 describe-route-tables --filters "Name=vpc-id,Values=vpc-0123456789abcdef0"

Check your Internet Gateway

aws ec2 describe-internet-gateways --filters "Name=attachment.vpc-id,Values=vpc-0123456789abcdef0"

Check your NAT Gateway

aws ec2 describe-nat-gateways --filter "Name=vpc-id,Values=vpc-0123456789abcdef0"

Check your security groups

aws ec2 describe-security-groups --filters "Name=vpc-id,Values=vpc-0123456789abcdef0"

These commands provide detailed information about each component of your VPC, allowing you to verify that everything is configured correctly.

Deploy EC2 instances

Now that you have created your VPC infrastructure, you can deploy EC2 instances to demonstrate how the architecture works. You'll launch a web server in a public subnet and a database server in a private subnet.

Create a key pair for SSH access

First, create a key pair to securely connect to your instances:

aws ec2 create-key-pair --key-name vpc-tutorial-key --query 'KeyMaterial' --output text > vpc-tutorial-key.pem chmod 400 vpc-tutorial-key.pem

This command creates a new key pair and saves the private key to a file with restricted permissions.

Find the latest Amazon Linux 2 AMI

Find the latest Amazon Linux 2 AMI to use for your instances:

aws ec2 describe-images --owners amazon \ --filters "Name=name,Values=amzn2-ami-hvm-*-x86_64-gp2" "Name=state,Values=available" \ --query "sort_by(Images, &CreationDate)[-1].ImageId" --output text

Note the AMI ID from the output. For this tutorial, we'll use "ami-0123456789abcdef0" as an example.

Launch a web server in the public subnet

Now, launch an EC2 instance in the public subnet to serve as a web server:

aws ec2 run-instances \ --image-id ami-0123456789abcdef0 \ --count 1 \ --instance-type t2.micro \ --key-name vpc-tutorial-key \ --security-group-ids sg-0123456789abcdef0 \ --subnet-id subnet-0123456789abcdef0 \ --associate-public-ip-address \ --user-data '#!/bin/bash yum update -y yum install -y httpd systemctl start httpd systemctl enable httpd echo "<h1>Hello from $(hostname -f)</h1>" > /var/www/html/index.html' \ --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=WebServer}]'

Note the instance ID from the output. For this tutorial, we'll use "i-0123456789abcdef0" as an example for the web server instance.

Launch a database server in the private subnet

Next, launch an EC2 instance in the private subnet to serve as a database server:

aws ec2 run-instances \ --image-id ami-0123456789abcdef0 \ --count 1 \ --instance-type t2.micro \ --key-name vpc-tutorial-key \ --security-group-ids sg-0123456789abcdef1 \ --subnet-id subnet-0123456789abcdef2 \ --user-data '#!/bin/bash yum update -y yum install -y mariadb-server systemctl start mariadb systemctl enable mariadb' \ --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=DBServer}]'

Note the instance ID from the output. For this tutorial, we'll use "i-0123456789abcdef1" as an example for the database server instance.

Access your web server

Once your web server instance is running, you can access it using its public IP address:

aws ec2 describe-instances \ --instance-ids i-0123456789abcdef0 \ --query 'Reservations[0].Instances[0].PublicIpAddress' \ --output text

This command will output the public IP address of your web server. For this tutorial, we'll use "203.0.113.10" as an example.

You can now open this URL in your web browser: http://203.0.113.10

Connect to your instances via SSH

To connect to your web server:

ssh -i vpc-tutorial-key.pem ec2-user@203.0.113.10

To connect to your database server, you need to SSH to your web server first and then to your database server:

# Get the private IP of the database server aws ec2 describe-instances \ --instance-ids i-0123456789abcdef1 \ --query 'Reservations[0].Instances[0].PrivateIpAddress' \ --output text

This command will output the private IP address of your database server. For this tutorial, we'll use "10.0.2.10" as an example.

# First SSH to web server, then to database server ssh -i vpc-tutorial-key.pem -A ec2-user@203.0.113.10 ssh ec2-user@10.0.2.10

This demonstrates the network architecture you've created: the web server is publicly accessible, while the database server is only accessible from within the VPC.

Troubleshooting

Here are some common issues you might encounter when creating a VPC and how to resolve them:

CIDR Block Overlaps

If you receive an error about CIDR block overlaps, ensure that the CIDR blocks for your VPC and subnets don't overlap with existing VPCs or subnets in your account.

Permission Errors

If you encounter permission errors, verify that your IAM user or role has the necessary permissions to create and manage VPC resources. You might need to attach the AmazonVPCFullAccess policy or create a custom policy with the required permissions.

Resource Limits

AWS accounts have default limits on the number of VPCs, subnets, and other resources you can create. If you hit these limits, you can request an increase through the AWS Support Center.

Dependency Failures During Cleanup

When cleaning up resources, you might encounter dependency errors if you try to delete resources in the wrong order. Always delete resources in the reverse order of creation, starting with the most dependent resources.

Clean up resources

When you're finished with your VPC, you can clean up the resources to avoid incurring charges. Delete the resources in the reverse order of creation to handle dependencies correctly.

Terminate EC2 instances

aws ec2 terminate-instances --instance-ids i-0123456789abcdef0 i-0123456789abcdef1 aws ec2 wait instance-terminated --instance-ids i-0123456789abcdef0 i-0123456789abcdef1

Delete the key pair

aws ec2 delete-key-pair --key-name vpc-tutorial-key rm vpc-tutorial-key.pem

Delete the NAT Gateway

aws ec2 delete-nat-gateway --nat-gateway-id nat-0123456789abcdef0 aws ec2 wait nat-gateway-deleted --nat-gateway-ids nat-0123456789abcdef0

Release the Elastic IP

aws ec2 release-address --allocation-id eipalloc-0123456789abcdef0

Delete security groups

aws ec2 delete-security-group --group-id sg-0123456789abcdef1 aws ec2 delete-security-group --group-id sg-0123456789abcdef0

Delete route tables

First, find the route table association IDs:

aws ec2 describe-route-tables --route-table-id rtb-0123456789abcdef0 aws ec2 describe-route-tables --route-table-id rtb-0123456789abcdef1

Then disassociate the route tables from the subnets (replace the association IDs with the ones from your output):

aws ec2 disassociate-route-table --association-id rtbassoc-0123456789abcdef0 aws ec2 disassociate-route-table --association-id rtbassoc-0123456789abcdef1 aws ec2 disassociate-route-table --association-id rtbassoc-0123456789abcdef2 aws ec2 disassociate-route-table --association-id rtbassoc-0123456789abcdef3

Then delete the route tables:

aws ec2 delete-route-table --route-table-id rtb-0123456789abcdef1 aws ec2 delete-route-table --route-table-id rtb-0123456789abcdef0

Detach and delete the Internet Gateway

aws ec2 detach-internet-gateway --internet-gateway-id igw-0123456789abcdef0 --vpc-id vpc-0123456789abcdef0 aws ec2 delete-internet-gateway --internet-gateway-id igw-0123456789abcdef0

Delete subnets

aws ec2 delete-subnet --subnet-id subnet-0123456789abcdef0 aws ec2 delete-subnet --subnet-id subnet-0123456789abcdef1 aws ec2 delete-subnet --subnet-id subnet-0123456789abcdef2 aws ec2 delete-subnet --subnet-id subnet-0123456789abcdef3

Delete the VPC

aws ec2 delete-vpc --vpc-id vpc-0123456789abcdef0

Going to production

This tutorial is designed to help you learn how to create a VPC using the AWS CLI. For production environments, consider the following security and architecture best practices:

  1. Security Group Rules: Restrict inbound traffic to specific IP ranges rather than allowing traffic from 0.0.0.0/0.

  2. High Availability: Deploy NAT Gateways in each Availability Zone where you have private subnets to eliminate single points of failure.

  3. Network ACLs: Implement Network ACLs as an additional layer of security beyond security groups.

  4. VPC Flow Logs: Enable VPC Flow Logs to monitor and analyze network traffic patterns.

  5. Resource Tagging: Implement a comprehensive tagging strategy for better resource management.

For more information on building production-ready architectures, refer to AWS Well-Architected Framework and AWS Security Best Practices.

Next steps

Now that you've created a VPC with public and private subnets, you can:

  1. Launch EC2 instances in your public or private subnets.

  2. Deploy load balancers to distribute traffic across multiple instances.

  3. Set up Auto Scaling groups for high availability and scalability.

  4. Configure RDS databases in your private subnets.

  5. Implement VPC peering to connect with other VPCs.

  6. Set up VPN connections to connect your VPC with your on-premises network.