🌙

Creating a VPC with Subnet, Security Group, and EC2 instance using Terraform on AWS(Part04)

terraformawshashicorpdevopsInfrastructure as Code

Published on

May 2, 2023

Youtube Video link: https://youtu.be/BWhQOoS3PeI

Github repo: https://github.com/rashiddaha/terraform-course


In Previous ( Part 01 , Part 02 , Part 03 ) We have discussed Introduction to Terraform, Terraform, and aws cli Setup.In this Part 04 article, we will be discussing the fourth part of the Terraform series, where we will be creating a VPC with a Subnet, Security Group, and EC2 instance. We will create an Amazon Virtual Private Cloud (VPC) with a Subnet, Internet Gateway, and Custom Route Table using Terraform. We will then create an Elastic Network Interface (ENI) and associate an Elastic IP (EIP) address with it. We will also create a Security Group to allow ports 22, 80, and 443. Finally, we will create an EC2 instance and install Apache2 on it.



By the end of this article, you will have a solid understanding of creating a VPC with Subnet, Security Group, and EC2 instance using Terraform. Let’s begin by discussing the project outline.

Project Outline

The project outline consists of the following steps:

  1. Create VPC

    2. Create Internet Gateway

    3. Create Custom Route Table

    4. Create Subnet

    5. Associate Subnet with Route Table

    6. Create Security Group to allow ports: 22, 80, 443

    7. Assign ENI with IP

    8. Assign Elastic IP to ENI

    9. Create IAM Role to access S3

    10. Create Linux Server and Install/Enable Apache2

    10. Enable VCP Endpoint

    11. Setting up the Environment

    Before we start creating the infrastructure, we need to set up the environment. We will be using the AWS provider for Terraform to create the infrastructure in AWS. The first step is to create a directory and initialize it with Terraform:
mkdir terraform-vpc
cd terraform-vpc
terraform init

Next, we need to create a file with the .tf extension that will contain the Terraform code. We will name it main.tf.

touch main.tf

Now, open the main.tf file in your favorite text editor and paste the following code:

// Configure the AWS Provider
provider "aws" {
  region     = "us-east-1"
}

This code configures the AWS provider for Terraform and sets the region to us-east-1.

1. Creating a VPC

Now, we can move on to creating a VPC. We will use the aws_vpc resource to create the VPC.

# 1) Create VPC
resource "aws_vpc" "A" {
  cidr_block = "10.0.0.0/16"
  
  tags = {
    name = "Codewithmuh"
  }
}

Here, we create a VPC with the CIDR block 10.0.0.0/16. We also add a tag with the name Codewithmuh to identify the VPC.

2. Creating an Internet Gateway

Next, we need to create an Internet Gateway to allow our VPC to communicate with the Internet. We will use the aws_internet_gateway resource for this.

# 2) Create Internet Gateway
resource "aws_internet_gateway" "GW_Codewithmuh" {
  vpc_id = aws_vpc.A.id

  tags = {
    Name = "IG_Codewithmuh"
  }
}

Here, we create an Internet Gateway and associate it with the VPC we created earlier. We also add a tag to identify the Internet Gateway.

3.Creating a Custom Route Table

After creating the VPC and the Internet Gateway, we can create a custom route table to specify how traffic should be routed between the VPC and the internet. In this case, we want to route all internet-bound traffic through the Internet Gateway we just created.

To create a custom route table in Terraform, we use the aws_route_table resource. We specify the VPC ID using the vpc_id attribute and then define a default route to the internet gateway using the route block:

# 3) Create Route Table
resource "aws_route_table" "RT_Codewithmuh" {
  vpc_id = aws_vpc.A.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.GW_Codewithmuh.id
  }

  route {
    ipv6_cidr_block = "::/0"
    gateway_id = aws_internet_gateway.GW_Codewithmuh.id
  }

  tags = {
    Name = "RT_Codewithmuh"
  }
}

Here, we create a aws_route_table resource named "RT_Codewithmuh". We set the vpc_id attribute to the ID of the VPC we created earlier using aws_vpc.A.id.
Next, we define a default route to the internet gateway using the route block. We specify a CIDR block of 0.0.0.0/0, which matches all IP addresses, and set the gateway_id attribute to the ID of the internet gateway we created earlier using aws_internet_gateway.GW_Codewithmuh.id.

Finally, we add a tags block to the resource to give it a descriptive name.

4. Creating a Subnet

Now that we have a VPC and a route table, we can create a subnet for our resources. In this case, we want to create a subnet in the us-east-1a availability zone with a CIDR block of 10.0.0.0/24.

To create a subnet in Terraform, we use the aws_subnet resource. We specify the VPC ID using the vpc_id attribute, the CIDR block using the cidr_block attribute, and the availability zone using the availability_zone attribute:

# 4) Create Subnet
resource "aws_subnet" "Subnet_Codewithmuh" {
  vpc_id     = aws_vpc.A.id
  cidr_block = "10.0.0.0/24"
  availability_zone = "us-east-1a"
  depends_on = [aws_internet_gateway.GW_Codewithmuh]

  tags = {
    Name = "Subnet_Codewithmuh"
  }
}

Here, we create a aws_subnet resource named "Subnet_Codewithmuh". We set the vpc_id attribute to the ID of the VPC we created earlier using aws_vpc.A.id, the cidr_block attribute to 10.0.0.0/24, and the availability_zone attribute to "us-east-1a". We also set the depends_on attribute to the internet gateway resource to ensure that the subnet is created after the internet gateway.
Finally, we add a tags block to the resource to give it a descriptive name.

5. Associate Subnet with Route Table

To associate the Subnet with the Route Table, we will use the ‘aws_route_table_association’ resource. In the code, we have already created the Subnet and the Route Table. We will use their IDs to create the association. Add the following code to your project:

# 5) Associate Subnet with Route Table
resource "aws_route_table_association" "RT_to_Subnet" {
  subnet_id      = aws_subnet.Subnet_Codewithmuh.id
  route_table_id = aws_route_table.RT_Codewithmuh.id
}

We have created a new resource called ‘aws_route_table_association’ and assigned the Subnet ID to ‘subnet_id’ and Route Table ID to ‘route_table_id’. This will create an association between the Subnet and the Route Table.

6. Create a Security Group to allow ports: 22, 80, 443

# 6) Create Security Group to allow ports: 22, 80, 443
resource "aws_security_group" "SG_Codewithmuh" {
  name        = "SG_Codewithmuh"
  description = "Allow SSH, HTTP, HTTPS inbound traffic"
  vpc_id      = aws_vpc.A.id

  ingress {
    description = "HTTPS from VPC"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "HTTP from VPC"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "SSH from VPC"
    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 = "SG_Codewithmuh"
  }
}

Here, we are using the aws_security_group resource to create a security group named SG_Codewithmuh. We are providing a name prefix for the security group, along with the VPC ID of the VPC we created earlier (aws_vpc.A.id).

We are also defining ingress rules to allow traffic on ports 22 (SSH) and 80 (HTTP) from any IP address (0.0.0.0/0), and egress rules to allow all outbound traffic.

Finally, we are adding a tag to the security group for easy identification.

7. Assign ENI with IP

# 7) Assign ENI with IP
resource "aws_network_interface" "ENI_A" {
subnet_id = aws_subnet.Subnet_Codewithmuh.id
private_ips = ["10.0.0.10"]
security_groups = [aws_security_group.SG_Codewithmuh.id]
}

Creates an ENI with the specified private IP address (10.0.0.10) in the subnet identified by the resource aws_subnet.Subnet_Codewithmuh.id.

Associates the ENI with the security group identified by the resource aws_security_group.SG_Codewithmuh.id.

8. Assign Elastic IP to ENI

# 8) Assign Elastic IP to ENI
resource "aws_eip" "EIP_A" {
  vpc                       = true
  network_interface         = aws_network_interface.ENI_A.id
  associate_with_private_ip = "10.0.0.10"
  depends_on                = [aws_internet_gateway.GW_Codewithmuh, aws_instance.Instance_A]
  
  tags = {
    Name = "EIP_A"
  }
}
  • Creates an Elastic IP address and associates it with the ENI identified by the resource aws_network_interface.ENI_A.id.
  • Associates the Elastic IP address with the private IP address 10.0.0.10 on the ENI.
  • Specifies that the Elastic IP address is associated with a VPC.
  • Sets a tag for the Elastic IP address with the name “EIP_A”.
  • Depends on the aws_internet_gateway.GW_Codewithmuh resource to be created first.

9. Create IAM Role to access S3

# 9) Creat IAM Role to acces S3
resource "aws_iam_role" "EC2-S3" {
  name = "EC2-S3"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF

  tags = {
      Name = "EC2-S3"
  }
}

// IAM Profile
resource "aws_iam_instance_profile" "EC2-S3_Profile" {
  name = "EC2-S3_Profile"
  role = "${aws_iam_role.EC2-S3.name}"
}

// IAM Policy
resource "aws_iam_role_policy" "EC2-S3_Policy" {
  name = "test_policy"
  role = "${aws_iam_role.EC2-S3.id}"

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:*"
      ],
      "Resource": "*"
    }
  ]
}
EOF
 
}
  • Creates an IAM role named “EC2-S3” with a trust policy that allows EC2 instances to assume the role.
  • Sets a tag for the IAM role with the name “EC2-S3”.
  • Creates an instance profile named “EC2-S3_Profile” and associates it with the IAM role created by the resource aws_iam_role.EC2-S3.
  • Creates an IAM policy named “test_policy” and associates it with the IAM role created by the resource aws_iam_role.EC2-S3.
  • Grants permission to perform all actions (s3:) on all resources () in the S3 service.

9. Create Linux Server and Install/Enable Apache2

# 10) Create Linux Server and Install/Enable Apache2
resource "aws_instance" "Instance_A" {
  ami                  = "ami-0947d2ba12ee1ff75" 
  instance_type        = "t2.micro"
  availability_zone    = "us-east-1a"
  key_name             = "Codewithmuh"
  iam_instance_profile = "${aws_iam_instance_profile.EC2-S3_Profile.name}"
  
  network_interface {
    device_index = 0
    network_interface_id = aws_network_interface.ENI_A.id
  }

  user_data = <<-EOF
    #!/bin/bash
    sudo yum update -y
    sudo yum install -y httpd.x86_64
    sudo systemctl start httpd.service
    sudo systemctl enable httpd.service
    sudo aws s3 sync s3://awsbucketbeta00/website /var/www/html 
  EOF

  tags = {
    Name = "Codewithmuh_1.0"
  }
}
  • Creates an EC2 instance using the Amazon Linux 2 AMI (ami-0947d2ba12ee1ff75).
  • Sets the instance type to t2.micro.
  • Specifies the availability zone to launch the instance in (us-east-1a).
  • Specifies the key pair to use for SSH access (Codewithmuh).
  • Specifies the IAM instance profile created in resource #9 (aws_iam_instance_profile.EC2-S3_Profile.name).
  • Associates the instance with the network interface created in resource #7 (aws_network_interface.ENI_A.id).
  • Defines the user_data script to install and enable Apache2, start the Apache2 service, and sync the contents of an S3 bucket to the Apache2 document root directory (/var/www/html).
  • Sets a tag for the instance with the name “Codewithmuh_1.0”.

11. Enable Vpc Endpoint

# 11) Enable VCP Endpoint
resource "aws_vpc_endpoint" "s3" {
  vpc_id       = aws_vpc.A.id
  service_name = "com.amazonaws.us-east-1.s3"

  tags = {
    Environment = "test"
  }
}
  • Creates an S3 VPC endpoint in the VPC created in resource #2 (aws_vpc.A.id).
  • Specifies the service name for the S3 endpoint (com.amazonaws.us-east-1.s3).
  • Sets a tag for the endpoint with the name “test”. So we are done.

    All code is in one file, which you can find it here.

Now let’s Validate, Plan and Apply.
Before we do, we need to create keypair, which we have defined in our Point 10.
You can create an AWS key pair using the AWS Command Line Interface (CLI) with the following command:
aws ec2 create-key-pair --key-name <KEY-PAIR-NAME> --query 'KeyMaterial' --output text > <KEY-PAIR-NAME>.pem

Replace with the name you want to give to your key pair. This will create a new key pair with the specified name and save the private key to a file with the same name and a .pem extension in the current directory.

Now it's time to Build.
Run These commands.

1. terraform init
2. terraform validate # run this command to validate your code

3. terraform plan  # run this command to check the infrastructure you are going to create

4. terraform apply # run this command to create infrastructure

Conclusion:

In this project, we have successfully created a VPC and configured various resources within it using the Terraform infrastructure as code tool. The resources created include an internet gateway, a custom route table, a subnet, a security group to allow inbound traffic on ports 22, 80, and 443, an Elastic Network Interface (ENI) with a private IP address, an Elastic IP (EIP) associated with the ENI, and an IAM role for accessing Amazon S3. Additionally, we have installed and enabled Apache2 on a Linux server and enabled a VPC endpoint.

The use of Terraform enables us to automate the deployment and management of infrastructure resources, allowing us to provision resources quickly and efficiently. This project provides an excellent example of how Terraform can be used to build, manage, and update infrastructure resources in an organized and scalable way.



You can Folllow me at:

Note: Thank You for reading!

Note: For Quries and for projects work you cna react out to me at codewithmuh@gmail.com