Explain VPC in AWS

Explain VPC in AWS

VPC in AWS and example set-up with terraform

Overview

I am kinda confused about the concept of VPC and subnet when I have to set up AWS infrastructure with terrafrom. So I decide to dig keep on this concept. The blog will follow the following structure

  • refresher of IP address and subnet mask

  • go over concepts of VPC, subnet in AWS

  • terraform setup for RDS with VPC and subnet

IP address

Some basic knowledge of IP needs to be studied for networking

Some terms are:

  • IP address: for example 10.100.122.2, it consists of 4 segments and each segment ranges from 0-255. The IP address consists of the network id and host id. Each can take up a variable amount of segments.

The reason why we divide ip address into network id and host id is that it is going to take forever to find a specific device, therefore we divide and conquer to locate the host machine:

  • find the subnet first identified by network id

  • find the host by host id

The definition is here

  • network id: used to locate subnet.

  • host id: used to locate subnet

Then the question is how to find which portion of the IP address is network id and host id. The answer is a subnet mask.

  • subnet mask: subnet mask follows right behind the ip address with /24 the maximum number for a subnet mask is 32. The value stands for the number of ones in binary.

The IP address consists of the network id and host id. To determine the host id, you can do the following operation:

  • convert IP address and subnet mask to binary form

  • AND operation to obtain the host id

Check the IP calculator here to have a deeper understanding of this.

Now we have the basics to move forward to understand VPC.

Virtual Private Cloud

Virtual Private Cloud (VPC) is a secure and private cloud on a public cloud provider. It sounds like a tongue twister. VPC is just a secure network dedicated to the aws account holder, managed by AWS.

Let's consider you create a web-application with

  • load balancer

  • EC2 to host an instance of your web app

  • RDS to store data

You need to deploy it to two availability zone within the ca-central-1 region to improve fault tolerance.

Under ca-central-1, everything you have created, by default, will live in a VPC. The availability zone lives inside the VPC. Each availability zone hosts an instance of your web server.

Each subnet contains an instance of EC2, load balancer and RDS. Between the subset, they are free to talk to each other, even if the two subnets are not in the same availability zone. Your services are very secure now!

However, from a practical point of view, your web app lives in a VPC that blocks traffic from users. Therefore, no one can use your app.

I made the mistake when I spin up RDS for testing but without public access, since they live in a private subnet.

Lucky for us, you can control the subnet to be either private or public. The way to do this is to set up a gateway.

You typically set up an internet gateway for your load balancer so users can reach it but no one can SSH into your ec2 instance or rds.

Terraform set-up RDS

Let's set up AWS RDS with proper VPC and subnet.

Even if you don't specify VPC, one by default will be set for you.

# configured aws provider with proper credentials
provider "aws" {
  region  = "ca-canada-1"
  profile = "terraform"
}

# create default vpc if one does not exit
resource "aws_default_vpc" "default_vpc" {

  tags = {
    Name = "default vpc"
  }
}

# use data source to get all avalablility zones in region
data "aws_availability_zones" "available_zones" {}


# create a default subnet in the first az if one does not exit
resource "aws_default_subnet" "subnet_az1" {
  availability_zone = aws_availability_zones.available_zones.names[0]
}

# create a default subnet in the second az if one does not exit
resource "aws_default_subnet" "subnet_az2" {
  availability_zone = aws_availability_zones.available_zones.names[1]
}

# create security group for the web server
resource "aws_security_group" "webserver_security_group" {
  name        = "webserver security group"
  description = "enable http access on port 80"
  vpc_id      = aws_default_vpc.default_vpc.id

  ingress {
    description      = "http access"
    from_port        = 80
    to_port          = 80
    protocol         = "tcp"
    cidr_blocks      = ["0.0.0.0/0"]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = -1
    cidr_blocks      = ["0.0.0.0/0"]
  }

  tags   = {
    Name = "webserver security group"
  }
}

# create security group for the database
resource "aws_security_group" "database_security_group" {
  name        = "database security group"
  description = "enable mysql/aurora access on port 3306"
  vpc_id      = aws_default_vpc.default_vpc.id

  ingress {
    description      = "postgres"
    from_port        = 5432
    to_port          = 5432
    protocol         = "tcp"
# allow traffic to RDS from webserver
    security_groups  = [aws_security_group.webserver_security_group.id]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = -1
    cidr_blocks      = ["0.0.0.0/0"]
  }

  tags   = {
    Name = "database security group"
  }
}


# create the subnet group for the rds instance
resource "aws_db_subnet_group" "database_subnet_group" {
  name         = "database-subnets"
  subnet_ids   = [aws_default_subnet.subnet_az1.id, aws_default_subnet.subnet_az2.id]
  description  = "subnets for database instance"

  tags   = {
    Name = "database-subnets"
  }
}

# create the rds instance
resource "aws_db_instance" "db_instance" {
  engine                  = "postgres"
  engine_version          = "14.7"
  multi_az                = false
  identifier              = "rds-database-instance"
  username                = "admin"
  password                = "password"
  instance_class          = "db.t3.micro"
  allocated_storage       = 200
# 无限套娃,subnet放入group
  db_subnet_group_name    = aws_db_subnet_group.database_subnet_group.name
  vpc_security_group_ids  = [aws_security_group.database_security_group_id]
# spin up in one az zone, multi_az = false
  availability_zone       = data.aws_availability_zones.available_zones.names[0]
  db_name                 = "applicationdb"
# when you delete rds instance, will you have a final snapshot of it?
  skip_final_snapshot     = false
}

Before you create a database instance, you need to

  • create VPC,

  • create AZ

  • create a subset within different AZ

  • create a web server security group to allow traffic at port 80 to interact with the user

  • create a database security group and connect it with the web server security group so it can talk to the database

  • create your database instance

Summary

In this blog, we have covered

  • some breakdown of IP address and its relationship with subnet mask to calculate host id and network id

  • graphical illustration of the concept of VPC etc

  • terraform implementaion of setting up RDS with granular control

Reference