Deploying a backend and frontend application on AWS using Terraform involves several steps, each integral to creating a robust and scalable environment. This guide will walk you through setting up your infrastructure on AWS with Terraform, focusing on creating resources and using modules for a clear, maintainable approach.

Prerequisites

  • AWS Account: Ensure you have an AWS account with the necessary permissions.
  • Terraform installed on your local machine
  • AWS CLI installed: Install AWS CLI and configure it with your AWS credentials.

Initialize your Terraform Project

Create a new directory for your Terraform project and initialize it:

mkdir my-terraform-project
cd my-terraform-project
terraform init

Define AWS Provider

Create a file provider.tf and define the AWS provider:

provider "aws" {
  region = "us-east-1"
}

Set Up the VPC

Define a Virtual Private Cloud (VPC) in vpc.tf:

resource "aws_vpc" "my_vpc" {
  cidr_block = "10.0.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true
  tags = {
    Name = "my_vpc"
  }
}

Create Subnets

Create your subnets.tf file and create the public and private subnets inside it.

resource "aws_subnet" "public_subnet" {
  vpc_id     = aws_vpc.my_vpc.id
  cidr_block = "10.0.1.0/24"
  map_public_ip_on_launch = true
  availability_zone = "us-east-1a"
  tags = {
    Name = "public_subnet"
  }
}

resource "aws_subnet" "private_subnet" {
  vpc_id     = aws_vpc.my_vpc.id
  cidr_block = "10.0.2.0/24"
  availability_zone = "us-east-1b"
  tags = {
    Name = "private_subnet"
  }
}

Create an Internet Gateway and Route Tables

Define an internet Gateway in igw.tf:

resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.my_vpc.id
  tags = {
    Name = "my_igw"
  }
}

resource "aws_route_table" "public_route_table" {
  vpc_id = aws_vpc.my_vpc.id

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

  tags = {
    Name = "public_route_table"
  }
}

Associate the route table with the public subnet in route_table.tf:

resource "aws_route_table_association" "public" {
  subnet_id      = aws_subnet.public_subnet.id
  route_table_id = aws_route_table.public_route_table.id
}

Configure Security Groups

Create a new file called security_groups.tf to define your security groups:

resource "aws_security_group" "allow_web" {
  name        = "allow_web"
  description = "Allow Web inbound traffic"
  vpc_id      = aws_vpc.my_vpc.id

  ingress {
    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"]
  }
}

Deploy EC2 Instances

Create EC2 instances for your frontend and backend in ec2_instance.tf:

resource "aws_instance" "frontend_instance" {
  ami           = "ami-123456"
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.public_subnet.id
  security_groups = [aws_security_group.allow_web.id]

  tags = {
    Name = "frontend_instance"
  }
}

resource "aws_instance" "backend_instance" {
  ami           = "ami-123456"
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.private_subnet.id

  tags = {
    Name = "backend_instance"
  }
}

Apply the Configuration

Before you apply your configuration, you can compare the current state of your infrastructure with the desired state defined in your configuration files using terraform plan This ensures that resources are created, updated, or deleted in the correct sequence.

Run the following command to apply your Terraform configuration:

terraform apply

Implementing Terraform Module

The Terraform module is a great practice for creating reusable, maintainable, and organized infrastructure code. We can modify the Terraform code above to include modules for our backend and frontend applications on AWS.

Define the Module Structure

Firstly we will structure our Terraform configuration to use modules. Here’s a basic directory structure:

my-terraform-project/
│
├── main.tf
├── variables.tf
├── outputs.tf
│
├── modules/
    ├── vpc/
    │   ├── main.tf
    │   ├── variables.tf
    │   └── outputs.tf
    │
    ├── security/
    │   ├── main.tf
    │   ├── variables.tf
    │   └── outputs.tf
    │
    └── ec2/
        ├── main.tf
        ├── variables.tf
        └── outputs.tf

Define Modules

In your modules/vpc/main.tf define your VPC module configuration.

resource "aws_vpc" "my_vpc" {
  cidr_block = var.cidr_block
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "my_vpc"
  }
}

Define your Security Module (modules/security/main.tf)

resource "aws_security_group" "allow_web" {
  name        = "allow_web"
  description = "Allow Web inbound traffic"
  vpc_id      = var.vpc_id

  ingress {
    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"]
  }
}

Define your EC2 Module (modules/ec2/main.tf)

resource "aws_instance" "app_instance" {
  ami           = var.ami_id
  instance_type = var.instance_type
  subnet_id     = var.subnet_id
  security_groups = [var.security_group_id]

  tags = {
    Name = var.instance_name
  }
}

Define the main configuration using Modules (`main.tf`)

module "vpc" {
  source      = "./modules/vpc"
  cidr_block  = "10.0.0.0/16"
}

module "security" {
  source   = "./modules/security"
  vpc_id   = module.vpc.my_vpc.id
}

module "frontend_instance" {
  source          = "./modules/ec2"
  ami_id          = "ami-123456"
  instance_type   = "t2.micro"
  subnet_id       = module.vpc.public_subnet.id
  security_group_id = module.security.allow_web.id
  instance_name   = "frontend_instance"
}

module "backend_instance" {
  source          = "./modules/ec2"
  ami_id          = "ami-123456"
  instance_type   = "t2.micro"
  subnet_id       = module.vpc.private_subnet.id
  security_group_id = module.security.allow_web.id
  instance_name   = "backend_instance"
}

Define Variables and Outputs

In each module, you need to define variables.tf and outputs.tf to manage inputs and outputs. This ensures that each module can be used flexibly in different environments.

Apply the Configuration

Run the commands to initialize Terraform and apply the configuration:

terraform init
terraform apply

Conclusion

Using modules in Terraform allows you to combine different parts of your AWS infrastructure, such as VPC, security settings, and EC2 instances, into reusable components. This approach enhances your organization, scalability, and maintainability of your Terraform code, particularly for complex deployments like a full-stack application.