Deploy Custom VPC and Resources in AWS using Terraform

Project Date April 18th, 2024
Code Available at lakshminkmeda.github
Tools AWS, Terraform

In the journey of Cloud learning, I've used the console, cli and SAM cli to deploy the resources in AWS, always wanted to try vendor neutral platform like Terraform due to it declarative statements and CRUD (Create, Read, Update, Delete) actions. Terraform provides the same result as with SAM but makes it easier to manage the resources using the Terraform STATE file.


Step 1: Terraform Code for creating a VPC

Terraform allows resource deployment in the cloud for several cloud providers such as AWS, Google Cloud, Azure etc. using provider specific plugins. Hence the first step is to declare the cloud provider which sets up the provider specific environment.

It's always a good idea to draw a simple map to understand a deployment, as deploying multiple resources can quickly become complicated to manage and track. Here is a simple design of the planned deployment.



The code below shows a simple deployment of a custom VPC in AWS with a cidr block of 10.0.0.0/16.

                terraform {
                    required_providers {              -
                      aws = {                           -
                        source  = "hashicorp/aws"         - Declare Provider Specific Plugins
                        version = "> 4.16"              -
                      }                               -
                    }
                  }
                  
                  provider "aws" {
                    region = "eu-north-1"                 - Declare key variables
                  }
                  
                  # Create a Custom VPC
                  resource "aws_vpc" "vpc_c9" {
                    cidr_block = "10.0.0.0/16"
                    tags       = { Name = "vpc_c9" }
                  }
            


Step 2: Terraform Code for creating VPC Resources


Once we have a VPC the next step is to create key resources which are:

  • Subnets
  • Internet Gateway for public access.
  • Route Tables.
  • Security Group to allow traffic to the VPC.

  •                 # Create a Subnet inside the VPC
                    resource "aws_subnet" "sub_c9" {
                        vpc_id                  = aws_vpc.vpc_c9.id
                        cidr_block              = "10.0.1.0/24"
                        availability_zone       = "eu-north-1b"
                        tags                    = { Name = "subnet_c9" }
                        map_public_ip_on_launch = true
                    }
    
                    # Create a Internet Gateway for the VPC
                    resource "aws_internet_gateway" "ig_c9" {
                        vpc_id     = aws_vpc.vpc_c9.id
                        depends_on = [aws_vpc.vpc_c9]
                        tags       = { Name = "gateway_c9" }
                    }
    
                    # Create a Route Table for the VPC
                    resource "aws_route_table" "rt_c9" {
                        vpc_id = aws_vpc.vpc_c9.id
                        tags   = { Name = "route_table_c9" }
                    }
    
                    # Associate the route table to the devices inside the Subnet
                    resource "aws_route_table_association" "public" {
                        subnet_id      = aws_subnet.sub_c9.id
                        route_table_id = aws_route_table.rt_c9.id
                    }
    
                    # Create a route to the Internet by pointing to the Internet Gateway
                    resource "aws_route" "rt_internet" {
                        destination_cidr_block = "0.0.0.0/0"
                        route_table_id         = aws_route_table.rt_c9.id
                        gateway_id             = aws_internet_gateway.ig_c9.id
                    }
    
                    # Create security group to allow traffic for EC2 instances
                    # Custom VPC's block all incoming/outgoing traffic by default
                    resource "aws_security_group" "sg_c9" {
                        name        = "sg_c9vpc"
                        description = "Security group to allow traffic to ec2 instances"
                        vpc_id      = aws_vpc.vpc_c9.id
                        ingress {
                            from_port   = "80"
                            to_port     = "80"
                            protocol    = "tcp"
                            cidr_blocks = ["0.0.0.0/0"]
                        }
                        ingress {
                            from_port   = "22"
                            to_port     = "22"
                            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 = "security_grp_c9" }
                    }
                

    Step 3: Create EC2 Instances


    The last step is to create a couple of ec2 instances to check the VPC and its accessibility from the internet. I've used ec2 instances based on different AMI's which could be changed according to the requirement. An user script is added to both the instances to install a Web Server which is used to check the functionality from the internet. The scripts can be found in the Github resorces.

    The code to deploy the ec2 instances is shown below.

                    # Create instance to act as a web server using UBUNTU AMI
                    resource "aws_instance" "test_server_ubulnx" {
                        ami                    = "ami-0914547665e6a707c"
                        instance_type          = "t3.micro"
                        vpc_security_group_ids = [aws_security_group.sg_c9.id]
                        subnet_id              = aws_subnet.sub_c9.id
                        key_name               = "terraform-demo"
                        user_data = file("${path.module}/ubuntu_script.sh")
                        tags = {
                            Name = "Ubuntu_web_server"
                        }
                    }
    
                    # Create instance to act as a web server using AMAZON LINUX AMI
                    resource "aws_instance" "test_server_amzlnx" {
                        ami                    = "ami-01dad638e8f31ab9a"
                        instance_type          = "t3.micro"
                        vpc_security_group_ids = [aws_security_group.sg_c9.id]
                        subnet_id              = aws_subnet.sub_c9.id
                        key_name               = "terraform-demo"
                        user_data = file("${path.module}/amzlnx_script.sh")
                        tags = {
                            Name = "AmzLnx_web_server"
                        }
                    }
                

    Now we have a VPC with a route to the internet using the Internet Gateway, route tables to route the traffic in the subnet, an Ubuntu and an Amazon Linux web servers which are accessible from the internet. The servers are accessible using the public_ip of the instances in any browser using the following format.

                    public_ip/index.html