# Build and Deploy a Full-Stack E-Commerce Application on AWS

## 1\. Introduction

In this project, we build and deploy a **full-stack e-commerce application** on **Amazon Web Services (AWS)**. The main objective is to understand how different AWS services work together to host a real-world application.

Instead of running the entire application on a single server, we use multiple AWS services for **networking, frontend hosting, authentication, backend deployment, databases, APIs, and notifications**.

This project provides practical experience in deploying applications, configuring AWS infrastructure, troubleshooting issues, and managing cloud resources.

---

## 2\. What Are We Building?

We are building an e-commerce application where users can **register, log in, browse products, manage their shopping cart, and place orders**.

The application consists of three main components:

- **Frontend:** Provides the user interface.
- **Backend:** Processes requests and handles application logic.
- **Database:** Stores application data.

AWS services connect these components and provide the infrastructure required to run the application securely and reliably.

---

## 3\. Technologies and AWS Services Used

The project uses the following technologies and AWS services.

| Technology / Service | Purpose |
| --- | --- |
| **React.js** | Builds the frontend user interface. |
| **Git** | Manages source code and version control. |
| **Docker** | Packages backend services into container images. |
| **Amazon VPC** | Provides an isolated network for AWS resources. |
| **Subnets and Route Tables** | Organize network resources and control traffic routing. |
| **Internet Gateway and NAT Gateway** | Provide the required internet connectivity for network resources. |
| **Amazon S3** | Stores frontend build files and product images. |
| **Amazon CloudFront** | Delivers frontend content to users. |
| **Amazon Cognito** | Handles user registration and authentication. |
| **Amazon DynamoDB** | Stores application data such as product and cart information. |
| **Amazon RDS** | Provides a managed relational database. |
| **AWS Systems Manager Parameter Store** | Stores application configuration values and parameters. |
| **Amazon ECR** | Stores Docker container images. |
| **Amazon ECS with AWS Fargate** | Runs containerized backend services. |
| **Application Load Balancer (ALB)** | Routes requests to the appropriate backend services. |
| **Amazon API Gateway** | Provides API endpoints and connects requests to backend services. |
| **Amazon SNS** | Publishes notification messages. |
| **Amazon SQS** | Stores messages for asynchronous processing. |
| **AWS Certificate Manager (ACM)** | Provides SSL/TLS certificates for HTTPS. |
| **Amazon Route 53** | Manages DNS records for the custom domain. |
| **Amazon CloudWatch** | Supports application logging and monitoring. |
| **AWS Identity and Access Management (IAM)** | Controls access to AWS resources. |

Each service has a specific responsibility in the application architecture.

---

## 4\. Project Architecture and Request Flow

The application follows a **multi-layer architecture**, where each component performs a specific task.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/804b2879-dc77-4b25-b9dc-03dedee0babb.png align="center")

### Architecture Layers

- **Frontend layer:** React.js, Amazon S3, and Amazon CloudFront.
- **Authentication layer:** Amazon Cognito.
- **API layer:** Amazon API Gateway.
- **Backend layer:** Containerized services running on Amazon ECS with AWS Fargate.
- **Database layer:** Amazon DynamoDB and Amazon RDS.
- **Notification layer:** Amazon SNS and Amazon SQS.
- **Networking and security layer:** Amazon VPC, subnets, route tables, security groups, and IAM.

### Application Request Flow

The application works through the following flow:

1. Users access the website through **Amazon CloudFront** or the configured **custom domain**.
2. CloudFront delivers the frontend files stored in **Amazon S3**.
3. Users register or log in through **Amazon Cognito**.
4. The frontend sends requests to **Amazon API Gateway**.
5. API Gateway forwards requests through the configured integration to the appropriate backend services.
6. Backend services process requests and communicate with **Amazon DynamoDB, Amazon RDS**, and other required resources.
7. Order-related workflows can use **Amazon SNS and Amazon SQS** to support notifications and asynchronous processing.
8. The application returns the response to the frontend for the user to view.

**Networking and security:** Amazon VPC, subnets, route tables, security groups, IAM permissions, authentication, and HTTPS help control access and communication between application components.

This overview explains how the main components work together. The individual implementation modules will cover the configuration details.

---

## 5\. Project Implementation Roadmap

We will implement the project step by step, creating and configuring the required AWS resources in the following order.

1. **Module 0: Prerequisites and Environment Setup** — Prepare the workstation, install the required tools, and configure AWS CLI access.
2. **Module 1: Networking Foundation** — Configure the VPC, subnets, gateways, and route tables.
3. **Module 2: User Authentication** — Configure Amazon Cognito for user registration and login.
4. **Module 3: Frontend Deployment** — Build the React application and deploy it using Amazon S3 and CloudFront.
5. **Module 4: Database Configuration** — Configure the required databases and application parameters.
6. **Module 5: Backend Deployment** — Build Docker images, push them to Amazon ECR, and deploy backend services using Amazon ECS and AWS Fargate.
7. **Module 6: API Gateway Configuration** — Configure API routes and backend integration.
8. **Module 7: Frontend and Backend Integration** — Connect the frontend to backend APIs and verify communication.
9. **Module 8: Notifications** — Configure Amazon SNS, Amazon SQS, and the required subscriptions.
10. **Module 9: Custom Domain and HTTPS** — Configure DNS, the custom domain, and the SSL/TLS certificate.
11. **Module 10: Testing and Cleanup** — Test the application, troubleshoot issues, and remove AWS resources that are no longer required.

Each module will document the relevant **implementation steps, AWS console settings, commands, verification procedures, and troubleshooting observations**.

# Module 0: Prerequisites and Environment Setup

## Overview

Before deploying the e-commerce application on AWS, we need to prepare the development environment and configure the required tools.

In this module, we will use an **Amazon EC2 instance** as our working environment. We will connect to the instance, clone the project repository, install the required tools, and configure AWS CLI access.

By the end of this module, the environment will be ready for the next stage of the project: **Module 1 — Networking Foundation**.

## Prerequisites

Before starting, ensure you have the following:

### 1\. AWS Account

- An active **AWS account** with the required permissions to create and manage AWS resources.
- Access to the **Amazon EC2** service to launch and connect to an instance.
- AWS credentials with the necessary permissions for AWS CLI operations.

### 2\. Amazon EC2 Instance

We will use an **EC2 virtual machine** as the development environment for this project.

The initial setup includes:

- Launching an EC2 instance.
- Selecting an appropriate operating system and instance type.
- Configuring the required network access.
- Connecting to the instance using the appropriate connection method.

### 3\. Required Tools

The following tools are required for the project:

| Tool | Purpose |
| --- | --- |
| **Git** | Cloning the repository and managing source code. |
| **Docker** | Building and managing container images. |
| **Node.js 20+** | Running and building the React frontend. |
| **npm** | Managing JavaScript dependencies. |
| **AWS CLI v2** | Interacting with AWS services through the terminal. |
| **Bash** | Running the prerequisite installation script. |

## Step 1: Launch and Connect to the EC2 Instance

- Launch an **Amazon EC2 instance** with the required configuration.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/416ad05f-2346-44ba-992f-892864b355f4.png align="center")

- Once the instance is running, connect to it and open the terminal to start the setup process.

## Step 2: Clone the Project Repository

- Clone the e-commerce application repository to the EC2 instance.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/6445d2cf-602d-4d50-b8e6-f35a03b2f2d5.png align="center")

- Run the following commands:

```bash
git clone https://github.com/KandlaguntaVenkataSivaNiranjanReddy/ecommerce-web-app.git
```

- The `git clone` command downloads the project source code, while `cd ecommerce-web-app` moves into the project directory.

```plaintext
cd ecommerce-web-app
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/31d0f78f-4f00-4a80-936b-615059bbb05c.png align="center")

## Step 3: Install the Required Tools

- The project repository includes an installation script for the required prerequisites.
- Run the script from the project directory:

```bash
bash install-prerequisites.sh
```

- The script is intended to install the required tools based on the supported operating system.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/539b2c57-caea-4c75-a6de-3e2172bfa7cd.png align="center")

## Step 4: Verify Tool Installation

- After installation, verify that the required tools are available.

Run the following commands:

```bash
aws --version
docker --version
node --version
npm --version
git --version
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/04d2f41f-580d-4246-bc2f-935bb7021343.png align="center")

- These commands display the installed versions of AWS CLI, Docker, Node.js, npm, and Git.
- Confirm that each command runs successfully before proceeding.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d0f4426a-335e-4bd8-9b8e-c034cafba04f.png align="center")

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f5fd7ce3-a918-4321-82a1-c18e52615fe4.png align="center")

## Step 5: Configure AWS CLI Access

- AWS CLI allows us to interact with AWS services directly from the terminal.
- If AWS CLI is not already configured, run:

```bash
aws configure
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/711da2bf-2198-4518-ac42-15202db32c0f.png align="center")

- Enter the required configuration values when prompted:

```text
AWS Access Key ID [None]: <your-access-key-id>
AWS Secret Access Key [None]: <your-secret-access-key>
Default region name [None]: ap-south-1
Default output format [None]: json
```

- Use the AWS region selected for your project. In this example, `ap-south-1` represents the Mumbai region.
- **Security note:** Never share your AWS access keys or commit them to the project repository. Use credentials with only the permissions required for your tasks. For EC2 workloads, an appropriately permissioned IAM role attached to the instance is generally preferable to storing long-term access keys.

## Conclusion

In this module, we prepared the development environment using an **Amazon EC2 instance**. We connected to the instance, cloned the e-commerce project repository, installed and verified the required tools, and configured AWS CLI access.

With the prerequisites in place, we can proceed to the next stage of the project.

---

# Module 1: Networking Foundation — Build AWS VPC Infrastructure

## 1\. Overview

In this module, we will create the **AWS network** required for our **e-commerce application**.

We will create a **VPC, six subnets across two Availability Zones, an Internet Gateway, a NAT Gateway, and route tables**.

We will use the **AWS Mumbai region (**`ap-south-1`**)**.

## 2\. Understanding the Network Architecture

Before creating the resources, let's understand the main components.

- **VPC:** Our application's private network in AWS.
- **Public Subnets:** Used to place resources that require public network connectivity, such as the **NAT Gateway**.
- **Private ECS Subnets:** Used to run our **backend services**.
- **Private Database Subnets:** Used for our **Amazon RDS database**.
- **Internet Gateway:** Provides **internet connectivity** to resources in public subnets when properly configured.
- **NAT Gateway:** Allows resources in private subnets to make **outbound internet connections**.
- **Route Tables:** Control how **network traffic** moves between different networks.

We will keep our **backend services and database in private subnets** to limit direct exposure to the internet.

Our application will also use an **internal Application Load Balancer (ALB)**, **Amazon ECS with AWS Fargate**, **Amazon RDS**, and **Amazon DynamoDB**. DynamoDB is an **AWS-managed service** that operates outside our VPC.

## 3\. Network Architecture

We will create the following network infrastructure in the **Asia Pacific (Mumbai) region** `ap-south-1`.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/615d6d66-b173-45f5-9eea-8ad44dd81f4e.png align="center")

### VPC Configuration

| Property | Value |
| --- | --- |
| VPC Name | `ecommerce-vpc` |
| IPv4 CIDR Block | `10.10.0.0/16` |
| AWS Region | `ap-south-1` |
| Availability Zones | `ap-south-1a`, `ap-south-1b` |
| Total Subnets | 6 |
| Route Tables | 3 custom route tables |

### Subnet Configuration

| Subnet Type | Subnet Name | CIDR Block | Availability Zone |
| --- | --- | --- | --- |
| Public | `ecommerce-public-subnet-1` | `10.10.0.0/24` | `ap-south-1a` |
| Public | `ecommerce-public-subnet-2` | `10.10.1.0/24` | `ap-south-1b` |
| Private ECS | `ecommerce-private-ecs-1` | `10.10.10.0/24` | `ap-south-1a` |
| Private ECS | `ecommerce-private-ecs-2` | `10.10.11.0/24` | `ap-south-1b` |
| Private Database | `ecommerce-private-database-1` | `10.10.20.0/24` | `ap-south-1a` |
| Private Database | `ecommerce-private-database-2` | `10.10.21.0/24` | `ap-south-1b` |

We use separate CIDR blocks for each subnet so that network resources can be organized without overlapping IP address ranges.

### Route Table Design

We will create three custom route tables.

| Route Table | Route | Associated Subnets |
| --- | --- | --- |
| `ecommerce-public-rt` | `0.0.0.0/0` → Internet Gateway | Both public subnets |
| `ecommerce-private-ecs-rt` | `0.0.0.0/0` → NAT Gateway | Both private ECS subnets |
| `ecommerce-private-db-rt` | No additional default internet route | Both private database subnets |

- Each subnet must be associated with the appropriate route table to follow the intended network design.

## 4\. Hands-on Lab: Create the AWS Networking Infrastructure

- We will now configure the network resources through the AWS Management Console.

### Step 1: Select the AWS Region

1. Sign in to the **AWS Management Console**.
2. Open the region selector.
3. Select **Asia Pacific (Mumbai)** `ap-south-1`.

Use the same AWS region for this project unless a specific service requires otherwise.

### Step 2: Create the VPC

- Navigate to:**AWS Console → VPC → Your VPCs → Create VPC**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/c7d0892a-e938-4566-a3d7-aca5bf83ed8f.png align="center")

- Select **VPC only** and provide the following configuration:

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/6f7eec83-68f4-4a80-aa20-dc9bb4bd302b.png align="center")

| Setting | Value |
| --- | --- |
| Name tag | `ecommerce-vpc` |
| IPv4 CIDR block | `10.10.0.0/16` |
| IPv6 CIDR block | No IPv6 CIDR block |
| Tenancy | Default |

- Click **Create VPC**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/55c78695-c5fb-4f69-84e1-f0a916b3933f.png align="center")

- The VPC provides the isolated network in which we will create our subnets and configure routing.

---

### Step 3: Create and Attach the Internet Gateway

- Navigate to: **VPC → Internet Gateways → Create internet gateway**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/32a88598-f405-461e-986f-9422bedbf603.png align="center")

- Enter the following name: `ecommerce-igw`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8c04243d-7292-48e8-b292-bffca70a8cf7.png align="center")

- Click **Create internet gateway**.

After creating it:

1. Select the Internet Gateway.
2. Choose **Actions → Attach to a VPC**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/90daade5-a5b8-47bf-9079-07ca9c495300.png align="center")

1. Select `ecommerce-vpc`.
2. Click **Attach internet gateway**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a2fb013f-0293-48fa-a42d-e63a3ce41540.png align="center")

- The Internet Gateway is now attached to our VPC. `We will configure the public route to use it in a later step`.
- **Make sure you select the correct VPC.**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/513cf74c-2f1d-4575-b1c0-f9e9bfd28fe8.png align="center")

---

### Step 4: Create the Six Subnets

The subnets are organized into three groups:

- **Two public subnets** for public network placement.
- **Two private ECS subnets** for backend application services.
- **Two private database subnets** for database resources.

### Subnet Configuration

| Subnet Type | Subnet Name | CIDR Block | Availability Zone |
| --- | --- | --- | --- |
| Public | `ecommerce-public-subnet-1` | `10.10.0.0/24` | `ap-south-1a` |
| Public | `ecommerce-public-subnet-2` | `10.10.1.0/24` | `ap-south-1b` |
| Private ECS | `ecommerce-private-ecs-1` | `10.10.10.0/24` | `ap-south-1a` |
| Private ECS | `ecommerce-private-ecs-2` | `10.10.11.0/24` | `ap-south-1b` |
| Private Database | `ecommerce-private-database-1` | `10.10.20.0/24` | `ap-south-1a` |
| Private Database | `ecommerce-private-database-2` | `10.10.21.0/24` | `ap-south-1b` |

- Navigate to:**VPC → Subnets → Create subnet**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f3699c9e-7fc2-430c-aaab-18d2c921d597.png align="center")

- Select `ecommerce-vpc` as the VPC.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/acc1d95b-ca33-4de1-a65a-c6958ab9b6ca.png align="center")

#### 1\. Create `ecommerce-public-subnet-1`

- **Subnet Name:** `ecommerce-public-subnet-1`
- **Availability Zone:** `ap-south-1a`
- **IPv4 CIDR Block:** `10.10.0.0/24`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/66be57ce-6018-4785-8161-f095451485af.png align="center")

- Enter the corresponding IPv4 CIDR block `10.10.0.0/24`
- Repeat the process until all subnets have been created.

#### 2\. Create `ecommerce-public-subnet-2`

- **Subnet Name:** `ecommerce-public-subnet-2`
- **Availability Zone:** `ap-south-1b`
- **IPv4 CIDR Block:** `10.10.1.0/24`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ceb9451e-7502-4f90-b7d7-bc2f53a56a80.png align="center")

Both public subnet configurations are now covered. Next, we configure the private ECS subnets.

#### 3\. Create `ecommerce-private-ecs-1`

- **Subnet Name:** `ecommerce-private-ecs-1`
- **Availability Zone:** `ap-south-1a`
- **IPv4 CIDR Block:** `10.10.10.0/24`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/afeb9711-2fdf-47a8-82b4-0abb6176e49f.png align="center")

#### 4\. Create `ecommerce-private-ecs-2`

- **Subnet Name:** `ecommerce-private-ecs-2`
- **Availability Zone:** `ap-south-1b`
- **IPv4 CIDR Block:** `10.10.11.0/24`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/32035d47-3719-4aab-bddf-f7a14528a03d.png align="center")

Both private ECS subnet configurations are covered. Next, we configure the private database subnets.

#### 5\. Create `ecommerce-private-database-1`

- **Subnet Name:** `ecommerce-private-database-1`
- **Availability Zone:** `ap-south-1a`
- **IPv4 CIDR Block:** `10.10.20.0/24`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/599a6f08-d061-433e-b473-16bd693a5ca1.png align="center")

#### 6\. Create `ecommerce-private-database-2`

- **Subnet Name:** `ecommerce-private-database-2`
- **Availability Zone:** `ap-south-1b`
- **IPv4 CIDR Block:** `10.10.21.0/24`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/5cc17107-681e-4fc0-974e-f3a3a673adb9.png align="center")

#### Verify the Subnets

- After creation, navigate to **VPC → Subnets** and verify that all six subnets have the correct names, CIDR blocks, and Availability Zones.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/fa9e6bab-d84a-46bd-bead-ed105dec70fb.png align="center")

---

### Step 5: Create the Public Route Table

- Navigate to: **VPC → Route Tables → Create route table**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/9a74064c-9a3a-48e3-ba53-39c84be094ec.png align="center")

- Enter the following details:

| Setting | Value |
| --- | --- |
| Name | `ecommerce-public-rt` |
| VPC | `ecommerce-vpc` |

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/116a6482-6c27-4e4b-b855-907f124ac993.png align="center")

- Click **Create route table**.

#### Add the Internet Gateway Route

1. Select `ecommerce-public-rt`.
2. Open the **Routes** tab.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8fead1fb-07e0-4ee5-811f-f7a004cb71bf.png align="center")

1. Click **Edit routes → Add route**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/97057ace-e0b9-4b75-9548-7221df3e3f65.png align="center")

1. Enter the destination `0.0.0.0/0`.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/0784a19a-79e7-4508-abab-5ccaa3472e30.png align="center")

1. Select `ecommerce-igw` as the target.
2. Save the changes.

This route sends traffic destined for other IPv4 networks through the Internet Gateway.

#### Associate the Public Subnets

1. Open the **Subnet associations** tab.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/bf9ea0fd-37f4-4028-bea0-f42685cd71aa.png align="center")

1. Click **Edit subnet associations**.
2. Select:
   - `ecommerce-public-subnet-1`
   - `ecommerce-public-subnet-2`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/df67514e-732a-4b6c-9372-475e0c399f16.png align="center")

1. Save the associations.

- The two public subnets now use the public route table.

### Step 6: Create the Private ECS Route Table

- Navigate to: **VPC → Route Tables → Create route table**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/51e5d18c-0672-4c85-a558-8ef48f08e5dd.png align="center")

Enter:

| Setting | Value |
| --- | --- |
| Name | `ecommerce-private-ecs-rt` |
| VPC | `ecommerce-vpc` |

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/40e0b33c-49ba-46e2-84c1-0709db4f043a.png align="center")

- Click **Create route table**.

#### Associate the ECS Subnets

1. Select `ecommerce-private-ecs-rt`.
2. Open **Subnet associations**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/c19949a3-265e-49ab-ac22-ef6c4ea53c2c.png align="center")

1. Click **Edit subnet associations**.
2. Select:
   - `ecommerce-private-ecs-1`
   - `ecommerce-private-ecs-2`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b944c842-8a5c-4524-aee4-31c1ced75a1a.png align="center")

1. Save the associations.

`We will add the NAT Gateway route after creating the NAT Gateway.`

### Step 7: Create the Private Database Route Table

- Navigate to: **VPC → Route Tables → Create route table**

Enter:

| Setting | Value |
| --- | --- |
| Name | `ecommerce-private-db-rt` |
| VPC | `ecommerce-vpc` |

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b8f61ff9-c055-4ccc-bef5-2744eebd68da.png align="center")

- Click **Create route table**.

#### Associate the Database Subnets

1. Select `ecommerce-private-db-rt`.
2. Open **Subnet associations**.
3. Click **Edit subnet associations**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/16cd9a4e-4579-4590-b6be-6e1ecce79f95.png align="center")

1. Select:
   - `ecommerce-private-database-1`
   - `ecommerce-private-database-2`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/eff8f787-eec3-476e-8ba3-10853212d9dc.png align="center")

1. Save the associations.

- We will not add a default internet route to this route table. The database subnets can still communicate within the VPC according to the applicable routes and security rules.

### Step 8: Create the NAT Gateway

- The NAT Gateway allows resources in our private ECS subnets to initiate outbound connections to the internet without requiring direct public IP addresses.
- Navigate to: **VPC → NAT Gateways → Create NAT gateway**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/fffeaaf3-f7c5-42ec-8d44-ba2809c45d2e.png align="center")

- Enter the following configuration:

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e1bb3f6f-4d2c-413a-b56e-1fd5022485a9.png align="center")

| Setting | Value |
| --- | --- |
| Name | `ecommerce-nat-gateway` |
| Availability mode | Zonal |
| Subnet | `ecommerce-public-subnet-1` |
| Connectivity type | Public |
| Elastic IP | Allocate Elastic IP |

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e4eb8288-fc10-471c-bba2-465724c80fa2.png align="center")

- Click **Create NAT gateway**.
- Wait until the NAT Gateway status changes to Available before adding it to the route table.

**Note:** AWS charges you for using a NAT Gateway. We are using only one NAT Gateway in one Availability Zone, so if that zone has a problem, network connectivity may be affected. Using NAT Gateways in multiple Availability Zones can improve availability.

### Step 9: Add the NAT Gateway Route to the ECS Route Table

- After the NAT Gateway becomes available, navigate to:

**VPC → Route Tables →** `ecommerce-private-ecs-rt`

Then:

1. Open the **Routes** tab.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ba2c3df8-ecb9-4059-9056-ed23cc44b115.png align="center")

1. Click **Edit routes**.
2. Click **Add route**.
3. Enter the destination `0.0.0.0/0`.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/bd8c0316-6553-4575-a0b6-07135ba72487.png align="center")

1. Select the NAT Gateway `ecommerce-nat-gateway` as the target.
2. Save the changes.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ae794158-41e7-49d7-a700-8ab86eee1ddf.png align="center")

- The private ECS subnets can now use the NAT Gateway for outbound IPv4 internet traffic, subject to the applicable network and security configuration.

## 5\. Verify the Networking Configuration

After completing the configuration, verify the resources in the VPC console.

### VPC Verification

- Confirm that `ecommerce-vpc` exists with CIDR `10.10.0.0/16`.
- Confirm that `ecommerce-igw` is attached to the VPC.
- Confirm that all six subnets exist in their intended Availability Zones.
- Confirm that the NAT Gateway status is **Available**.

### Route Table Verification

Check that the route tables have the expected routes and subnet associations.

| Route Table | Expected Configuration |
| --- | --- |
| `ecommerce-public-rt` | `0.0.0.0/0` → Internet Gateway; both public subnets associated |
| `ecommerce-private-ecs-rt` | `0.0.0.0/0` → NAT Gateway; both ECS subnets associated |
| `ecommerce-private-db-rt` | No additional default internet route; both database subnets associated |

- If a subnet is associated with the wrong route table, edit the subnet associations and correct the configuration.
- Remember that a subnet's route table alone does not determine whether a resource is publicly reachable. Public IP assignment, security groups, network ACLs, and the resource's own configuration also affect connectivity.

## 6\. Conclusion

In this module, we created the networking foundation for our e-commerce application using **Amazon VPC**.

We configured:

- One VPC with CIDR `10.10.0.0/16`.
- Six public and private subnets across two Availability Zones.
- An Internet Gateway attached to the VPC.
- A public NAT Gateway for outbound connectivity.
- Three custom route tables with the appropriate subnet associations and routes.

This network design separates the application and database subnets and provides the routing foundation required for the next deployment stages.

Proceed to **User Authentication**, where we will configure Amazon Cognito for user registration and login.

---

# Module 2: Authentication with Amazon Cognito

## 1\. Overview

In this module, we will configure **Amazon Cognito** to manage **user registration and login** for our e-commerce application.

We will create a **Cognito User Pool**, configure the **App Client**, and enable the required **authentication methods**.

This allows users to sign up and log in before accessing features such as their **cart, orders, and profile**.

## 2\. Understanding Amazon Cognito

**Amazon Cognito** is an AWS service that manages **user identities and authentication**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d34c3b91-cb63-4cfe-9fc6-5f1685a07698.png align="center")

For our e-commerce application:

- **User Pool:** Stores user information and manages registration and login.
- **App Client:** Allows our frontend application to communicate with the User Pool.
- **Authentication Flows:** Define how users authenticate with their credentials.
- **Cognito Domain:** Provides a domain for Cognito's authentication endpoints.

Our **product listing** can be publicly accessible, while features such as managing the **cart, placing orders, and viewing user profiles** can require authentication.

We will use **Amazon Cognito with API Gateway** to help protect the application's APIs in later modules.

## 3\. Create a Cognito User Pool

### Step 1: Open Amazon Cognito

1. Sign in to the **AWS Management Console**.
2. Search for **Amazon Cognito**.
3. Open **Amazon Cognito → User pools**.
4. Make sure you are in the **Mumbai region (**`ap-south-1`**)**.
5. Click **Create user pool**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d9af5fc1-fd40-463b-ba3b-a190a9789e12.png align="right")

### Step 2: Define the Application

Under **Define your application**, select:

- **Application type:** Single-page application (SPA)

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e55ea5ba-38c8-4d01-bb17-feb95b9193d5.png align="center")

- Enter the application name: `ecommerce-app`

### Step 3: Configure Sign-in Options

Configure the following settings:

- **Sign-in identifier:** Email

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/27334a05-0e93-4aaa-ae0d-bfbc47cd7b35.png align="center")

- **Self-registration:** Enable
- **Required sign-up attributes:** `email` and `name`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/98139384-64bf-4477-a48a-35b3ac1ec028.png align="center")

These settings allow users to register using their **email addresses** and provide their **names** during sign-up.

### Step 4: Add a Return URL

- Enter your application's URL as the **Return URL**.
- For example: `https://yourdomain.com`
- Leave the **Return URL** field blank for now.

### Step 5: Create the User Pool

- Review the configuration and click **Create user directory**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/3c98304f-59a0-4ae1-adc7-b4e8ed0dbf66.png align="center")

- Amazon Cognito will create the **User Pool** and, through the application setup wizard, configure an **App Client** for the application.

### Step 6: Save the User Pool ID

After creating the User Pool, open its **Overview** page.

1. Find the **User Pool ID** under User pool information.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/30739a09-9a9a-492c-8c77-8af51295a399.png align="center")

1. Click the copy icon next to the ID.
2. Save it in a notepad for later use.

- We will need this **User Pool ID** when configuring Amazon Cognito in our frontend application.

## 4\. Configure the App Client

- After creating the User Pool, open it and navigate to **App integration → App clients**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/332dee42-1adb-4315-9d19-6b6b3240649e.png align="center")

- Select the **App Client** created for `ecommerce-app`.

### Step 1: Edit Authentication Flows

- Open the App Client settings and click **Edit**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/77534eb6-0388-4661-9d47-f2b56eb61b49.png align="center")

Under **Authentication flows**, enable Only these three options:

- `ALLOW_USER_PASSWORD_AUTH`
- `ALLOW_USER_SRP_AUTH`
- `ALLOW_REFRESH_TOKEN_AUTH`
- **Disable any other authentication flows that are enabled.**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/4884e653-6728-4903-9df8-dfe13cd2872d.png align="center")

- These options support **password-based authentication, SRP authentication, and refresh-token authentication**.
- Click **Save changes**.

## 5\. Save the Cognito Details

We need **three values** for the frontend configuration in the next module.

Copy and save the following values in a **safe place**.

### 1\. User Pool ID

- Navigate to your User Pool and copy the **User Pool ID**.
- Example: `ap-south-1_xxxxxxxxx`

### 2\. App Client ID

- Open **App integration → App clients** and copy the **Client ID**.
- Example: `1a2b3c4d5e6f7g8h9i0j1k2l3m`

### 3\. Cognito Domain

- Navigate to the User Pool's **Branding → Domain** section and copy the configured **Cognito domain**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/cbe9c6c3-cd55-4b16-b623-2be542e898a1.png align="center")

- Keep these **three values** available for the next module.

## 6\. Verify the Configuration

Before moving to the next module, verify the following:

- The `ecommerce-app` **User Pool** has been created.
- **Email** is enabled as the sign-in identifier.
- **Self-registration** is enabled.
- The required attributes are `email` and `name`.
- The required **authentication flows** are enabled for the App Client.
- The **User Pool ID, App Client ID, and Cognito domain** have been saved.

## 7\. Conclusion

- In this module, we configured **Amazon Cognito** for our e-commerce application.
- We created the **User Pool**, configured the **App Client authentication flows**, and saved the details required for **frontend integration**.
- Now proceed to **Frontend Deployment**, where we will deploy the frontend application and configure it to use **Amazon Cognito for user authentication**.

---

# Module 3: Frontend Deployment

## 1\. Overview

- In this module, we will deploy the **React frontend application** using **Amazon S3 and Amazon CloudFront**.
- We will configure the frontend with the **Amazon Cognito User Pool ID and App Client ID** from Module 2.
- By the end of this module, we will be able to access our application through a CloudFront URL and test **user registration, login, and logout**.

**Note:** Product listing, cart, and order features will work after completing **Module 7: Frontend-Backend Integration**, when the backend services and APIs are connected.

## 2\. Understanding the Architecture

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/fd530870-c178-45b9-8735-c9dc52032d1a.png align="center")

Our frontend deployment uses two main AWS services:

- **Amazon S3:** Stores the React application's static files, including HTML, CSS, JavaScript, and other assets.
- **Amazon CloudFront:** Delivers the frontend content to users through its global network and caches content to improve performance.
- We will keep the **S3 bucket private** and allow CloudFront to access its contents using **Origin Access Control (OAC)**.
- This setup prevents users from accessing the S3 content directly and allows them to access the application through CloudFront.
- We will also configure **Amazon Cognito** in the frontend so users can sign up and log in.

## 3\. Create an S3 Bucket for the Frontend

### Step 1: Open Amazon S3

1. Sign in to the **AWS Management Console**.
2. Search for **Amazon S3**.
3. Navigate to **S3 → Buckets → Create bucket**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/cf103561-ec46-4a05-b87e-a6dfd4ed4602.png align="center")

1. Select **General purpose** bucket.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/3b7e0db0-58c2-4a82-aaf0-34123d90f813.png align="center")

### Step 2: Configure the S3 Bucket

Enter the following configuration:

- **Bucket name:** `ecommerce-frontend-<your-name>`
- **AWS Region:** Asia Pacific (Mumbai) — `ap-south-1`
- **Block all public access:** Keep enabled.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/7d49a724-057b-467f-8f15-ebd389443667.png align="center")

- **Bucket Versioning:** Disable.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/aee8da08-9b93-4ea2-9426-7a28f1896bf7.png align="center")

- **Default Encryption:** Keep enabled with the default SSE-S3 encryption.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/eb97172e-d91f-4a47-98e2-834e4edb4d9f.png align="center")

- Replace `<your-name>` with your preferred name or text to create a unique bucket name.
- Click **Create bucket**.

**Note:** Keep the bucket private. CloudFront will access the bucket using the configured permissions.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ea108655-5101-4dbb-9a07-9f5abf039b9e.png align="center")

## 4\. Create a CloudFront Distribution

### Step 1: Open CloudFront

1. Search for **Amazon CloudFront** in the AWS Management Console.
2. Navigate to **CloudFront → Distributions → Create distribution**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/7f242f3f-c85f-49d6-9dd7-1948acf25c0a.png align="center")

1. Amazon CloudFront is a global service, so you do not need to select an AWS Region.

### Step 2: Configure the Distribution

Enter the following details:

- **Distribution name:** `ecommerce-distribution`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/842c8c32-d5f0-4e58-ac6c-c72c13ff0962.png align="center")

- **Distribution type:** Single website or app

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/7f5a620a-6f8c-4ddc-b299-0be15a5b1575.png align="center")

- **Origin type:** Amazon S3

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/48bb98b3-787f-430b-99a9-06ec37ef04a6.png align="center")

- **Origin:** Select your frontend S3 bucket.
- Under the origin settings, select **Allow private S3 bucket access to CloudFront — Recommended**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e2400d0d-85f6-4b4c-9e2a-d685023b406d.png align="center")

- This setting configures **Origin Access Control (OAC)** and allows CloudFront to access the private S3 bucket.
- Keep the recommended cache settings.

### Step 3: Configure Security Settings

- Under the security settings:
- Select **Do not enable security protections** for this development and testing setup.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d5a2bf24-68cd-400d-8b4f-39bf025dbf5f.png align="center")

### Step 4: Review and Create the Distribution

1. Review all the CloudFront distribution settings.
2. Verify that the S3 bucket is selected as the origin.
3. Ensure that **Allow private S3 bucket access to CloudFront** is enabled.
4. Confirm the recommended cache settings.
5. Click **Create distribution**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8fda62cd-966e-4e1c-87f2-b29523d463d8.png align="center")

- Click **Create distribution**.

### Step 5: Wait for Deployment

1. Wait until the CloudFront distribution status changes to **Deployed**.
2. Open the distribution details.
3. Copy and save the **Distribution ID** and **Distribution domain name** for future use.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/32fbb0cf-58d2-4152-9334-38a5027eb021.png align="center")

**Note:** CloudFront deployment may take several minutes. Wait until the distribution status changes to **Deployed** before proceeding to the next step.

## 5\. Configure the CloudFront Root Document and Custom Error Pages

### Step 1: Set the Default Root Object

- Navigate to: **CloudFront → Distributions → Your distribution → General → Edit**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/96e0fe0c-864b-4940-ace8-47af97a2c7f2.png align="center")

- Set the **Default root object** to: `index.html`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/93576b0c-5773-4057-9ceb-62ff35a41702.png align="center")

- Click **Save changes**.
- This tells CloudFront to load `index.html` when users open the main application URL.

### Step 2: Configure the 403 Error Response

- Navigate to: **CloudFront → Your distribution → Error pages → Create custom error response**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b8418b68-582a-41e3-96b3-d9cc76038e1c.png align="center")

Configure the following:

- **HTTP error code:** `403`
- **Customize error response:** Yes
- **Response page path:** `/index.html`
- **HTTP response code:** `200`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2c3a7595-57b9-4134-aa23-92d2160bd80b.png align="center")

- Click **Create**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/00181b9d-9bc1-4681-8c29-5dd709e968ba.png align="center")

### Step 3: Configure the 404 Error Response

Repeat the process with the following settings:

- **HTTP error code:** `404`
- **Customize error response:** Yes
- **Response page path:** `/index.html`
- **HTTP response code:** `200`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/dea72d34-53a6-4858-bf05-160f4659b662.png align="center")

- Save the configuration.
- These settings allow React to handle application routes on the client side.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/4e3ec76f-cd03-41d0-935a-ab79f3e0eee4.png align="center")

### Step 4: Save the CloudFront Details

Open your CloudFront distribution and copy the following values:

- **Distribution ID:** For example, `E1234567890ABC`
- **Distribution Domain Name:** For example, `dq68byzoh2345u.cloudfront.net`

Save both values in a notepad. We will use the CloudFront domain to access our application.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/12a37362-96f5-4f41-8018-fe78baac7144.png align="center")

## 6\. Verify the S3 Bucket Policy

- After creating the CloudFront distribution, check whether the S3 bucket policy allows CloudFront to access the bucket.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8a7be28d-709c-4fa2-a36b-ca383db9639d.png align="center")

- Navigate to: **Amazon S3 → Your frontend bucket → Permissions → Bucket policy**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/413243d9-501f-4520-9545-21822642b58e.png align="center")

- Verify that the policy allows the **CloudFront service** to read objects from your bucket and restricts access to your specific CloudFront distribution.

The policy should include:

- **Principal:** `cloudfront.amazonaws.com`
- **Action:** `s3:GetObject`
- **Resource:** Your frontend bucket's objects.
- **Condition:** The ARN of your CloudFront distribution.
- CloudFront may configure this policy automatically when you enable private S3 access during distribution creation.
- If the policy has not been updated, check the configuration and add the appropriate policy using your **AWS account ID, S3 bucket name, and CloudFront distribution ID**.

## 7\. Configure the React Application

We will now configure the frontend to use the Cognito details created in **Module 2**.

### Step 1: Open the Frontend Directory

Open your terminal in the project working environment and navigate to the React application directory:

```bash
cd frontend/react-app
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2d3b90f2-ccd3-40d8-9241-758050fe1ad0.png align="center")

### Step 2: Update the AWS Configuration

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/57904a82-0416-44ee-a7b4-4d2e33c43965.png align="center")

- Open the following file: `src/aws-config.js`

```plaintext
nano aws-config.js
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/793ab440-8603-41b1-bf08-cc7c320e7e11.png align="center")

- Update the configuration with your actual **Cognito User Pool ID** and **App Client ID**.

```javascript
const awsConfig = {
  Auth: {
    Cognito: {
      userPoolId: '<COGNITO_USER_POOL_ID>',
      userPoolClientId: '<COGNITO_CLIENT_ID>',
      loginWith: {
        email: true,
      },
    }
  },
  API: {
    baseUrl: ''
  }
};

export default awsConfig;
```

1. Replace the placeholder values with the details saved in Module 2.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/730d0549-d6ae-43bf-9179-70b16455525d.png align="center")

1. Keep `baseUrl` empty for now. We will configure it when connecting the frontend to the backend APIs in **Module 7**.
2. Save the file.

## 8\. Build and Deploy the React Frontend

### Step 1: Install Dependencies

- Run the following command inside the React application directory:

```bash
npm install
```

- This command installs the packages required by the frontend application.
- Wait for the installation to finish.

### Step 2: Build the Application

Run:

```bash
npm run build
```

- This command creates the production build files in the `build` directory.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/86e0a5f4-ef91-4ef9-9393-ed5ae5bb658d.png align="center")

### Step 3: Upload the Build to S3

- Run the following command, replacing `<your-frontend-bucket-name>` with your actual S3 bucket name:

```bash
aws s3 sync build/ s3://<your-frontend-bucket-name> --delete --exclude "images/*"
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/4f3ffbd3-3d64-49e5-b284-92de2f6a6c3a.png align="center")

- This command uploads the frontend build files to S3.
- The `--delete` option removes destination files that are no longer present in the local source, while `--exclude "images/*"` excludes the images path from the sync operation.

## 9\. Open CloudFront and Test the Frontend Application

### Step 1: Verify CloudFront Origins

- The **Origins** tab shows the S3 bucket configured as the source for serving the frontend application files.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/4ac0afea-fec9-49cb-aa6e-499fdef6a4aa.png align="center")

### Step 2: Verify CloudFront Behaviors

- The **Behaviors** tab shows the default path pattern (`*`), which forwards incoming requests to the configured S3 origin. It also shows the viewer protocol policy set to **Redirect HTTP to HTTPS**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/590fd183-3949-4494-99e4-ff14570e269c.png align="center")

### Step 3: Open the Application

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/0ce0835d-9445-4429-b232-646442961ed4.png align="center")

- Open your CloudFront domain in a browser: `https://<your-cloudfront-domain>`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d4613ba7-3322-4d20-a70b-1b75a0fd460c.png align="center")

- Verify that the frontend application loads successfully.

### Step 2: Test User Registration

1. Open the sign-up page.
2. Enter your email address, name, and password.
3. Submit the registration form.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/21cc1796-5086-4cfb-8ac3-7de0afa0f5be.png align="center")

1. Check your email for the verification code.
2. Enter the code to verify your account.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/5f449a3c-2eee-4202-80f0-0a5fe2eed821.png align="center")

### Step 3: Test Login and Logout

1. Open the sign-in page.
2. Enter your registered email address and password.
3. Verify that you can log in successfully.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/543b9fca-1ab1-4b2b-b112-bd46733adeae.png align="center")

1. Log out and confirm that you can sign in again.

### Expected Results

**Working in this module:**

- Frontend application loads through CloudFront.
- Users can sign up.
- Email verification works.
- Users can log in and log out.

**Not yet working:**

- Product listing may display an error because the backend API is not connected.
- Cart and order features require the backend services and APIs.

These features will be connected in **Module 7: Frontend-Backend Integration**.

## 11\. Conclusion

- In this module, we deployed our React frontend using **Amazon S3 and Amazon CloudFront**.
- We configured private S3 access through **Origin Access Control (OAC)**, set up the default root document and custom error pages, and connected the frontend to **Amazon Cognito**.
- Finally, we built and uploaded the React application and tested the **registration, login, and logout** functionality.
- Now Proceed to **Database Configuration**, where we will configure the database required for our e-commerce application.

---

# Module 4: Data Layer

## Overview

- In this module, we will configure the **data storage and databases** required for our **e-commerce application**.
- In the previous module, we deployed the frontend using **Amazon S3 and CloudFront** and configured **Amazon Cognito** for user authentication. Now, we will prepare the **data layer** required by our backend services.
- Our application uses **Amazon S3** to store product images, **Amazon DynamoDB** to store product and cart data, and **Amazon RDS PostgreSQL** to store user and order data. We will also use **AWS Systems Manager Parameter Store** to store configuration values required by our microservices.

## In This Module

- Upload product images to **Amazon S3** and access them through **CloudFront**.
- Create **DynamoDB tables** for products and shopping carts.
- Load sample product data into **DynamoDB**.
- Create an **Amazon RDS PostgreSQL database** for user and order services.
- Store database configuration parameters in **AWS Systems Manager Parameter Store**.

---

## 4.1 Upload Product Images to Amazon S3

- We will use the **same S3 bucket created in Module 3** to store our product images. **CloudFront** will deliver these images to users and cache them at edge locations for improved performance.
- The repository contains **20 sample product images** under the `data/product-images/` directory.

### Step 1: Upload the Product Images

- Open your terminal and navigate to the repository's `data` directory.

```bash
cd data
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/267030d8-ba42-433f-a0f5-2bf7e499a6c6.png align="center")

- Run the upload script using your **S3 bucket name** and **AWS Region**, as required by the script in your repository.

```bash
bash upload-images-to-s3.sh <your-bucket-name> <your-region>
```

Example values:

- **S3 bucket:** `<your-frontend-bucket-name>`
- **AWS Region:** `ap-south-1`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f03b2071-870e-47b4-9819-1ca7d6fcb7c2.png align="center")

- The script uses the **AWS CLI** to upload the sample product images to your S3 bucket.

### Step 2: Verify the Uploaded Images

1. Open the **AWS Management Console**.
2. Navigate to **Amazon S3 → Buckets**.
3. Open your **frontend bucket**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2f3d4c16-c556-459d-ac19-ac400e647e71.png align="center")

1. Navigate to the `images/products/` directory.
2. Verify that the **20 product images** have been uploaded.

---

## 4.2 Amazon DynamoDB — NoSQL Database

**Amazon DynamoDB** is a fully managed **NoSQL database service**. In this project, we will create two tables:

- `ecommerce-products` — stores **product information**.
- `ecommerce-cart` — stores **shopping cart information** for each user.

The product table will contain **sample data** that we load manually using a script. The cart table will be populated automatically when users add items to their carts.

### Step 1: Create the Tables

1. Open the **AWS Management Console**.
2. Navigate to **Amazon DynamoDB → Tables**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/40867c1e-cf95-4482-8110-8fedc930e652.png align="center")

1. Click **Create table**.
2. Configure the following settings.
3. **DynamoDB Console → Tables → Create table**

### **Step 3: Create Products Table**

1. **Table name:** `ecommerce-products`
2. **Partition key:** `product_id` (String)

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/189b6b0c-bd56-4bd5-bc4b-9f6fc5859fb3.png align="center")

1. **Sort key:** Leave empty (no sort key needed)
2. **Table class:** DynamoDB Standard

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d6e8a883-5939-400d-9e6f-ad0389754ae1.png align="center")

1. **Capacity mode:** On-demand

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/016d68d3-cd15-44d6-9972-f879bcc11d3a.png align="center")

1. **Create table**

- Wait until the table becomes **active**.

### Step 3: Create the Cart Table

1. **DynamoDB Console → Tables → Create table**
2. **Table name:** `ecommerce-cart`
3. **Partition key:** `user_id` (String)

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/df160b3f-4264-4891-9cf0-d2d5caa75e20.png align="center")

1. **Sort key:** Leave empty (no sort key needed)
2. **Table class:** DynamoDB Standard

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d6e8a883-5939-400d-9e6f-ad0389754ae1.png align="center")

1. **Capacity mode:** On-demand

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/016d68d3-cd15-44d6-9972-f879bcc11d3a.png align="center")

1. **Create table**

- Wait until the table becomes **active**. The `user_id` partition key identifies the **user whose cart contains the items**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/c185c326-7c40-4528-91dc-3e880b92540c.png align="center")

### Step 4: Understand the Sample Product Data

The repository contains a `products.json` file with **sample data for 20 products**.

Each product contains fields such as:

- `product_id`
- `name`
- `description`
- `price`
- `stock`
- `image_url`
- `category`

Example product:

```json
{
  "product_id": "prod-001",
  "name": "Wireless Bluetooth Headphones",
  "description": "Premium noise-cancelling over-ear headphones",
  "price": 89.99,
  "stock": 150,
  "image_url": "https://example.com/images/products/prod-001.jpg",
  "category": "Electronics"
}
```

- The example `image_url` is a **placeholder**. We must replace it with the actual **CloudFront URL** before loading the product data into DynamoDB.

### Step 5: Update the Product Image URLs

- Navigate to the repository's `data` directory.

```bash
cd data
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/cc3f4e6e-9b8c-4faa-a902-5c378f095e9a.png align="center")

- Run the following script, replacing the placeholder with your **CloudFront domain**:

```bash
bash update-product-image-urls.sh https://<your-cloudfront-domain>
```

Example:

```bash
bash update-product-image-urls.sh https://dq68byzoh70jq.cloudfront.net
```

- This script updates the product image URLs in `products.json` to use **CloudFront**.
- Verify that the image URLs point to the **correct CloudFront domain** and the `images/products/` path.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/17716f1a-0388-492b-aaeb-7f22f974b219.png align="center")

- The `update-product-image-urls.sh` script replaces the placeholder image URLs in `products.json` with the CloudFront URL.
- This updates the image URLs for all 20 products, so each product points to its corresponding image in the S3 bucket through CloudFront.

### Step 6: Load Products into DynamoDB

- Run the product loading script from the `data` directory:

```bash
bash load-products.sh <your-region>
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e87bf454-4c3b-4010-9708-a70f8ab24358.png align="center")

Example:

```bash
bash load-products.sh ap-south-1
```

- The script loads the **20 sample products** into the `ecommerce-products` DynamoDB table.

**Important:** Ensure that the **table name expected by the script** matches the table you created.

- Terminal showing the product loading process.

### Step 7: Verify the Product Data

1. Open **Amazon DynamoDB → Tables**.
2. Select `ecommerce-products`.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8263d5a9-a5dc-4580-8f02-76c1c63e062f.png align="center")

1. Open **Explore table items**.
2. Verify that the table contains **20 product items**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/5a5867b0-324e-499f-9fda-99e685d32cff.png align="center")

1. Open an item and check its **product details and CloudFront image URL**.

- At this point, we have **uploaded the product images to S3** and created the **DynamoDB tables with sample product data**.

---

## 4.3 Amazon RDS — PostgreSQL Database

- **Amazon RDS** is a managed relational database service. In this project, we will use **PostgreSQL** to store the data required by the **user and order services**.
- The database will be deployed inside our existing **VPC**, using **private database subnets**. It will **not be publicly accessible**.
- We will use a **single Availability Zone configuration** for this development and testing environment to keep costs lower.

### Step 1: Create a DB Subnet Group

A **DB subnet group** identifies the subnets that Amazon RDS can use within our VPC.

1. Open **Amazon RDS → Subnet groups**.
2. Click **Create DB subnet group**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/13771502-b095-4788-afed-17764791ccdc.png align="center")

1. Enter the following details.

| Setting | Value |
| --- | --- |
| **Name** | `ecommerce-db-subnet-group` |
| **Description** | Subnet group for ecommerce RDS |
| **VPC** | `ecommerce-vpc` |

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/393a1e04-d6b5-45f6-bc68-fbf8cd8ee7ac.png align="center")

<table style="min-width: 50px;"><colgroup><col style="min-width: 25px;"><col style="min-width: 25px;"></colgroup><tbody><tr><td colspan="1" rowspan="1"><p><strong>Availability Zones</strong></p></td><td colspan="1" rowspan="1"><p><code>ap-south-1a</code>, <code>ap-south-1b</code></p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Subnets</strong></p></td><td colspan="1" rowspan="1"><p>Both private database subnets</p></td></tr></tbody></table>

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/760a3649-f15b-43a1-8a92-b3eb519d569e.png align="center")

- Select the **two private database subnets** created in Module 1.
- Click **Create**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/bd75a53d-8597-4149-8265-eddc18fb556d.png align="center")

- The DB subnet group should include the **private database subnets in both Availability Zones**.

### Step 2: Create a Security Group for RDS

The **RDS security group** controls which network traffic can reach the database.

1. Open **VPC → Security Groups**.
2. Click **Create security group**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/3d8cee2a-7a6a-43d7-95f0-437c2e4bf8a0.png align="center")

1. Configure the following settings.

| Setting | Value |
| --- | --- |
| **Security group name** | `ecommerce-rds-sg` |
| **Description** | Security group for RDS PostgreSQL |
| **VPC** | `ecommerce-vpc` |

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a0c17da8-48d1-4f0f-af6a-2cc6888a814e.png align="center")

- Under **Inbound rules**, add the following rule.

| Setting | Value |
| --- | --- |
| **Type** | PostgreSQL |
| **Protocol** | TCP |
| **Port** | `5432` |
| **Source** | `10.10.0.0/16` |
| **Description** | Allow PostgreSQL from VPC |

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/5cccb799-7204-4a4c-bbff-ba9b7c35f1f5.png align="center")

- Keep the **default outbound rules**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/562ca47c-5cc2-437e-96a4-9800845b1cd3.png align="center")

- Click **Create security group**.

**Security note:** The VPC CIDR rule is the initial configuration demonstrated in the lecture. For a more restrictive setup, allow inbound PostgreSQL traffic **only from the ECS services' security group** once it has been created.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/10ddcdea-4d36-4615-8e8b-c984feca78b4.png align="center")

### Step 3: Create the RDS PostgreSQL Database

1. Open **Amazon RDS → Databases**.
2. Click **Create database**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/13304c68-eb48-4ea5-b944-b14c06044c8b.png align="center")

3\. Configure the database as follows.

#### Engine and Template

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/43626dfd-6b62-476d-a82f-17b88e6bd46a.png align="center")

| Setting | Value |
| --- | --- |
| **Engine type** | PostgreSQL |
| **Database creation method** | Full configuration |
| **Template** | Free tier or Dev/Test, depending on availability |

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d21742da-3463-4822-9263-1a36a52e37a9.png align="center")

- Choose the available development configuration that supports a **single-AZ database**.

#### Settings

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/403b368e-9649-488a-bfc3-33257500b25d.png align="center")

| Setting | Value |
| --- | --- |
| **DB instance identifier** | `ecommercedb` |
| **Master username** | `postgres` |
| **Master password** | Create your own strong password |
| **Database authentication** | Password authentication |

- `Save the password securely.` You will need it when configuring Parameter Store.

#### Instance Configuration

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/0744a274-30ae-4540-a028-f5ca4476dccb.png align="center")

- Select the `db.t3.micro` instance class if it is **available and suitable for your account and chosen template**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/9ee11537-c5df-4a52-9e1c-3290c6553e65.png align="center")

- `Save the password securely.` You will need it when configuring Parameter Store.

#### Connectivity

| Setting | Value |
| --- | --- |
| **VPC** | `ecommerce-vpc` |
| **DB subnet group** | `ecommerce-db-subnet-group` |

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/957f545d-334b-42c2-890a-45aa51e5d964.png align="center")

<table style="min-width: 50px;"><colgroup><col style="min-width: 25px;"><col style="min-width: 25px;"></colgroup><tbody><tr><td colspan="1" rowspan="1"><p><strong>Public access</strong></p></td><td colspan="1" rowspan="1"><p>No</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>VPC security group</strong></p></td><td colspan="1" rowspan="1"><p><code>ecommerce-rds-sg</code></p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Availability Zone</strong></p></td><td colspan="1" rowspan="1"><p>Select the first Availability Zone (ap-south-1a)</p></td></tr></tbody></table>

- Keep the database **private**. Do not enable public access.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8e455bc7-ac38-47a2-b6cc-c71b90c2c59d.png align="center")

- Select the first Availability Zone (ap-south-1a)

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f10d6916-3527-486f-bee5-0762aefbce72.png align="center")

#### Additional Configuration

Expand **Additional configuration** and set the initial database name.

| Setting | Value |
| --- | --- |
| **Initial database name** | `ecommercedb` |

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b0107cc8-34a4-4928-bcd5-d3d26c19e6ff.png align="center")

- **Important:** The initial database name is required by the backend services. Make sure it matches the **expected name exactly** `ecommercedb` .

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8cfd3f2f-fa4d-4cec-b4ff-c772efb8e7b6.png align="center")

- By default, automated backups are enabled. For this development and testing project, uncheck this option to **disable backups**.
- For a production environment, keep automated backups enabled to protect your database.

4. Review all the settings.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/cfb1eb15-0252-46e3-a481-d6e29ed7a05a.png align="center")

4. Click **Create database**.

### Step 4: Wait for the Database to Become Available

RDS database creation may take approximately **5–10 minutes**.

1. Open **Amazon RDS → Databases**.
2. Select your database.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f898391d-5896-4a51-840d-6644b33907ac.png align="center")

1. Wait until its status becomes **Available**.
2. Find the **database endpoint** in the database details.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/6becca57-16ce-4ccc-b587-1d81211fac12.png align="center")

1. Copy and save the endpoint for the next section.

- The endpoint will be used by the **backend services to connect to PostgreSQL**.

**Cost note:** RDS can incur charges while running. **Stop the database when you are not using it**, if stopping is supported for your instance and setup. Remember to start it again before continuing with the backend deployment module. Storage and other resources may still incur charges while the database is stopped.

---

## 4.4 AWS Systems Manager Parameter Store

**AWS Systems Manager Parameter Store** provides a central place to store configuration values that applications can retrieve at runtime.

In this project, the backend services will use Parameter Store to retrieve the **AWS Region and database connection details**.

We will create three parameters:

- **AWS Region**
- **Database host**
- **Database password**

The database username is `postgres`, as configured when creating the RDS database.

### Step 1: Open Parameter Store

1. Open the **AWS Management Console**.
2. Navigate to **AWS Systems Manager**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/c4c2d80c-8b41-4407-93f3-e1390aa43d62.png align="center")

1. Select **Parameter Store** from the navigation menu.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/002f7a0c-23c5-4554-96c2-d749460378de.png align="center")

1. Make sure you are in the **correct AWS Region**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/0bd6e45c-4e66-475a-8d23-8bc7d41e0630.png align="center")

1. Click **Create parameter**.

### Step 2: Create the AWS Region Parameter

- **Name:** `/ecommerce/dev/aws/region`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d9d67ccb-d126-4899-b82c-428838b72a77.png align="center")

- **Type:** String
- **Data type:** Text

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/de8942ed-858c-4ddb-8ef8-afeeb3af0544.png align="center")

- **Value:** `ap-south-1`
- Replace the value with your **actual AWS Region** if you are using a different one.
- Click **Create parameter**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/062fc7c1-7629-45d5-9589-6dd61b13ae7f.png align="center")

### Step 3: Create the Database Host Parameter

Create another parameter with the following settings.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/79bb1a66-8a67-45fd-af7a-5623b1f01cd5.png align="center")

| Setting | Value |
| --- | --- |
| **Name** | `/ecommerce/dev/db/host` |
| **Type** | String |
| **Data type** | text |
| **Value** | Your RDS database endpoint |

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8a651026-0ea3-4f0d-a035-a927e8cd65c9.png align="center")

To obtain the endpoint:

1. Open **Amazon RDS → Databases**.
2. Select your **PostgreSQL database**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/39c65eb0-a841-4b31-91a2-6b83ce147090.png align="center")

1. Copy the endpoint from the database details.
2. Paste the endpoint into the parameter's value field.

- Use the **hostname provided by RDS**. Do not enter the database password in this parameter.
- Click **Create parameter**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2606d09a-8678-44b1-b8a8-fa83f3d161dd.png align="center")

### Step 4: Create the Database Password Parameter

- Create another parameter for the database password.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e6e48bf8-1afd-4a11-8b90-d83b37a030d2.png align="center")

| Setting | Value |
| --- | --- |
| **Name** | `/ecommerce/dev/db/password` |
| **Type** | SecureString |
| **Value** | Your RDS master password |

Select **SecureString** so that the stored value is **encrypted using AWS KMS**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/56535c8e-32c0-4f12-b2f2-f093cffc75b7.png align="center")

By default, these values are selected:

- **KMS key source**: My current account
- **KMS Key ID:** alias/aws/ssm (default AWS-managed key) You don't need to change these settings for our project.
- **Enter the actual password configured when you created the RDS database.**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/60caf94d-0d43-4ea2-96b5-f41ef4973a5b.png align="center")

- Click **Create parameter**.

**Important:** Never include your **real database password** in screenshots, published documentation, source control, or public repositories.

### Step 5: Verify the Parameters

Open **Systems Manager → Parameter Store** and confirm that these three parameters exist:

| Parameter name | Type | Purpose |
| --- | --- | --- |
| `/ecommerce/dev/aws/region` | String | Identifies the AWS Region |
| `/ecommerce/dev/db/host` | String | Provides the RDS endpoint |
| `/ecommerce/dev/db/password` | SecureString | Stores the database password securely |

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/777c1aa1-09e4-46cc-aaf2-1bdb190b6b7a.png align="center")

- The **backend services will retrieve these values when they run in ECS**.
- The `/ecommerce/dev/` prefix identifies the **application and environment**. If you create separate environments in the future, you can use different prefixes for their configuration values.

**Note:** The ECS task or execution role must have the **appropriate permissions to retrieve these parameters**. The required permissions will be configured as part of the backend deployment.

---

## Summary

In this module, we prepared the **data layer required by the e-commerce application**.

We completed the following tasks:

- Uploaded **20 sample product images** to Amazon S3.
- Verified that product images can be accessed through **CloudFront**.
- Created the `ecommerce-products` and `ecommerce-cart` **DynamoDB tables**.
- Updated product image URLs to use **CloudFront**.
- Loaded **20 sample products** into DynamoDB.
- Created a **private PostgreSQL database** using Amazon RDS.
- Stored the **AWS Region, database endpoint, and database password** in Systems Manager Parameter Store.

These resources provide the **data storage required by our backend services**.

## Next Steps

- Proceed to **Backend Deployment**, where we will **build container images**, push them to **Amazon ECR**, configure **ECS tasks and services**, and set up the backend infrastructure.
- If you are taking a break before the next module, remember to **stop the RDS database** if appropriate for your setup, and **start it again before continuing**.

---

# Module 5: Backend Services Deployment (ECS and ALB)

## Overview

In this module, we will deploy the four backend microservices as Docker containers using **Amazon ECS (Elastic Container Service)** with **AWS Fargate**.

We will use an **Internal Application Load Balancer (ALB)** to distribute incoming requests to the appropriate backend service based on the request path.

The four backend services are:

- **Product Service** – Port `8001`
- **Cart Service** – Port `8002`
- **User Service** – Port `8003`
- **Order Service** – Port `8004`

By the end of this module, all four ECS services should be running and registered as healthy targets behind the internal ALB.

## Architecture Overview

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/c6abdfd0-76b9-4078-800e-6df411e8bed1.png align="center")

The architecture consists of an internal Application Load Balancer deployed across the two private ECS subnets. It routes requests to the corresponding ECS services using path-based routing rules.

**Request routing:**

- `/products*` → Product Service
- `/cart*` → Cart Service
- `/user*` → User Service
- `/order*` → Order Service

Each ECS service runs as a Fargate task in the private ECS subnets. The services are not directly exposed to the internet.

## In This Module

1. Create an internal Application Load Balancer and configure target groups.
2. Configure ALB listener rules for path-based routing.
3. Create service URL parameters in AWS Systems Manager Parameter Store.
4. Create ECR repositories for all four backend services.
5. Build Docker images locally and push them to ECR.
6. Create an IAM role and security group for ECS tasks.
7. Create ECS task definitions for all four services.
8. Create the ECS cluster and ECS services.
9. Validate backend service availability.
10. Perform cost-saving steps when temporarily stopping the project.

---

## 5.1 Create an Internal Application Load Balancer

Before creating the load balancer, we need to create its security group and four target groups.

### 5.1.1 Create the ALB Security Group

1. **EC2 Console → Security Groups → Create security group**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2dc42707-af93-4909-9216-467e73a5aa31.png align="center")

1. **Name:** `ecommerce-alb-sg`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8c3b7c70-412a-453c-b43c-5748fa1113c7.png align="center")

1. **Description:** `ecommerce-alb-sg`
2. **VPC:** Select `ecommerce-vpc`
3. **Inbound rules:**
   - **Type:** HTTP
   - **Port:** 80
   - **Source:** `10.10.0.0/16` (VPC CIDR)
   - **Description:** Allow HTTP traffic from within the VPC.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/76311716-7167-4a66-9cc1-797ef7a5d8f2.png align="center")

1. **Outbound rules:** Keep the default rule (All traffic).
2. Click **Create security group**.

**Why do we allow traffic from the VPC CIDR?**

- This is an internal load balancer. Later, API Gateway will connect to this ALB using the **VPC Link** feature. The VPC Link creates network interfaces inside our VPC, and traffic reaches the ALB through those interfaces.
- For this setup, we allow HTTP traffic from the VPC CIDR because the VPC Link network interface IP addresses are not known at this stage.
- For a more restrictive production configuration, you can update the security group later to allow traffic only from the appropriate VPC Link security group or source.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/220f9065-c4f9-45a9-a8b2-2538283742ad.png align="center")

### 5.1.2 Create Target Groups

We need to create four target groups, one for each backend service.

For the first target group:

1. **EC2 Console → Load Balancing → Target Groups → Create target group**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a44be1cc-dd77-4256-8a5d-c5c0112e39d6.png align="center")

1. **Target type:** IP addresses

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/5ba16dcb-c7ca-4d23-88c0-51dd21a93972.png align="center")

1. **Target group name:** `product-service-tg`
2. **Protocol:** HTTP
3. **Port:** `8001`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/5d75edda-d5eb-417a-ad04-baf99b3ce7d0.png align="center")

1. **VPC:** Select `ecommerce-vpc`
2. **Health check path:** `/health`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ccd7877c-2626-4b84-ab99-c71a05f9667b.png align="center")

1. Click **Create target group**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/4f1b3522-60a8-4691-accd-4d18c3b18e26.png align="center")

- Review all the configurations

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d6afc6d6-959e-4a25-8269-6f7c19f382e3.png align="center")

- Click Create target group.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/401b8b87-646e-4d6c-bbfa-8532978dd49a.png align="center")

- Product Service Target Group

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/9a64cab6-6c1f-4505-9a62-877d2cc49429.png align="center")

- The health check endpoint `/health` is used to verify whether the backend service is running. In this project, the service responds with HTTP `200 OK` when it is healthy.

**Create the remaining three target groups using the following configurations:**

**Cart Service Target Group**

- **Target group name:** `cart-service-tg`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b1b0ca31-6622-4b2e-84e4-ce48a75b8d6e.png align="center")

- **Protocol:** HTTP
- **Port:** `8002`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/283d2513-a00b-4246-bb6d-0c1e3bbee61d.png align="center")

- **VPC:** `ecommerce-vpc`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ccd7877c-2626-4b84-ab99-c71a05f9667b.png align="center")

- **Health check path:** `/health`
- Review all the configurations and click Create target group.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/19a836ca-c982-49b7-84e1-b37ff0cabbc1.png align="center")

---

**User Service Target Group**

- **Target group name:** `user-service-tg`
- **Protocol:** HTTP
- **Port:** `8003`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/284d5d44-e1d2-4910-b087-22ce77cb6c08.png align="center")

- **VPC:** `ecommerce-vpc`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/807f23e3-cadb-4cd6-b52a-d52a3fb5d36b.png align="center")

- **Health check path:** `/health`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/295992b4-cbaa-42c1-9a2a-34d56f491f68.png align="center")

- Review all the configurations and click Create target group.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f4c1a136-90b9-4ad6-870c-2d9ac133ce60.png align="center")

---

**Order Service Target Group**

- **Target group name:** `order-service-tg`
- **Protocol:** HTTP
- **Port:** `8004`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d6cb13e5-e5fa-4c7f-b5e0-9b5ed1f76e91.png align="center")

- **VPC:** `ecommerce-vpc`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/807f23e3-cadb-4cd6-b52a-d52a3fb5d36b.png align="center")

- **Health check path:** `/health`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/295992b4-cbaa-42c1-9a2a-34d56f491f68.png align="center")

- Review all the configurations and click Create target group.

**Target Group Validation**

| Service | Target Group | Port | Health Check |
| --- | --- | --- | --- |
| Product | `product-service-tg` | 8001 | `/health` |
| Cart | `cart-service-tg` | 8002 | `/health` |
| User | `user-service-tg` | 8003 | `/health` |
| Order | `order-service-tg` | 8004 | `/health` |

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f0c860a7-7125-4026-93fe-e2dedbe1c9d3.png align="center")

### 5.1.3 Create the Application Load Balancer

After creating all four target groups, we can create the internal Application Load Balancer.

1. **EC2 Console → Load Balancers → Create load balancer**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/77f7e996-6f09-4423-8103-a0e03982a68e.png align="center")

1. Select **Application Load Balancer** and click **Create**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/5d0d1796-48fe-4fa3-8aed-d931bc27be17.png align="center")

**Basic Configuration**

3. **Name:** `ecommerce-internal-alb`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d75ba7f2-71d6-48fa-b401-91b5e78be829.png align="center")

3. **Scheme:** Internal
4. **IP address type:** IPv4

**Network Mapping**

6. **VPC:** Select `ecommerce-vpc`.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f85bd50d-5797-497b-8d24-8efa555cc15a.png align="center")

6. **Availability Zones:** Select both Availability Zones.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/540c6348-2e28-4a73-952f-eeed2fd0c510.png align="center")

6. **Subnets:** Select both private ECS subnets:
   - `10.10.10.0/24`
   - `10.10.11.0/24`

**Security Groups**

9. Select the existing security group: `ecommerce-alb-sg`.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/6fd709b3-14f3-44b8-8d73-c9ff9d18f2dc.png align="center")

**Listeners and Routing**

10. **Listener protocol:** HTTP
11. **Listener port:** `80`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/7098b3a0-64ce-4f41-b503-04f79c968c60.png align="center")

10. **Default action:** Forward to `product-service-tg`.

The default rule forwards requests to the Product Service. We will configure additional path-based routing rules in the next step.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b9485dae-e610-457a-80c5-e8b03b82ac64.png align="center")

13. Click **Create load balancer**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/fa9871a1-6cf1-4e42-a058-da0483abce9b.png align="center")

**Note:** It may take a few minutes for the load balancer to become active.

### 5.1.4 Configure ALB Listener Rules

After creating the ALB, we need to configure listener rules to route requests to the appropriate backend services.

1. **EC2 Console → Load Balancers → Select** `ecommerce-internal-alb`
2. Open the **Listeners** tab.
3. Select the **HTTP:80** listener.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d20ae9dc-6cc2-4f2b-8f9e-0fffc4b244c6.png align="center")

1. Under the Rules column, click **1 rule** for the `HTTP:80` listener.
2. On the Listener rules page, select Add rule to create a new routing rule.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a56a92d8-529c-4930-9c49-1caa1c37a11f.png align="center")

1. Add the following path-based routing rules.

**Rule 1 – Product Service**

- **Condition:** Path

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/19da5b94-2e23-497b-979a-006601c1135f.png align="center")

- **Path pattern:** `/products*`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/eb542e47-a84d-49fc-840f-e2535d15a41c.png align="center")

- **Action:** Forward to `product-service-tg`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/3d27bb3b-9b75-4f72-96f6-46969f892b40.png align="center")

- **Priority:** `1`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/577ca3aa-6e88-4640-b2d6-e766dc226a8e.png align="center")

- Click Next and review the configuration.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/05670129-dfb1-410b-a5c9-9b5d00b1427d.png align="center")

- Click Create rule.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/99e3d417-db8f-4010-9965-2b04e754fa43.png align="center")

**Rule 2 – Cart Service**

- **Condition:** Path
- **Path pattern:** `/cart*`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a3226c1f-eab5-4d84-914d-0f0365f008e8.png align="center")

- **Action:** Forward to `cart-service-tg`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8e3b7ddd-a980-4cf7-976c-90501e08bb16.png align="center")

- **Priority:** `2`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/89784a91-fd05-4017-8981-978edf415d73.png align="center")

- Click Next and review the configuration.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/76c087eb-8852-415d-85b1-42a8ace5d8bb.png align="center")

- Click Create rule.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/c8c2ecdf-5ba5-48c2-8667-f1ff18dbd1e5.png align="center")

**Rule 3 – User Service**

- **Condition:** Path

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/47ffc606-bc17-4d91-b96d-b13ed72daf28.png align="center")

- **Path pattern:** `/user*`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/1e3d3709-c908-4e68-95f7-9ed0db4b69e7.png align="center")

- **Action:** Forward to `user-service-tg`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/34a15b35-a08e-4ff0-b31c-590ec929cef0.png align="center")

- **Priority:** `3`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a287c572-1b26-45db-8f0c-79e602c6b0d7.png align="center")

- Click Next and review the configuration.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/c0666263-f4c6-40ee-954f-2e41eb885ca8.png align="center")

- Click Create rule.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ade1d6df-3acc-461d-aaef-59b1141db318.png align="center")

**Rule 4 – Order Service**

- **Condition:** Path

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/1dc6a1de-0b56-41d8-8cef-ae965067cb73.png align="center")

- **Path pattern:** `/order*`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ed490c3b-6ce0-40f7-b7bf-90a1ab9a0fb4.png align="center")

- **Action:** Forward to `order-service-tg`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f081bd77-026c-49c8-9851-d43281e8ce66.png align="center")

- **Priority:** `4`
- Click Next and review the configuration.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/32271868-4ee5-4372-bb11-0e53c3d00b94.png align="center")

- Click Create rule.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/7923b3e1-0598-41af-80be-47089c16a673.png align="center")

6. Save the listener rules.

**Listener Rule Validation**

| Priority | Path Pattern | Forward To |
| --- | --- | --- |
| 1 | `/products*` | `product-service-tg` |
| 2 | `/cart*` | `cart-service-tg` |
| 3 | `/user*` | `user-service-tg` |
| 4 | `/order*` | `order-service-tg` |
| Default | All other requests | `product-service-tg` |

## 5.2 Create Service URL Parameters in Parameter Store

The backend services need to know the internal ALB URL to communicate with the other backend services.

We will store the ALB DNS name in AWS Systems Manager Parameter Store.

### 5.2.1 Get the Internal ALB DNS Name

1. **EC2 Console → Load Balancers**
2. Select `ecommerce-internal-alb`.
3. Copy the **DNS name** from the load balancer details.
4. Add the HTTP protocol before the DNS name.

The parameter value should look like this:

```text
http://<internal-alb-dns-name>
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/6146378b-200c-4086-850a-166bdcef9241.png align="center")

### 5.2.2 Create the Product Service URL Parameter

1. **Systems Manager Console → Parameter Store → Create parameter**
2. **Name:** `/ecommerce/dev/product-service-url`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/22e4aa4a-a2e5-4c61-a882-3b4ff80c24a2.png align="center")

1. **Type:** String
2. **Value:** `http://<internal-alb-dns-name>`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b7bd54d1-f3eb-4ac1-b7e5-5ae7384d4964.png align="center")

1. Click **Create parameter**.

---

### 5.2.3 Create the Remaining Service URL Parameters

Repeat the same steps for the other three services.

**Cart Service**

- **Name:** `/ecommerce/dev/cart-service-url`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/548d537e-9f7a-49ef-bf1a-4c1beba0de41.png align="center")

- **Type:** String

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b7bd54d1-f3eb-4ac1-b7e5-5ae7384d4964.png align="center")

- **Value:** `http://<internal-alb-dns-name>`

---

**User Service**

- **Name:** `/ecommerce/dev/user-service-url`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/3ec7e20f-9b3b-47e0-bfb4-f881396eb9f2.png align="center")

- **Type:** String

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b7bd54d1-f3eb-4ac1-b7e5-5ae7384d4964.png align="center")

- **Value:** `http://<internal-alb-dns-name>`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b7bd54d1-f3eb-4ac1-b7e5-5ae7384d4964.png align="center")

---

**Order Service**

- **Name:** `/ecommerce/dev/order-service-url`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/32e3d048-54f9-4062-be7a-acd87c6e8c74.png align="center")

- **Type:** String

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b7bd54d1-f3eb-4ac1-b7e5-5ae7384d4964.png align="center")

- **Value:** `http://<internal-alb-dns-name>`

**Important:** All four parameters use the same internal ALB DNS name. The ALB identifies the destination service based on the request path.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d25ccea9-08ef-4caf-8441-fef4db2f04f4.png align="center")

---

## 5.3 Create ECR Repositories

Amazon ECR (Elastic Container Registry) is a fully managed container image registry that stores Docker images.

We will create a separate ECR repository for each backend service. This makes it easier to manage and update images independently.

### 5.3.1 Create the Product Service Repository

1. **AWS Console → Elastic Container Registry (ECR)**
2. Open **Repositories**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/3baafdfc-f439-4dea-8f56-4d6f88715404.png align="center")

1. Click **Create repository**.
2. **Repository name:** `ecommerce/product-service`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a780b21f-43ae-451e-bb3d-289f0d88f751.png align="center")

1. Keep the remaining settings as default.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/22fc0434-5496-4471-aa45-81c03935af49.png align="center")

1. Click **Create repository**.

### 5.3.2 Create the Remaining Repositories

Repeat the same steps for the other three services.

- **ecommerce/cart-service**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/742e2c5b-484a-4f3e-a1c4-d737c55b533a.png align="center")

- **ecommerce/user-service**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/987bd676-591f-4b79-ada3-5406d724822f.png align="center")

- **ecommerce/order-service**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/1608a517-fd99-457e-995e-f3b961b2a022.png align="center")

Verify that all four repositories have been created successfully.

| Service | ECR Repository Name |
| --- | --- |
| Product Service | `ecommerce/product-service` |
| Cart Service | `ecommerce/cart-service` |
| User Service | `ecommerce/user-service` |
| Order Service | `ecommerce/order-service` |

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/9465f4ef-d4c9-4166-b095-939c58114334.png align="center")

---

## 5.4 Build and Push Docker Images to ECR

Now that the ECR repositories are ready, we need to build Docker images from our local source code and push them to the corresponding repositories.

**Important:** Run all the commands in this section from your **local workstation**, where the project source code is available.

### 5.4.1 Get the ECR Registry URL

The ECR registry URL follows this format:

```text
<account-id>.dkr.ecr.<your-region>.amazonaws.com
```

Where:

- **Account ID:** Your 12-digit AWS account ID.
- **Region:** Your AWS region, for example, `ap-south-1`.

You can get the registry URL from:

**ECR Console → Repositories → Select a repository → Copy URI**

The registry URL is the part before the repository name.

### 5.4.2 Authenticate Docker with ECR

- Before pushing images, we need to authenticate Docker with our ECR registry.
- Run the following command:

```bash
aws ecr get-login-password --region <your-region> | docker login --username AWS --password-stdin <account-id>.dkr.ecr.<your-region>.amazonaws.com
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/5a75784c-6a41-4d52-a0b5-caefda6f6433.png align="center")

- Replace the account ID and region with your actual AWS details.
- For this project, the AWS region is:

```text
ap-south-1
```

After running the command, you should see:

```text
Login Succeeded
```

### 5.4.3 Build and Push the Product Service Image

- First, navigate to the Product Service directory:

```bash
cd services/product-service
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/1207c42e-41ab-4652-8fd6-12a87d0c54d5.png align="center")

- Make sure that the directory contains the Dockerfile.

**Step 1: Build the Docker image**

```bash
docker build -t ecommerce/product-service .
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/40392c8c-0171-4ba9-bb78-72f69c058dcc.png align="center")

- The command builds the Docker image using the Dockerfile in the current directory.

**Step 2: Tag the Docker image**

```bash
docker tag ecommerce/product-service:latest <account-id>.dkr.ecr.<your-region>.amazonaws.com/ecommerce/product-service:latest
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/6d65d898-c65f-419e-99d3-c8498f11335b.png align="center")

- Replace the account ID and region with your actual values.

**Step 3: Push the image to ECR**

```bash
docker push <account-id>.dkr.ecr.<your-region>.amazonaws.com/ecommerce/product-service:latest
```

- After the push completes, open the Product Service repository in ECR and verify that the `latest` image is available.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/4b68ef44-ae1c-4122-985a-e38ea5fc4250.png align="center")

### 5.4.4 Build and Push the Cart Service Image

- Navigate to the Cart Service directory:

```bash
cd ../cart-service
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/bfdef842-0a3c-41bf-b87e-8dd0d20bdbb5.png align="center")

- Build the Docker image:

```bash
docker build -t ecommerce/cart-service .
```

- Tag the image:

```bash
docker tag ecommerce/cart-service:latest <account-id>.dkr.ecr.<your-region>.amazonaws.com/ecommerce/cart-service:latest
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/7f4e066e-7d22-411b-bf9d-391d2bd7f882.png align="center")

Push the image:

```bash
docker push <account-id>.dkr.ecr.<your-region>.amazonaws.com/ecommerce/cart-service:latest
```

- After the push completes, open the repository in ECR and verify that the `latest` image is available.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/72475b35-d0a7-4786-9533-43f3c56d5ac2.png align="center")

### 5.4.5 Build and Push the User Service Image

Navigate to the User Service directory:

```bash
cd ../user-service
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2b0181cf-8596-4f92-b31f-eb48fc950441.png align="center")

- Build the Docker image.

```bash
docker build -t ecommerce/user-service .
```

- Tag the image:

```bash
docker tag ecommerce/user-service:latest <account-id>.dkr.ecr.<your-region>.amazonaws.com/ecommerce/user-service:latest
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ce874b50-4070-40a5-b501-348f3949d60b.png align="center")

- Push the image:

```bash
docker push <account-id>.dkr.ecr.<your-region>.amazonaws.com/ecommerce/user-service:latest
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/fbbca0b1-7d01-4268-a57f-00154a8697f7.png align="center")

- After the push completes, open the repository in ECR and verify that the `latest` image is available.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ae563971-1044-4a96-afff-9c37272e3c74.png align="center")

### 5.4.6 Build and Push the Order Service Image

Navigate to the Order Service directory:

```bash
cd ../order-service
```

- Build the Docker image.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/3cd49a43-44b9-4d16-afa3-5b24c9f2eb87.png align="center")

```bash
docker build -t ecommerce/order-service .
```

- Tag the image:

```bash
docker tag ecommerce/order-service:latest <account-id>.dkr.ecr.<your-region>.amazonaws.com/ecommerce/order-service:latest
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/3ad379c7-91e7-4cf6-8388-db02071eb443.png align="center")

- Push the image:

```bash
docker push <account-id>.dkr.ecr.<your-region>.amazonaws.com/ecommerce/order-service:latest
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2192abda-692a-46cb-9f2a-26f3e55ff200.png align="center")

- After the push completes, open the repository in ECR and verify that the `latest` image is available.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2044256b-8051-47b4-8a22-1dc98241324a.png align="center")

### 5.4.7 Verify All Images in ECR

Open the ECR console and verify that all four repositories contain their respective `latest` images.

---

## 5.5 Create an IAM Role for ECS Tasks

The backend containers need permission to communicate with other AWS services such as DynamoDB, Systems Manager Parameter Store, CloudWatch Logs, S3, and SNS.

We will create an IAM role and attach the required permissions to it.

### 5.5.1 Create the ECS Task Role

1. **IAM Console → Roles → Create role**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e6cd08f7-fdcd-49eb-bba3-93ad5249194e.png align="center")

1. **Trusted entity type:** AWS service

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ddc1c1d5-4d3c-46cc-8f6a-52e21cfd27a4.png align="center")

1. **Service:** Elastic Container Service

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d5fdb07a-fe4c-440d-a968-26286e404ac2.png align="center")

1. **Use case:** Elastic Container Service Task
2. Click **Next**.

### 5.5.2 Attach Permissions Policies

Attach the following AWS managed policies:

1. `AmazonDynamoDBFullAccess`
2. `AmazonSSMReadOnlyAccess`
3. `CloudWatchLogsFullAccess`
4. `AmazonS3ReadOnlyAccess`
5. `AmazonSNSFullAccess`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b0102789-0c66-4eb1-acb0-e7c8defccc46.png align="center")

These permissions are used for:

- **DynamoDB:** Read and write application data.
- **Systems Manager:** Read configuration values from Parameter Store.
- **CloudWatch Logs:** Send application logs to CloudWatch.
- **S3:** Read objects when required by the application.
- **SNS:** Send notifications from the Order Service.

6. **Role name:** `ecommerce-ecs-task-role`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/17ab85d9-a7e4-4de9-942d-1c4c879c9f91.png align="center")

6. Review the permissions and click **Create role**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8bcb743d-3e6b-48e0-81d6-846e7df7e17a.png align="center")

**Important:** For this development exercise, we are using one common task role for all four services. In a production environment, create separate IAM roles with only the permissions required by each service. Avoid granting unnecessary permissions such as full access when a more restrictive policy is sufficient.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/c3d3ca5b-5166-47d1-9e90-ef4e66875b3b.png align="center")

---

## 5.6 Create the ECS Tasks Security Group

The ECS tasks need to receive traffic from the internal Application Load Balancer.

We will create a security group that allows traffic on ports `8001` to `8004` from the ALB security group.

### 5.6.1 Create the Security Group

1. **VPC Console → Security Groups → Create security group**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/de0fb171-6798-4d95-b92c-b51c0ab74f84.png align="center")

1. **Name:** `ecommerce-ecs-sg`
2. **Description:** `Security group for ECS tasks`
3. **VPC:** Select `ecommerce-vpc`.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/37eb0ebb-0c81-4eff-8df3-7d79dba68320.png align="center")

### 5.6.2 Configure Inbound Rules

Add the following inbound rules:

| Type | Port | Source |
| --- | --- | --- |
| Custom TCP | 8001 | `ecommerce-alb-sg` |
| Custom TCP | 8002 | `ecommerce-alb-sg` |
| Custom TCP | 8003 | `ecommerce-alb-sg` |
| Custom TCP | 8004 | `ecommerce-alb-sg` |

Alternatively, you can configure a single custom TCP port range from `8001-8004`, using `ecommerce-alb-sg` as the source.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8630672e-ebff-4b3b-afb9-5eb43cee6373.png align="center")

### 5.6.3 Configure Outbound Rules

Keep the default outbound rule:

- **Type:** All traffic
- **Destination:** `0.0.0.0/0`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/29d85588-17b9-4188-b605-dc3222249ee3.png align="center")

The ECS tasks need outbound connectivity to communicate with AWS services and other required endpoints. The private ECS subnets use the NAT Gateway for internet access.

- Click **Create security group**.

---

## 5.7 Create ECS Task Definitions

An ECS task definition is a blueprint that tells Amazon ECS how to run a container.

It defines important configuration details such as:

- Docker image URI.
- Container name.
- Container port.
- CPU and memory allocation.
- IAM task role.
- Environment variables.
- CloudWatch logging configuration.

We will create four separate task definitions, one for each backend service.

### 5.7.1 Create the Product Service Task Definition

**ECS Console → Task definitions → Create new task definition**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/1aa4be66-ab39-49b7-b2be-8adc90e7766f.png align="center")

**Task Definition Configuration**

2. **Task definition family:** `ecommerce-product-service`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/0793634e-8b7c-484b-9487-db425aab7b07.png align="center")

2. **Launch type:** AWS Fargate
3. **Operating system/Architecture:** Linux/X86_64
4. **CPU:** `0.25 vCPU`
5. **Memory:** `0.5 GB`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/cec4540d-7b85-4799-89f3-552fe1f1f115.png align="center")

2. **Task role:** Select `ecommerce-ecs-task-role`.
3. **Task execution role:** Select **Create default role**.

- The task role provides permissions for the application to access AWS services. The task execution role allows ECS to perform tasks such as pulling images from ECR and sending logs to CloudWatch.
- AWS will create the default execution role if it does not already exist. We will reuse it for the remaining task definitions.

**Container Configuration**

9. **Container name:** `product-service`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/03e9ba36-c81a-493b-bd13-c5c64833b1b4.png align="center")

9. Click Browse containers
10. select the `ecommerce/product-service` repository.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/4acea349-e197-4ce4-ac95-682e8628c936.png align="center")

9. **Image URI:** Select the `latest` image from:

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8dd0d689-2fb7-41a2-a8f4-351c560bb8e1.png align="center")

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/cc511aa9-215b-4cd5-8ac2-85335a22a30f.png align="center")

11. **Port mapping:**
    - Container port: `8001`
    - Protocol: TCP

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/805597ab-ecdd-490f-9dfb-922faa3d8925.png align="center")

**Environment Variables**

12. Add the following environment variables:

| Key | Value |
| --- | --- |
| `ENVIRONMENT` | `dev` |
| `AWS_REGION` | `ap-south-1` |

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f22c74cb-f40d-4eb2-b2a2-f1bb97c6c10b.png align="center")

- Replace the region with your actual AWS region if it is different.
- Review the configuration and click **Create task definition**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e500aced-bf88-434b-b0a6-3be853332145.png align="center")

### 5.7.2 Create the Cart Service Task Definition

Repeat the same process with the following service-specific details:

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/c12bbb26-6cd0-4bb4-9ae3-296dae3e8dcb.png align="center")

- **Task definition family:** `ecommerce-cart-service`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/55de368a-faa2-41eb-a888-46fe58d35519.png align="center")

- **Operating system/Architecture:** Linux/X86_64
- **CPU:** `0.25 vCPU`
- **Memory:** `0.5 GB`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/3015b9d6-97bd-4ace-b2a3-b5c8e66020e4.png align="center")

- Earlier, select Create default role to allow AWS to create the default ECS task execution role.
- AWS has now created the role named `ecsTaskExecutionRole`.
- Select the existing `ecsTaskExecutionRole` from the Task execution role dropdown.
- **Container name:** `cart-service`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a23f5ae8-83c9-4fa8-9669-c04563c33f90.png align="center")

- **Image:** `ecommerce/cart-service:latest`
- **Container port:** `8002`
- **Environment variable:** `ENVIRONMENT = dev`
- **Environment variable:** `AWS_REGION = ap-south-1`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/dcf7ebb8-3f24-4580-a7be-fadd428e41f0.png align="center")

- Click **Create task definition**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/bc6a6f06-5f56-4184-a5e7-f99a209c9a8b.png align="center")

### 5.7.3 Create the User Service Task Definition

Repeat the same process with these details:

- **Task definition family:** `ecommerce-user-service`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/43f6c192-9710-4c4b-91cf-6a56793d1fbf.png align="center")

- **Operating system/Architecture:** Linux/X86_64
- **CPU:** `0.25 vCPU`
- **Memory:** `0.5 GB`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/3015b9d6-97bd-4ace-b2a3-b5c8e66020e4.png align="center")

- Select the existing `ecsTaskExecutionRole` from the Task execution role dropdown.
- **Container name:** `user-service`
- **Image:** `ecommerce/user-service:latest`

  ![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/7f1809d0-71d9-41cc-a8f9-510763c0e66a.png align="center")
- **Container port:** `8003`
- **Environment variable:** `ENVIRONMENT = dev`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/dcf7ebb8-3f24-4580-a7be-fadd428e41f0.png align="center")

- **Environment variable:** `AWS_REGION = ap-south-1`
- Click **Create task definition**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/10652523-e8af-47a4-b627-7079095498a6.png align="center")

### 5.7.4 Create the Order Service Task Definition

Repeat the same process with these details:

- **Task definition family:** `ecommerce-order-service`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a913de7a-5b4f-4efe-8f67-672781fe1dcb.png align="center")

- **Operating system/Architecture:** Linux/X86_64
- **CPU:** `0.25 vCPU`
- **Memory:** `0.5 GB`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/3015b9d6-97bd-4ace-b2a3-b5c8e66020e4.png align="center")

- Select the existing `ecsTaskExecutionRole` from the Task execution role dropdown.
- **Container name:** `order-service`
- **Image:** `ecommerce/order-service:latest`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d34398a3-7e27-461c-96da-f3566e92a258.png align="center")

- **Container port:** `8004`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/dcf7ebb8-3f24-4580-a7be-fadd428e41f0.png align="center")

- **Environment variable:** `ENVIRONMENT = dev`
- **Environment variable:** `AWS_REGION = ap-south-1`
- Click **Create task definition**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f1f31799-5ba4-4bb3-b1f7-a292d0be8605.png align="center")

### 5.7.5 Verify All Task Definitions

After creating all four task definitions, verify that they are available in the ECS console.

| Service | Task Definition | CPU | Memory | Container Port |
| --- | --- | --- | --- | --- |
| Product | `ecommerce-product-service` | 0.25 vCPU | 0.5 GB | 8001 |
| Cart | `ecommerce-cart-service` | 0.25 vCPU | 0.5 GB | 8002 |
| User | `ecommerce-user-service` | 0.25 vCPU | 0.5 GB | 8003 |
| Order | `ecommerce-order-service` | 0.25 vCPU | 0.5 GB | 8004 |

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/3520227a-47e0-4b40-8f06-ae2455268ec1.png align="center")

---

## 5.8 Create the ECS Cluster and Services

Now that all four task definitions are ready, we can create the ECS cluster and launch the backend services.

### 5.8.1 Create the ECS Cluster

1. **ECS Console → Clusters → Create cluster**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/41cf0410-53ff-4f24-8f69-a4fbece54246.png align="center")

1. **Cluster name:** `ecommerce-cluster`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d577e799-4db0-485f-8a19-cc8409677687.png align="center")

1. **Infrastructure:** Fargate only.
2. Keep the remaining settings as default.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2db0763a-2f54-4428-b9f7-f5abfda8c7c6.png align="center")

1. Click **Create cluster**.

AWS Fargate manages the underlying compute infrastructure required to run the containers. We do not need to create or manage EC2 instances for the ECS tasks.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/44048645-f8c5-4d85-8b30-0e77a5509817.png align="center")

### 5.8.2 Create the Product Service

1. Open `ecommerce-cluster`.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a4ad6578-befd-424f-961d-547fbe87e360.png align="center")

1. Go to **Services → Create**.

**Deployment Configuration**

3. **Task definition:** Select `ecommerce-product-service`
4. Task definition revision: Select the latest revision.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/1aca90c6-6794-4287-b9b0-f00341827ca3.png align="center")

3. **Service name:** Once you select the task definition, AWS may automatically populate a service name with a generated suffix. You can keep the automatically generated name.
4. **Compute option:** Fargate.
5. **Desired tasks:** `1`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/cef4077c-6490-41cd-b888-127b4fdcc3a7.png align="center")

- For this development and testing project, we will run one task per service. In production, multiple tasks may be required for high availability and scalability.

**Networking Configuration**

7. **VPC:** Select `ecommerce-vpc`.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b6a7e89b-7263-491b-9816-3478a798a858.png align="center")

7. **Subnets:** Select only the two private ECS subnets:
   - `10.10.10.0/24`
   - `10.10.11.0/24`
8. **Security group:** Select `ecommerce-ecs-sg`.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e5b5652f-a8fd-4df3-9dfe-65bbdafa4dfa.png align="center")

7. **Public IP:** Turned off.

The ECS services should not be directly accessible from the internet. They will receive traffic through the internal ALB.

**Load Balancing Configuration**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2707348e-d1cb-4bd6-9e9b-12bf2a5894ec.png align="center")

11. Enable **Use load balancing**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/7c1bcf0b-e687-4bbf-a96a-6edeb40c800f.png align="center")

11. **Load balancer type:** Application Load Balancer
12. **Container:** Select `product-service`.
13. **Container port:** Verify `8001:8001` (Host port: 8001, Container port: 8001).
14. **Load balancer:** Select `ecommerce-internal-alb`.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/38865049-2402-4954-abc7-b83d267c5975.png align="center")

11. **Listener:** Select the existing HTTP:80 listener.
12. **Target group:** Select `product-service-tg`.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/5789d57b-9bfb-4a02-a41b-7d5196c35aa1.png align="center")

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/62929410-8be5-4262-8c79-557c01d5968b.png align="center")

11. Review the configuration and click **Create service**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f001ed9b-6f10-471e-aac5-2854109e7143.png align="center")

### 5.8.3 Create the Cart Service

Repeat the same steps to create the Cart Service.

Use the following service-specific details:

- **Task definition:** `ecommerce-cart-service`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/579c32ca-4371-4c4a-9a42-6ec96d402477.png align="center")

- **Service name:** You can keep the automatically generated name.
- **Desired tasks:** `1`
- **VPC:** `ecommerce-vpc`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b6a7e89b-7263-491b-9816-3478a798a858.png align="center")

- **Subnets:** Both private ECS subnets

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e5b5652f-a8fd-4df3-9dfe-65bbdafa4dfa.png align="center")

- **Security group:** `ecommerce-ecs-sg`
- **Public IP:** Off
- **Load balancer type:** Application Load Balancer

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/91535498-c931-4ace-8538-4127ab4de890.png align="center")

- **Container:** Select `cart-service`.
- **Container port:** Verify `8002:8002` (Host port: 8002, Container port: 8002).
- **Load balancer:** `ecommerce-internal-alb`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/38865049-2402-4954-abc7-b83d267c5975.png align="center")

- **Listener:** Existing HTTP:80 listener

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e5c79427-487d-490a-9895-403fd562a12c.png align="center")

- **Target group:** `cart-service-tg`
- Click **Create service**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ad8cceb9-97d4-4237-a1da-4546680200d8.png align="center")

### 5.8.4 Create the User Service

Repeat the same steps using:

- **Task definition:** `ecommerce-user-service`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f2b7c1bb-8b95-47c6-a013-9ce0117a1616.png align="center")

- **Desired tasks:** `1`
- **VPC:** `ecommerce-vpc`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b6a7e89b-7263-491b-9816-3478a798a858.png align="center")

- **Subnets:** Both private ECS subnets
- **Security group:** `ecommerce-ecs-sg`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e5b5652f-a8fd-4df3-9dfe-65bbdafa4dfa.png align="center")

- **Public IP:** Off
- **Load balancer type:** Application Load Balancer

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/058f8f36-3e70-4c2e-9f3d-986fd9b9baae.png align="center")

- **Container:** Select `user-service`.
- **Container port:** Verify `8003:8003` (Host port: 8003, Container port: 8003).
- **Load balancer:** `ecommerce-internal-alb`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/38865049-2402-4954-abc7-b83d267c5975.png align="center")

- **Listener:** Existing HTTP:80 listener
- **Target group:** `user-service-tg`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/93503bfe-452b-4456-a3aa-594847b5bfda.png align="center")

- Click **Create service**.

### 5.8.5 Create the Order Service

Repeat the same steps using:

- **Task definition:** `ecommerce-order-service`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d26966a0-d811-4238-9d78-277b4bccc592.png align="center")

- **Desired tasks:** `1`
- **VPC:** `ecommerce-vpc`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b6a7e89b-7263-491b-9816-3478a798a858.png align="center")

- **Subnets:** Both private ECS subnets
- **Security group:** `ecommerce-ecs-sg`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e5b5652f-a8fd-4df3-9dfe-65bbdafa4dfa.png align="center")

- **Public IP:** Off
- **Load balancer type:** Application Load Balancer

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/889bbd5b-63d1-4a9a-9c2b-f10a5ab225fb.png align="center")

- **Container:** Select `order-service`.
- **Container port:** Verify `8004:8004` (Host port: 8004, Container port: 8004).
- **Load balancer:** `ecommerce-internal-alb`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/38865049-2402-4954-abc7-b83d267c5975.png align="center")

- **Listener:** Existing HTTP:80 listener

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/51504d18-abd7-42ba-b459-2a617cc0be55.png align="center")

- **Target group:** `order-service-tg`
- Click **Create service**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a367b8c9-aab5-49d6-bcd7-474db21c61aa.png align="center")

### 5.8.6 Verify ECS Service Status

After creating all four services, wait for the ECS tasks to start.

Open:

**ECS Console → Clusters → ecommerce-cluster → Services**

Verify that all four services show a running task.

For this development setup, each service should have:

- **Desired tasks:** 1
- **Running tasks:** 1

It may take a few minutes for all services to reach the running state.

### 5.8.7 Verify ALB Target Group Health

We should also verify that the ECS tasks are registered as healthy targets behind the ALB.

1. **EC2 Console → Target Groups**
2. Open `product-service-tg`.
3. Go to the **Targets** tab.
4. Verify that the registered target is **Healthy**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f461f6f2-715f-40c8-a139-00ac828a6d29.png align="center")

- Repeat the same verification for:
- `cart-service-tg`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/435b8a9c-6591-4ba0-8428-e2fbff91d46c.png align="center")

- `user-service-tg`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2cba4935-6917-44a1-99c1-b749710caf43.png align="center")

- `order-service-tg`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ec5c1d33-916f-42ce-bade-6c7d4d8254b7.png align="center")

All four target groups should show healthy targets.

---

## 5.9 Test Backend Service Connectivity

- At this stage, all four ECS services are running behind the internal ALB.
- However, the ALB is internal, so it cannot be accessed directly from the public internet.
- To test the backend connectivity, we will temporarily launch a test EC2 instance inside the same VPC.

### 5.9.1 Launch a Test EC2 Instance

1. **EC2 Console → Instances → Launch instances**
2. **Name:** `test-ec2-instance`
3. **AMI:** Select Amazon Linux.
4. **Instance type:** Select an appropriate instance type, such as `t3.micro`.
5. **Key pair:** Select an existing key pair or create a new one.
6. **VPC:** Select `ecommerce-vpc`.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/28258387-cd66-432b-829a-396223556bdf.png align="center")

1. **Subnet:** Select one of the public subnets.
2. **Auto-assign public IP:** Enable.
3. Configure the security group to allow SSH access for the temporary test instance.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/4a70f649-16e5-419b-80a3-95c6941d58a4.png align="center")

1. Launch the instance.

**Security note:** For this temporary testing instance, restrict SSH access to your own public IP wherever possible instead of allowing SSH from anywhere. Terminate the instance after testing.

### 5.9.2 Connect to the Test EC2 Instance

1. Open the EC2 instance.
2. Click **Connect**.
3. Select **EC2 Instance Connect**.
4. Click **Connect** to open the browser-based terminal.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f48b6fcf-c1a4-4d34-808b-c2caac4baca3.png align="center")

### 5.9.3 Test the Product Service

- First, copy the internal ALB DNS name:

**EC2 Console → Load Balancers → ecommerce-internal-alb**

- From the test EC2 terminal, execute:

```bash
curl http://<internal-alb-dns-name>/products
```

- Replace `<internal-alb-dns-name>` with your actual ALB DNS name.

The request follows this path:

1. The test EC2 instance sends an HTTP request to the internal ALB.
2. The ALB receives the request on port 80.
3. The listener matches the `/products*` path rule.
4. The request is forwarded to `product-service-tg`.
5. The Product Service running on ECS receives the request.
6. The Product Service retrieves product information from DynamoDB and returns the response.

- If the request returns the product data, it confirms that the Product Service is reachable through the internal ALB and can retrieve the product information.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2a9b40b6-fb63-4682-81a5-9ba1788f3640.png align="center")

**Curl Command Returning Product Data**

**Note:** The other backend services may require authentication headers or additional request details. We will validate their application functionality during the later integration steps.

### 5.9.4 Terminate the Test EC2 Instance

After successfully testing the backend connectivity:

1. **EC2 Console → Instances**
2. Select `test-ec2-instance`.
3. Choose **Instance state → Terminate instance**.
4. Confirm the termination.

This instance was created only for temporary connectivity testing. Do not keep it running unnecessarily.

---

## 5.10 Cost-Saving Steps When Pausing the Project

This project uses AWS resources that may continue to incur charges even when we are not actively working on the application.

If you are unable to continue with the remaining modules for the day, perform the following steps before stopping.

### 5.10.1 Stop the RDS Database

1. **RDS Console → Databases**
2. Select your database instance.
3. Choose **Actions → Stop temporarily** (if the option is available).
4. Confirm the action.

RDS may continue to incur storage and other applicable charges even when stopped.

### 5.10.2 Set ECS Desired Tasks to Zero

We should stop the running ECS tasks to avoid unnecessary compute charges.

Repeat the following steps for all four ECS services:

1. **ECS Console → Clusters → ecommerce-cluster**
2. Select the service.
3. Click **Update**.
4. Change **Desired tasks** to `0`.
5. Update the service.

Repeat this for:

- `ecommerce-product-service`
- `ecommerce-cart-service`
- `ecommerce-user-service`
- `ecommerce-order-service`

This scales the services down to zero running tasks.

### 5.10.3 Delete the NAT Gateway

The NAT Gateway incurs charges while it is provisioned. The associated Elastic IP may also have applicable charges.

If you are pausing the project for an extended period:

1. **VPC Console → NAT Gateways**
2. Select the NAT Gateway.
3. Choose **Delete NAT Gateway**.
4. Confirm the deletion.

Release the associated Elastic IP if it is no longer required.

**Important:** Deleting the NAT Gateway will affect the route table used by the private ECS subnets.

### 5.10.4 Update the Route Table When Recreating the NAT Gateway

When you resume the project on another day, you need to recreate the NAT Gateway.

Follow these steps:

1. Create a new NAT Gateway in one of the public subnets.
2. Allocate and associate a new Elastic IP.
3. Wait until the NAT Gateway reaches the **Available** state.
4. Open **VPC Console → Route Tables**.
5. Select the route table associated with the private ECS subnets.
6. Open the **Routes** tab.
7. Click **Edit routes**.
8. Remove the old route that points to the deleted NAT Gateway and is showing a `blackhole` status.
9. Add the new route:
   - **Destination:** `0.0.0.0/0`
   - **Target:** Select the newly created NAT Gateway.
10. Save the changes.

**\[Private Route Table Showing the New NAT Gateway Route\]**

### 5.10.5 Resume the Project

When you are ready to continue:

1. Make sure the NAT Gateway is in the **Available** state.
2. Verify that the private ECS route table points to the new NAT Gateway.
3. Start the RDS database and wait until its status becomes **Available**.
4. Open the ECS cluster.
5. Update each of the four ECS services.
6. Set **Desired tasks** back to `1`.
7. Wait until all four ECS services are running.
8. Verify that all four ALB target groups show healthy targets.

After completing these steps, the project should return to its previous working state.

**Important:** NAT Gateway deletion and recreation can change the Elastic IP and incur additional charges. Also review other running AWS resources and billing before leaving the project idle.

---

## Module 5 Summary

In this module, we successfully configured the backend infrastructure for our e-commerce application.

We completed the following tasks:

1. Created an **internal Application Load Balancer**.
2. Created four **ALB target groups** for the backend services.
3. Configured **path-based routing rules** using the HTTP listener.
4. Stored the internal ALB URL in **AWS Systems Manager Parameter Store**.
5. Created four **Amazon ECR repositories**.
6. Built and pushed the four Docker images to ECR.
7. Created the **IAM task role** with the required AWS permissions.
8. Created the **ECS security group**.
9. Created four **ECS task definitions** using AWS Fargate.
10. Created the **ECS cluster** and deployed all four backend services.
11. Verified that the ECS tasks were running and the ALB target groups were healthy.
12. Tested Product Service connectivity using a temporary EC2 instance.

Our backend services are now deployed and running behind the internal Application Load Balancer.

In the next module, we will configure **Amazon API Gateway** and connect it to the internal ALB using **VPC Link**, allowing external clients to access our backend APIs.

**Next Module: API Gateway Configuration**

---

# Module 6: API Gateway Configuration

## Overview

- In this module, we will create an **Amazon API Gateway HTTP API** and integrate it with our internal Application Load Balancer (ALB).
- Since our ALB is internal and resides inside the private subnets of our VPC, it cannot be accessed directly from the internet. To securely connect API Gateway with the internal ALB, we will use **VPC Link**.
- We will also configure authentication using **Amazon Cognito JWT Authorizer**, allowing public access to the product endpoint while protecting the remaining backend APIs.

## In This Module

1. Create a security group for VPC Link.
2. Create a VPC Link to connect API Gateway with our VPC.
3. Create an HTTP API Gateway.
4. Configure a private HTTP integration with the internal ALB.
5. Create a Cognito JWT Authorizer.
6. Configure API routes.
7. Configure CORS.
8. Test the API Gateway endpoints.

## Architecture

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/224078c4-eec5-4bfd-893b-dd12f40d920b.png align="center")

The API Gateway will expose three routes:

- **GET /products** – Public endpoint. No authentication required.
- **ANY /{proxy+}** – Handles the remaining API requests and requires Cognito JWT authentication.
- **OPTIONS /{proxy+}** – Handles CORS preflight requests without authentication.

The API Gateway forwards requests to the internal ALB through VPC Link. The ALB then uses its path-based routing rules to forward requests to the appropriate ECS microservices.

---

## 6.1 Create VPC Link

- Before creating API Gateway, we need to establish a private connection between API Gateway and our internal ALB.
- For this, AWS provides a feature called **VPC Link**.
- VPC Link allows API Gateway to communicate privately with resources inside our VPC, such as an internal Application Load Balancer.

### 6.1.1 Create a Security Group for VPC Link

First, we need to create a security group that will be associated with the VPC Link.

1. **VPC Console → Security Groups → Create security group**
2. **Security group name:** `ecommerce-vpclink-sg`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ad9c36e7-a09d-4918-9007-c4b325950a92.png align="center")

1. **Description:** `ecommerce-vpclink-sg`
2. **VPC:** Select `ecommerce-vpc`.

**Inbound rules:**

Add the following rules:

- **Type:** HTTP
- **Port:** `80`
- **Source:** `0.0.0.0/0`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ff4cf85d-74ec-4516-88b4-ad94a1e42510.png align="center")

Add another rule:

- **Type:** HTTPS
- **Port:** `443`
- **Source:** `0.0.0.0/0`

**Outbound rules:**

- Keep the default outbound rule allowing all traffic.
- Click **Create security group**.

### 6.1.2 Create VPC Link

After creating the security group, we can create the VPC Link from the API Gateway console.

1. **API Gateway Console → VPC Links → Create VPC Link**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/09245dcc-0988-4481-bb94-12349f3bc5b6.png align="center")

1. **VPC Link version:** Select VPC Link for HTTP APIs (V2).
2. **Name:** `ecommerce-vpc-link`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/3c8f7039-f00a-4ad9-abd0-ae3134feca91.png align="center")

1. **VPC:** Select `ecommerce-vpc`.
2. **Subnets:** Select both private ECS subnets:
   - `ecommerce-private-ecs-1`
   - `ecommerce-private-ecs-2`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e6f56e5f-e439-43f0-9921-1582d7cc29b4.png align="center")

1. **Security groups:** Select `ecommerce-vpclink-sg`.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/81c3ee58-d909-4c89-a71c-af84367f2270.png align="center")

1. Click **Create**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a3d8d08b-a01a-4e85-989f-33752a060593.png align="center")

**Note:** VPC Link creation may take a few minutes. Wait until its status changes to **Available** before proceeding.

---

## 6.2 Create HTTP API Gateway

- Once the VPC Link is available, the next step is to create the HTTP API.
- We are using HTTP API because it provides a simpler and faster API Gateway option for our project.

1. **API Gateway Console → APIs → Create API**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8d03077d-18f3-4c96-81ae-f9946b16181b.png align="center")

1. Under **HTTP API**, click **Build**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/30bdd65c-6a36-4a53-b54d-c52682219880.png align="center")

1. **API name:** `ecommerce-api-http`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a5d81516-32fb-4285-bafd-01e988a1ef44.png align="center")

1. Skip adding integrations for now. We will configure the integration manually in the next step.
2. Click **Review and create**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/0a07a518-cce0-418f-bdb6-605c57f978c6.png align="center")

1. Click **Create**.

After creating the API, configure the default stage.

---

## 6.3 Create HTTP Integration

- Now we need to connect API Gateway to our internal Application Load Balancer.
- For this, we will create a **Private Resource Integration** using the VPC Link created earlier.
- We only need one integration because all our backend services are accessible through the same internal ALB.

1. Open your API Gateway.
2. Navigate to **Develop → Integrations → Manage integrations**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/6a1e8709-cb03-4588-ad4d-9d13d8b9c0a7.png align="center")

1. Click **Create**.

Configure the following settings:

4. **Integration type:** Private resource.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/30252b88-3d25-452b-a6cd-5025396f1359.png align="center")

4. **Selection method:** Select Select manually.
5. **Target service:** ALB/NLB.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a5962a02-0dbb-4d29-ac57-5ae1ac78ea8a.png align="center")

4. **Load balancer:** Select `ecommerce-internal-alb`.
5. **Listener:** Select `HTTP:80`.
6. **Connectivity type:** Select VPC Link.
7. **VPC Link:** Select `ecommerce-vpc-link`.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/6f5bedb8-d6de-4f78-a4b9-43827c2d201e.png align="center")

4. Keep the remaining settings as default.
5. Click **Create**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e64392ac-f549-4721-838c-e6b8bf5d3691.png align="center")

- The Private Resource Integration has been created.
- You can verify these details:
- **Integration:** HTTP:80 - ecommerce-internal-alb
- **Load balancer listener:** ANY HTTP:80 - ecommerce-internal-alb
- **VPC Link:** 2cwj3j
- **Timeout:** 30000 ms

**Note:** This single integration connects API Gateway to the internal ALB. We will reuse it for all three API routes. The ALB will handle path-based routing to the appropriate backend microservices.

---

## 6.4 Create Cognito JWT Authorizer

- Next, we need to configure authentication for our protected API endpoints.
- We will use the existing Amazon Cognito User Pool created in Module 2.
- API Gateway supports JWT authorization, allowing it to validate the JWT tokens issued by Cognito.

### Create the JWT Authorizer

1. Open your API Gateway.
2. Navigate to **Authorization → Authorizers**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d95b683e-55fb-45f6-b478-bcc676b345b3.png align="center")

1. Click **Create authorizer**.

Configure the following settings:

4. **Authorizer name:** `cognito-jwt-authorizer`
5. **Authorizer type:** JWT.
6. **Identity source:** `$request.header.Authorization`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b75a033b-bcaa-48dd-be9d-b7a34bb2ca7a.png align="center")

**Issuer URL:**

Enter your Cognito User Pool issuer URL using the following format:

```text
https://cognito-idp.<your-region>.amazonaws.com/<user-pool-id>
```

- Replace `<your-region>` and `<user-pool-id>` with your actual values.
- For this project, the AWS Region is `ap-south-1`.
- You can find the User Pool ID in the Amazon Cognito console.

**Audience:**

Enter your **Cognito** **App Client ID** created in Module 2.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/4bfaecb1-7e30-42af-bc21-81eb8ebedd20.png align="center")

7. Click **Create authorizer**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/0eaac927-24d9-49e7-b3eb-f355144581c1.png align="center")

**Note:** Make sure the Issuer URL contains the correct Cognito User Pool ID and the Audience contains the correct App Client ID.

---

## 6.5 Create API Routes

- Now we have successfully created the **VPC Link, HTTP API, ALB Integration, and Cognito JWT Authorizer**.
- The next step is to create three API routes.
- All three routes will use the same ALB integration, but their authentication settings will be different.
- Before creating the routes, let's understand why we need them.
- **Why do we need API routes?**
- API routes define how incoming API requests should be handled by API Gateway. They specify:
- Which HTTP method and resource path should be matched.
- Which backend integration should receive the request.
- Whether authentication is required to access the endpoint.

### Route 1: Public Products Route

This route allows users to access the product list without authentication.

1. Open your API → **Routes → Create route**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/800fd03b-34ec-44a2-8ccb-d6e5676bc2cf.png align="center")

1. **Method:** GET

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a0ef6147-202f-46cb-aae3-65272b9392c8.png align="center")

1. **Resource path:** `/products`
2. Create the route.
3. After creating the route, select GET /products.
4. Under Integration, click Attach integration.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/1b7dd6df-7898-43ab-a29a-7e6f47e0cfdf.png align="center")

1. Select the existing integration from the dropdown:\
   `HTTP:80 - ecommerce-internal-alb`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/1edd0ef0-0b52-4a70-9d6f-484c509693f5.png align="center")

1. Verify that the selected integration points to the correct internal ALB.
2. Click Attach integration.
3. After clicking Attach integration, AWS automatically takes you to the Integrations tab.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/018079e2-acca-48e9-8c37-448272c14ec7.png align="center")

### Route 2: Authenticated Proxy Route

This route handles requests for the remaining backend services, such as Cart, User, and Order.

We will use the `ANY` method with the `{proxy+}` path to capture the remaining API requests.

1. Navigate to **Routes → Create route**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/83f455ac-f0cd-4e6a-8564-ec8c95764641.png align="center")

1. **Method:** ANY

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/9e2d11c4-c0b1-4d12-9fb2-4dd0f3216c76.png align="center")

1. **Resource path:** `/{proxy+}`
2. Create the route.
3. **Attach Cognito JWT Authorizer**
4. Select the newly created **ANY /{proxy+}** route.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/cdf5ecfd-4021-453b-925c-61b14466e80e.png align="center")

7. Under Authorization, click Attach authorization.
8. Select the existing **cognito-jwt-authorizer** from the dropdown.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8cacbe40-210b-4626-8dfe-f2024746d929.png align="center")

7. Click Attach authorizer.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/5eca8222-9a0b-4ad9-b5c3-ce7855884316.png align="left")

**Attach the ALB Integration to the Route**

1. After creating the **ANY /{proxy+}** route, select the ANY method under /{proxy+}.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f2265b89-7dab-4a88-a3a0-cf3a1c47fdc2.png align="center")

1. Under Integration, click **Attach integration**.
2. You will be redirected to the Integrations tab.
3. Under Attach integrations to routes, select the existing integration from the dropdown: **HTTP:80 - ecommerce-internal-alb**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/0dfa15c9-c240-4ee2-86ae-9879002cc05c.png align="center")

1. Click Attach integration.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/fb677ae8-4876-45b4-9cf5-c078fbace956.png align="center")

### Route 3: CORS Preflight Route

- The third route is required to handle browser CORS preflight requests.
- Before sending certain cross-origin API requests, browsers send an HTTP OPTIONS request to check whether the requested operation is allowed.
- We will create a dedicated OPTIONS route without authentication.

1. Navigate to **Routes → Create route**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f85c0939-abdd-4d91-9804-88dea08e8427.png align="center")

1. **Method:** OPTIONS
2. **Resource path:** `/{proxy+}`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ae1ef04f-2219-4e99-80dd-fe805217c737.png align="center")

1. **Create the route.**
2. **Attach the ALB Integration**
3. Select the newly created **OPTIONS** method under **/{proxy+}.**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/373bb25e-e087-44e9-a8fa-4e4250403804.png align="center")

1. Under Integration, click **Attach integration.**
2. You will be redirected to the Integrations tab.
3. Select the existing integration from the dropdown: **HTTP:80 - ecommerce-internal-alb**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/71163547-0672-4204-ba7e-a3854e1c3b37.png align="center")

1. Click **Attach integration**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e87149f0-fda9-4bde-9cb1-6b0c21f331b2.png align="center")

### Verify All Three Routes

After creating the routes, verify the following configuration:

- **GET /products:** ALB integration attached; no authorization.
- **ANY /{proxy+}:** ALB integration attached; Cognito JWT Authorizer enabled.
- **OPTIONS /{proxy+}:** ALB integration attached; no authorization.

---

## 6.6 Configure CORS

Now we need to configure CORS (Cross-Origin Resource Sharing) to allow our frontend application to communicate with API Gateway.

Since our frontend and API Gateway use different domain names, we need to configure the appropriate CORS settings.

1. Open your API Gateway.
2. Navigate to **CORS → Configure**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/dad78d3a-cada-49b6-a781-f31fb803e3c4.png align="center")

Configure the following settings:

**Access-Control-Allow-Origin:**

```text
*
```

This allows requests from all origins during development.

**Access-Control-Allow-Headers:**

```text
*
```

This allows all request headers, including custom headers such as `Authorization`.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d620aa92-b5fe-41e8-aea1-d184fd2d26ea.png align="center")

**Access-Control-Allow-Methods:**

Select the following methods:

- GET
- POST
- PUT
- DELETE
- OPTIONS

3. Keep the remaining settings as default.
4. Click **Save**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/88dcc4f5-98a7-435c-8b07-aac647b8d2cd.png align="center")

**Note:** Using `*` is convenient for development and testing. For a production environment, specify your actual frontend domain and restrict the allowed headers and methods as required.

---

## 6.7 Test API Gateway

Now that we have configured the API Gateway, VPC Link, ALB integration, Cognito JWT Authorizer, routes, and CORS, we can test the API endpoints.

### Get the API Gateway Invoke URL

1. Open your API Gateway.
2. Navigate to **Stages → $default**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2e59d734-783e-4220-b94f-5716798a86c7.png align="center")

1. Copy the **Invoke URL**.

It will look similar to the following:

```text
https://xxxxxxxxxx.execute-api.ap-south-1.amazonaws.com
```

### Test the Public Products Endpoint

The products endpoint does not require authentication.

Run the following command from your local workstation:

```bash
curl https://xxxxxxxxxx.execute-api.ap-south-1.amazonaws.com/products
```

**Expected result:** The API should return the list of products from the Product Service.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/5f300fdf-9352-4e2f-afd4-cc59dc95906d.png align="center")

### Test Authenticated Endpoints

Now test the other backend endpoints without providing an authentication token.

**Cart Service:**

```bash
curl https://xxxxxxxxxx.execute-api.ap-south-1.amazonaws.com/cart
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/c9db4aea-df05-4dbe-b067-7f99efbf064c.png align="center")

**User Service:**

```bash
curl https://xxxxxxxxxx.execute-api.ap-south-1.amazonaws.com/users
```

**Order Service:**

```bash
curl https://xxxxxxxxxx.execute-api.ap-south-1.amazonaws.com/orders
```

**Expected result:**

```json
{"message": "Unauthorized"}
```

This is expected because these endpoints are protected by the Cognito JWT Authorizer.

**Note:** The authenticated endpoints will require a valid Cognito JWT token in the Authorization header. We will handle the frontend integration and token-based API requests in the next module.

---

## Troubleshooting

### 1\. CORS Errors

If you encounter `Access-Control-Allow-Origin` errors:

- Verify that CORS is configured in API Gateway.
- Check that `Access-Control-Allow-Headers` includes `*`.
- Verify that the required allowed methods are configured.
- Check that the OPTIONS route is created.

### 2\. 401 Unauthorized

If you receive a `401 Unauthorized` response:

- Verify the Cognito User Pool ID in the JWT Authorizer configuration.
- Check that the Issuer URL is correct.
- Verify that the App Client ID is configured correctly under Audience.
- Ensure that a valid JWT token is included when accessing protected endpoints.

**Note:** A 401 response when testing protected routes without a token is expected behavior.

### 3\. 502 Bad Gateway

If you encounter a `502 Bad Gateway` error:

- Check the VPC Link status.
- Verify the internal ALB integration configuration.
- Ensure the ALB target groups are healthy.
- Verify that the ALB listener rules are configured correctly.

### 4\. 504 Gateway Timeout

If you encounter a `504 Gateway Timeout` error:

- Check the ECS service health.
- Verify that the ALB target groups contain healthy targets.
- Check the ALB listener rules.
- Verify the VPC Link and security group configurations.
- Ensure the VPC Link security group and ALB security group allow the required traffic.

---

## Module Summary

In this module, we successfully configured Amazon API Gateway and connected it to our internal Application Load Balancer using VPC Link.

We completed the following configurations:

1. **VPC Link:** Created a private connection between API Gateway and our VPC.
2. **HTTP API:** Created the `ecommerce-api-http`.
3. **Private Integration:** Connected API Gateway to `ecommerce-internal-alb`.
4. **Cognito JWT Authorizer:** Configured authentication for protected endpoints.
5. **API Routes:** Created public, authenticated, and CORS preflight routes.
6. **CORS:** Configured cross-origin access for frontend communication.
7. **API Testing:** Successfully tested the public products endpoint and verified authentication on protected endpoints.

Our API Gateway is now publicly accessible, while communication between API Gateway and the internal ALB takes place privately through VPC Link.

**In the next module, we will integrate the frontend application with the backend services using the API Gateway endpoint.**

---

# Module 7: Frontend-Backend Integration

## Overview

- In this module, we will integrate the React frontend with the backend services through **Amazon API Gateway**.
- We have already deployed the frontend to **Amazon S3** and configured **CloudFront**. We have also created API Gateway and connected it to the internal Application Load Balancer.
- Now, we need to update the frontend configuration with the **API Gateway URL** so that the application can communicate with the backend services.
- After updating the configuration, we will rebuild the React application, upload the new build to S3, and invalidate the CloudFront cache to make the latest changes available.

## In this module

- Update the frontend configuration with the API Gateway URL.
- Rebuild the React application.
- Upload the updated build to the S3 frontend bucket.
- Invalidate the CloudFront cache.
- Test the fully integrated application.

---

## 7.1 Update Frontend with API Gateway URL

- The frontend needs to know the API Gateway endpoint to communicate with the backend services.
- Since our frontend is hosted on **Amazon S3**, we will configure the API Gateway URL directly in the React application's configuration file.

**Note:** If the frontend were running inside a container, we could retrieve configuration values dynamically from **SSM Parameter Store**. However, because our frontend is hosted on S3, we will update the API URL directly in the configuration file.

**Step 1: Navigate to the frontend directory**

- Open your terminal and navigate to the React application directory.

```bash
cd frontend/react-app
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8a8eb0f2-5e4b-43e0-ade3-74a91f29e3a5.png align="center")

**Step 2: Open the AWS configuration file**

- Open `src/aws-config.js` and locate the `baseUrl` field under the API configuration.

**Step 3: Update the API Gateway URL**

- Copy the **Invoke URL** from:

**API Gateway Console → APIs → ecommerce-api → Stages → $default**

- Update only the `baseUrl` field with your API Gateway URL.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/36e067d3-bb11-4593-bd14-aa72289d8cb4.png align="center")

```javascript
const awsConfig = {
  Auth: {
    Cognito: {
      userPoolId: '<COGNITO_USER_POOL_ID>',
      userPoolClientId: '<COGNITO_CLIENT_ID>',
      loginWith: {
        email: true,
      },
    }
  },
  API: {
    baseUrl: '<API_GATEWAY_URL>'
  }
};

export default awsConfig;
```

**Important:**

- Keep the existing **Cognito User Pool ID** and **App Client ID** configured in Module 3.
- Replace `<API_GATEWAY_URL>` with your actual API Gateway Invoke URL.
- Do not add a trailing slash (`/`) at the end of the API Gateway URL.

---

## 7.2 Rebuild and Redeploy Frontend to S3

- After updating the API Gateway URL, we need to create a new production build of the React application and upload it to the existing S3 frontend bucket.

**Step 1: Create a new production build**

- Run the following command from the React application directory:

```bash
npm run build
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a031d276-2b26-4f1b-867d-c16f3cc80a24.png align="center")

- This command generates the updated production-ready frontend files inside the `build/` directory.

**Step 2: Upload the updated build to S3**

- Run the following AWS CLI command to synchronize the new build files with your existing S3 frontend bucket.

```bash
aws s3 sync build/ s3://<your-frontend-bucket-name> --delete --exclude "images/*"
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/9f7c31f7-5d37-4715-b010-ee79df8a0354.png align="center")

- Replace `<your-frontend-bucket-name>` with your actual frontend S3 bucket name.

**Important:** We are using the same S3 bucket to host the frontend files and static product images. Therefore, the `--exclude "images/*"` option prevents the existing images from being overwritten or deleted during synchronization.

---

## 7.3 Invalidate CloudFront Cache

- After uploading the updated frontend files to S3, we need to invalidate the **CloudFront cache**.

**Why is CloudFront invalidation required?**

- CloudFront caches frontend files at its edge locations to improve application performance. Even after uploading the updated files to S3, users might continue receiving the previously cached files.
- To make the latest frontend changes available, we need to create a cache invalidation.

**Step 1: Open CloudFront**

Navigate to:

**AWS Console → CloudFront → Distributions**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/5c6766d3-82fd-478f-9621-f3d2ffb5aa3e.png align="center")

Select your existing frontend CloudFront distribution.

**Step 2: Open Invalidations**

1. Select the **Invalidations** tab.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a70d6dce-8868-4801-b28f-41658383f257.png align="center")

1. Click **Create invalidation**.

**Step 3: Enter the object path**

Under **Object paths**, enter:

```text
/*
```

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2aff2bba-f1bd-43ee-9523-d97eb55dd01a.png align="center")

- This invalidates all cached objects associated with the distribution.

**Step 4: Create the invalidation**

- Click **Create invalidation** to start the process.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/c765fda9-2c27-4991-8409-7e8d27f9e53d.png align="center")

- Wait until the invalidation status changes to **Completed**.

---

## 7.4 Test the Fully Integrated Application

Now that the frontend has been updated and the CloudFront cache has been invalidated, we can test the complete application.

**Step 1: Open the application**

Navigate to your CloudFront distribution and copy its domain name.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8b75c1fe-3e4a-4cbe-bec6-8a3e1a32f812.png align="center")

Open the application in your browser:

```text
https://<your-cloudfront-domain>
```

**Step 2: Verify product listing**

- Check whether the product listing loads successfully.
- Verify that product details are retrieved from the backend.
- Confirm that product images are displayed correctly.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/dee2327a-97e7-4874-9211-791d64170703.png align="left")

The product information is retrieved from the **DynamoDB products table** through the Product Service API.

**Step 3: Login to the application**

1. Click the login option.
2. Enter the email address and password of your existing Cognito user.
3. Sign in to the application.

Authentication is handled by **Amazon Cognito**.

**Step 4: Test cart functionality**

1. Select a product.

1) Click **Add to Cart**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ae4675c9-c04b-4b2e-9fa5-ab9385ead5d5.png align="center")

1. Navigate to the **Network** tab.
2. Check the request methods, response status codes, and any errors.
3. This helps us identify whether the frontend is communicating with the backend services correctly.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/88c414e5-023a-4a62-b3d4-bb0b819bcb87.png align="center")

1. Add a few more products.
2. Open the cart and verify that the selected items are displayed.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/140f8723-aff0-4dee-8fc9-fb48c9355f10.png align="center")

The Cart Service handles these requests and stores cart information in **DynamoDB**.

**Step 5: Place a test order**

1. Proceed to place an order.
2. Verify that the order is placed successfully.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/9f1e724a-718b-498a-87bd-0dd868c5dc18.png align="center")

1. Open the orders section and verify that the order details are displayed.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/c78cfb99-39b0-423c-8c21-ed41f4a50cd6.png align="center")

The Order Service processes the order, while the User Service manages user profile information in **Amazon RDS PostgreSQL**.

---

### Verify Backend Logs Using CloudWatch

We can also verify backend requests through **Amazon CloudWatch Logs**.

1. Open the **CloudWatch Console**.
2. Navigate to **Logs → Log groups**.
3. Open the `/ecs` log group.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/19e6161b-5de6-4a9e-8904-0504ae76ebe0.png align="center")

1. Select the relevant service log stream, such as the Product Service.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a57f24b5-8dde-4829-9cbb-daa25337f6a1.png align="center")

1. Review the API requests and application logs.

You should be able to observe API requests, CORS `OPTIONS` requests, and load balancer health checks.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2bd45ac5-fd37-406b-96e8-85558b4ed795.png align="center")

If any backend errors occur, they can also be investigated through these logs.

### Verify Database Records

The application uses different AWS database services to store application data.

- **DynamoDB:** Stores product information and shopping cart details.
- **RDS PostgreSQL:** Stores user profiles and order information.

You can connect to your RDS database using the PostgreSQL username and password configured earlier and verify the corresponding user and order records.

---

## Troubleshooting

**Products are not loading:**

- Verify that the CloudFront invalidation has completed.
- Check that the API Gateway URL in `aws-config.js` is correct.
- Ensure that the API Gateway URL does not contain a trailing slash.
- Verify the API Gateway CORS configuration from Module 6.
- Check whether the ECS services are running and healthy.
- Open the browser's **Developer Tools → Network and Console** to identify errors.
- Review the relevant CloudWatch service logs.

**Authentication errors:**

- Verify the Cognito User Pool ID in `aws-config.js`.
- Verify the Cognito App Client ID.
- Check the Cognito JWT Authorizer configuration in API Gateway.

**502 / 504 errors:**

- Check ECS service health in the ECS Console.
- Verify the internal ALB and target group health.
- Review the relevant CloudWatch logs.
- Check the API Gateway integration and VPC Link configuration.

---

## Next Steps

At this stage, the frontend and backend services are integrated, and we can test the main application functionality, including product browsing, authentication, cart operations, and order placement.

However, order notifications and other notification-related integrations are not configured yet.

In the next module, we will configure the notification services to extend the application's functionality.

---

# Module 8: Notifications and Integration (SNS and SQS)

## Overview

In this module, we will configure **Amazon SNS (Simple Notification Service)** and **Amazon SQS (Simple Queue Service)** to implement an event-driven notification workflow for our eCommerce application.

Whenever a customer places an order, the Order Service will publish an event to an SNS topic. SNS will distribute that event to multiple subscribers:

- **Email subscription:** Sends an order notification to a configured email address.
- **SQS subscription:** Stores the order event in a queue for downstream processing, such as integration with a shipping service provider.

This approach allows different systems to receive and process the same event independently.

### Important: Email Notification Approach

- Ideally, we would use **Amazon SES (Simple Email Service)** with an AWS Lambda function subscribed to SNS. Lambda would process the order event and send an email to the customer's email address using SES.
- However, Amazon SES is initially placed in Sandbox mode in many AWS accounts. This restricts email sending to verified email addresses and requires a request to AWS to move the account into production mode.
- To keep this project simple, we will use a direct **SNS Email Subscription** instead.
- With this approach, order notifications will be sent to one configured email address. This is mainly for demonstration and testing purposes, not a complete customer-specific email notification system.

## In this module

- Create an SNS topic for order events.
- Create an SQS queue for order shipping.
- Configure SNS email and SQS subscriptions.
- Verify the SQS queue access policy.
- Store the SNS Topic ARN in SSM Parameter Store.
- Restart the Order Service to load the new parameter.
- Test the notification workflow.

## Architecture

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/9a0a5806-84fa-458f-a388-ff52c3ac6933.png align="center")

### How the Notification Workflow Works

1. A customer places an order through the frontend.
2. The Order Service publishes an order event to the SNS topic.
3. SNS distributes the same event to its subscribers.
4. The Email subscription sends an order notification to the configured email address.
5. The SQS subscription stores the order event in the shipping queue.
6. A downstream process, such as a Lambda function or shipping service, can read and process messages from the SQS queue.

**Note:** In this project, we will test the email notification and SQS message delivery. We will not implement an actual shipping service or an SQS message-processing application.

---

## 8.1 Create SNS Topic

- First, we need to create an SNS topic that will receive order events from the Order Service.
- SNS follows a publish-subscribe model. The Order Service publishes a message to a topic, and SNS distributes that message to all the subscribers configured for that topic.

### SNS Topic Configuration

1. **AWS Console → Simple Notification Service (SNS) → Topics → Create topic**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/65dbe1cc-e484-426e-8005-c74c50e5b4d7.png align="center")

1. **Type:** Select Standard.
2. **Name:** `ecommerce-order-events`
3. **Display name:** `ecommerce-order-events`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/77c6fd45-5115-45f5-b20c-6a197a83c2d0.png align="center")

1. Keep the remaining settings as default.
2. Click **Create topic**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d3210a4d-51c5-4791-ad7e-64dc2910154e.png align="center")

### Copy the Topic ARN

After creating the topic:

1. Open the newly created `ecommerce-order-events` topic.
2. Locate the **ARN (Amazon Resource Name)**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/effeb096-8393-4fbb-ac94-ac37c9702336.png align="center")

1. Copy the Topic ARN and save it temporarily. We will use it in SSM Parameter Store later.

Example:

```text
arn:aws:sns:<your-region>:<account-id>:ecommerce-order-events
```

**Note:** We are using a Standard topic because FIFO ordering is not required for this project.

---

## 8.2 Create SQS Queue for Order Shipping

- Next, we need to create an SQS queue and subscribe it to the SNS topic.
- The purpose of this queue is to store order events so that a downstream system, such as a shipping service provider, can retrieve and process them later.

### SQS Queue Configuration

1. **AWS Console → Simple Queue Service (SQS) → Queues → Create queue**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8695a8db-4ff8-42d3-b1e9-96ff3b159250.png align="center")

1. **Type:** Select Standard.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/9fee2562-9721-4c14-a250-5eff23f4426a.png align="center")

1. **Name:** `ecommerce-order-shipping`
2. Keep the remaining settings as default.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/1ed8c8ea-4275-44f3-9aff-fc407321c064.png align="center")

1. Click **Create queue**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2d6ea7b3-2bd1-45a7-841a-28d8074e0534.png align="center")

### Verify the Queue Access Policy

After creating the queue:

1. Open `ecommerce-order-shipping`.
2. Navigate to the **Access policy** section.
3. Initially, the queue policy allows access to the AWS account owner.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/1b212cdf-9f36-4fde-8d6c-3ee267e8a6e9.png align="center")

- We will verify whether SNS receives permission to send messages to this queue after creating the SNS subscription.

---

## 8.3 Configure SNS Subscriptions

Now that we have created the SNS topic and SQS queue, we need to configure two subscriptions.

1. **Email subscription:** Sends order notifications to our configured email address.
2. **SQS subscription:** Delivers order events to the shipping queue.

### 8.3.1 Create Email Subscription

First, let's subscribe an email address to the SNS topic.

1. **SNS Console → Topics → Select** `ecommerce-order-events`.
2. Navigate to **Subscriptions**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/0b6fe2a9-926a-4586-9641-8e4b6878b6f8.png align="center")

1. Click **Create subscription**.
2. **Topic ARN:** Select `ecommerce-order-events`.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/79676137-9b41-4f98-b526-0893d2a97f99.png align="center")

1. **Protocol:** Select Email.
2. **Endpoint:** Enter your email address.
3. Click **Create subscription**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d3e87700-3a95-459c-8fa6-89d7a1555da6.png align="center")

### Confirm Email Subscription

After creating the subscription, AWS SNS sends a confirmation email to the configured email address.

1. Open your email inbox.
2. Find the subscription confirmation email from AWS.
3. Open the email.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ac9d260e-6f34-4b64-a510-41275547c844.png align="center")

1. Click **Confirm subscription**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/18e161d3-619e-4815-a1be-7f9b44e1b7e8.png align="center")

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/777607b0-9068-478d-bf50-bb134b6eabb6.png align="center")

1. Return to the SNS console.
2. Verify that the subscription status shows **Confirmed**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/fc2eef5b-b226-4f96-9f1f-c19af61d7b2f.png align="center")

**Important:** The email subscription will not become active until you confirm it using the link sent by AWS.

### 8.3.2 Create SQS Subscription for Shipping

- Next, we need to subscribe the SQS queue to the same SNS topic.
- We create an email subscription to receive order notifications whenever a customer places an order. Once the subscription is confirmed, Amazon SNS automatically sends order event notifications to the registered email address.

1. Navigate to **SNS → Topics → ecommerce-order-events → Subscriptions**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2e982685-3a80-459b-8268-3f99e5d4675d.png align="center")

1. Click **Create subscription**.
2. **Topic ARN:** Select `ecommerce-order-events`.
3. **Protocol:** Select Amazon SQS.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d329b5e5-93ad-468d-a8bd-1c9e2f6ebae3.png align="center")

1. **Endpoint:** Select the ARN of the `ecommerce-order-shipping` queue.
2. Keep the remaining settings as default.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b11c8122-244d-4e6d-988f-dd7b1fcfcdd1.png align="center")

1. Click **Create subscription**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/da230936-45d9-4044-b499-af27313ce4f6.png align="center")

### Verify SQS Queue Policy

Once the SQS subscription is created, AWS automatically updates the queue access policy to allow the SNS topic to send messages to the queue.

To verify this:

1. Open **SQS Console → Queues**.
2. Select `ecommerce-order-shipping`.
3. Navigate to the **Access policy** section.
4. Refresh the page.
5. Verify that the policy contains a statement allowing the SNS service to perform the `sqs:SendMessage` action.

The policy should restrict this permission to messages originating from our SNS topic.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8116714b-b2b8-4a3a-8135-026b47e58cb8.png align="center")

**Note:** In our implementation, AWS automatically updates the SQS queue policy when the SNS subscription is created. No manual policy update is required if this permission is already present.

### Subscription Summary

We now have two subscriptions configured for the `ecommerce-order-events` topic:

- **Email:** Sends order notifications to the configured email address.
- **Amazon SQS:** Stores order events in the `ecommerce-order-shipping` queue.

Whenever the Order Service publishes an event to SNS, both subscribers receive the message.

---

## 8.4 Update SSM Parameter Store

- Now we need to provide the SNS Topic ARN to the Order Service.
- The Order Service needs the **SNS Topic ARN** to know where to publish order notifications. We store this **ARN in AWS Systems Manager Parameter Store** so the service can retrieve it from its configuration.
- We will create a new parameter containing the ARN of our SNS topic.

### Create SNS Topic ARN Parameter

1. **AWS Console → Systems Manager → Parameter Store → Create parameter**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/355e5340-29e4-47a2-b63c-edde3fbc3c7d.png align="center")

1. **Name:** `/ecommerce/dev/sns/topic-arn`

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/50248786-d55e-4c32-9c5c-8b8a441fcbc4.png align="center")

1. **Tier:** Standard.
2. **Type:** String.
3. **Data type:** Text.
4. **Value:** Paste the SNS Topic ARN copied in Section 8.1.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/80a9d248-dd05-4b83-87dc-4bb7a3fb9dee.png align="center")

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/70cb5ef7-c184-4fa5-8dc5-997988d4a244.png align="center")

Example:

```text
arn:aws:sns:<your-region>:<account-id>:ecommerce-order-events
```

8. Click **Create parameter**.

**Note:** Make sure that the parameter name matches the name expected by the Order Service configuration.

---

## 8.5 Restart the Order Service to Load the SNS Topic ARN

After creating the SNS Topic ARN parameter, we need to restart the Order Service.

**Why is restarting the Order Service required?**

The Order Service loads its configuration parameters from SSM Parameter Store when the application starts.

Since we created the SNS Topic ARN parameter after the service was already running, the existing task may not have loaded this new parameter.

To ensure that the Order Service retrieves the latest parameter, we need to trigger a new ECS deployment.

### Force a New Deployment

1. **ECS Console → Clusters → ecommerce-cluster**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f5e5e374-a052-4bbe-bcc0-d42123b940c2.png align="center")

1. Navigate to **Services**.
2. Select the Order Service.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e8dd83c5-4760-4762-8126-861698b10168.png align="center")

1. Click **Update**.
2. Locate the deployment options.
3. Enable **Force new deployment**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/0d5f5cb2-fba2-4664-a02e-3e099b02c5e1.png align="center")

1. Keep the remaining settings unchanged.
2. Click **Update**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e4e393f4-8425-4a77-8312-b8bd40be9a51.png align="center")

### Verify Order Service Status

1. Open the Order Service in the ECS console.
2. Navigate to the **Tasks** section.
3. Wait until the new task reaches the **Running** state.
4. Verify that the service has one running task and the previous task has been replaced.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b6646ecf-f8d3-46ac-bf55-4ea1f199f440.png align="center")

**Note:** The new deployment allows the Order Service to load the SNS Topic ARN from SSM Parameter Store during startup. Make sure the task role has permission to read the required SSM parameter and publish to the SNS topic.

---

## 8.6 Test the Notification Workflow

Now that all the components are configured and the Order Service has been updated, let's test the complete notification workflow.

## Step 1: Place an Order

1. Open the eCommerce application using the CloudFront URL.
2. Log in to your account.
3. Add products to the cart.
4. Click Place Order.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/925b102e-a6f8-4a19-b3e6-6c25b82e777d.png align="center")

1. Verify that the order is placed successfully.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/5f174eba-b56e-4e86-88a8-71f148207969.png align="center")

## Step 2: Verify Email Notification

1. Open the email inbox configured in the SNS subscription.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/79eb116a-830d-4894-b0b0-955ed02b0610.png align="center")

1. Refresh the inbox and check for the order notification.
2. If the email is not visible, check the Spam folder. The email should contain the order details, such as Order ID, customer email address, total amount, and ordered products.

## Step 3: Verify SQS Message

1. **Open SQS Console → Queues → ecommerce-order-shipping.**

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/bfe29851-85e1-459d-bee1-d8d79d25fb41.png align="center")

1. Select **Send and receive messages**.
2. Click **Poll for messages**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/9a4302f2-cba4-4174-9510-2cac360a9332.png align="center")

1. Open the received message and verify the order details.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/1349be9d-13da-4554-aad3-c262aeedf0b8.png align="center")

- Select the message ID to view its details.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/626e98f8-7ac0-4c88-8793-be67740c7151.png align="center")

**Note:** Since no message-processing application is configured for this queue, messages will remain in SQS until a consumer processes and deletes them or they expire according to the queue's retention settings.

This confirms that the SNS topic successfully distributes order events to both the configured email and SQS subscribers.

---

## Troubleshooting

### 1\. Email Notification Not Received

If you do not receive an email notification:

- Check the Spam folder.
- Verify that the SNS email subscription status is **Confirmed**.
- Check whether the correct email address is configured.
- Review the Order Service logs in CloudWatch.
- Verify that the Order Service has successfully published the SNS event.

### 2\. SQS Queue Not Receiving Messages

If no messages appear in the SQS queue:

- Verify that the SQS subscription is created successfully.
- Check the SNS subscription status.
- Verify that the SQS queue policy allows the SNS topic to perform `sqs:SendMessage`.
- Check the Order Service logs in CloudWatch.

### 3\. SNS Event Publishing Error

If the Order Service reports an error while publishing an SNS event:

1. Open **CloudWatch Console → Logs → Log groups**.
2. Open the Order Service log group under `/ecs`.
3. Review the latest log stream.
4. Look for errors related to SNS event publishing or parameter retrieval.
5. Verify that `/ecommerce/dev/sns/topic-arn` exists in Parameter Store.
6. Verify that the Order Service task role has the required SSM and SNS permissions.
7. If the parameter was created after the service started, perform a **Force new deployment** of the Order Service.

**Important:** In this project, the Order Service loads the SNS Topic ARN during startup. Restarting the service after creating the parameter is an important step.

---

## Module Summary

In this module, we successfully configured an event-driven notification workflow using Amazon SNS and SQS.

We completed the following steps:

1. **SNS Topic:** Created `ecommerce-order-events` to publish order events.
2. **SQS Queue:** Created `ecommerce-order-shipping` to store order messages.
3. **Email Subscription:** Configured an email subscriber and confirmed the subscription.
4. **SQS Subscription:** Subscribed the shipping queue to the SNS topic.
5. **Queue Policy:** Verified that SNS has permission to send messages to SQS.
6. **SSM Parameter Store:** Stored the SNS Topic ARN for the Order Service.
7. **ECS Deployment:** Restarted the Order Service to load the newly created parameter.
8. **End-to-End Testing:** Placed orders and verified email notifications and SQS messages.

- Our notification workflow is now working. Whenever a customer places an order, the Order Service publishes an event to SNS, and SNS distributes that event to the configured subscribers.
- This also gives us a foundation for future integrations with shipping partners and other third-party services.

## Next Steps

So far, we have accessed our application using the CloudFront distribution URL.

In the next module, we will configure a **custom domain name and SSL certificate** so that users can access the eCommerce application using our own domain name over HTTPS.

---

# Module 9: Custom Domain & SSL Configuration on AWS

## Overview

- In this module, we will configure a custom domain name for our eCommerce application and enable secure HTTPS access using an SSL/TLS certificate.
- Until now, we have been accessing the application using the CloudFront-provided domain name. Now, we will configure our own domain name so users can access the application through a familiar URL.

For example:

- **CloudFront URL:** `https://dxxxxxxxxxxxxx.cloudfront.net`
- **Custom domain:** `https://yourdomain.com`

We will use **Amazon Route 53**, **AWS Certificate Manager (ACM)**, and **Amazon CloudFront** to complete this configuration.

## Architecture

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/d1f7e188-353c-4c06-9156-0e49814d20ae.png align="center")

## Prerequisites

Before starting this module, make sure you have:

- A registered public domain name (purchased through Hostinger, GoDaddy, Route 53, or another domain registrar).
- An existing CloudFront distribution hosting the frontend application.

**Note:** If you do not have a public domain name, you can skip this module and continue using the CloudFront domain name.

## In This Module

We will complete the following tasks:

1. Create a Route 53 Public Hosted Zone.
2. Update the domain nameservers at the domain registrar.
3. Request a public SSL certificate using AWS Certificate Manager.
4. Validate domain ownership using DNS validation.
5. Add alternate domain names to the CloudFront distribution.
6. Configure the SSL certificate in CloudFront.
7. Create Route 53 records pointing to CloudFront.
8. Test application access using the custom domain.

---

## 9.1 Create a Route 53 Public Hosted Zone

- First, we need to create a Public Hosted Zone in Amazon Route 53 for our registered domain name.
- A Public Hosted Zone contains the DNS records required to route internet traffic for our domain and its subdomains.

### Step 1: Open Route 53

1. Open the **AWS Management Console**.
2. Search for **Route 53**.
3. Select **Hosted zones**.
4. Click **Create hosted zone**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/97da087f-b8dc-48da-97a2-f52469865f7d.png align="center")

### Step 2: Configure the Hosted Zone

Enter the following details:

- **Domain name:** Enter your registered domain name (e.g., `niranjanreddy.shop`).

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ceadbfde-0c5d-4567-b1a9-f4e086c25bd4.png align="center")

- **Description:** Enter your domain name (optional).
- **Type:** Select **Public hosted zone**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2a0a57e1-9b2b-4574-8529-866abde313dc.png align="center")

- Click **Create hosted zone**.

### Step 3: Note the Route 53 Name Servers

After creating the hosted zone, Route 53 automatically creates two DNS records:

- **NS (Name Server):** Contains the four AWS name servers assigned to your hosted zone.
- **SOA (Start of Authority):** Contains the DNS administrative information for the hosted zone.

Open the **NS record** and note all four name servers. We will use these values in the next step.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/3ed4d46d-e354-4d97-ab10-a2213863ce55.png align="center")

### Step 4: Update Name Servers in Hostinger

Since our domain is registered with Hostinger, we need to update its name server configuration to point to Route 53.

1. Log in to your **Hostinger account**.
2. Navigate to **Domains → Domain portfolio**.
3. Select your domain.
4. Open **DNS / Nameservers**.
5. Under **Select Nameservers**, choose **Change nameservers**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/1fd6fc7d-e85b-402b-9113-19c4d92604dc.png align="center")

1. Replace the existing nameservers with the four name servers copied from Route 53.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e6504f82-f3ae-4e3e-8877-f7277945a8ac.png align="center")

1. Click **Save**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8b81be1b-6c17-4736-87bf-ba25ce267384.png align="center")

**Important:** Make sure you copy all four name servers correctly from the Route 53 NS record.

- Once the nameserver changes take effect, Route 53 becomes the DNS provider for your domain. DNS propagation may take some time.
- Now our Route 53 Public Hosted Zone is ready, and we can proceed with requesting an SSL certificate.

---

## 9.2 Request an SSL Certificate Using AWS Certificate Manager

- Now we need an SSL/TLS certificate for our custom domain.
- When users access the application using HTTPS, CloudFront uses this certificate to establish a secure connection with their browsers.
- We will request the certificate using **AWS Certificate Manager (ACM)**.

**Important:** CloudFront requires the ACM certificate to be created in the **US East (N. Virginia) –** `us-east-1` region, even if your application and other AWS resources are hosted in another region.

### Step 1: Open AWS Certificate Manager

1. Open the AWS Management Console.

1) Search for **Certificate Manager (ACM)**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/3668170a-a758-4456-8ada-b1a19a8981f8.png align="center")

1. Switch the AWS region to **US East (N. Virginia) –** `us-east-1`.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/107c8efb-5336-458b-935a-5f28627b1a4a.png align="center")

1. Open Certificate Manager.

### Step 2: Request a Public Certificate

1. Click **Request**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/fbac1239-d46b-4b37-8c94-f55c62f1da03.png align="center")

1. Select **Request a public certificate**.

1) Click **Next**.

### Step 3: Add Domain Names

Enter your domain names.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/fe111c7f-dc41-4078-96cd-1c4f6231c697.png align="center")

For example:

- `yourdomain.com`
- `www.yourdomain.com`

* `*.yourdomain.com` (optional, to cover subdomains such as `api.yourdomain.com` or `app.yourdomain.com`).

Using a wildcard certificate is useful if you plan to use additional subdomains in the future.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/87531fd7-4b29-4e7d-84c1-fe11fe92735f.png align="center")

### Step 4: Select DNS Validation

1. Select **DNS validation** as the validation method.
2. Keep the remaining settings as default.

1) Click **Request**.

ACM will create the certificate request and display its validation status as **Pending validation**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/9962e6d3-cf13-4555-9f1b-08daa0418a5a.png align="center")

### Step 5: Validate Domain Ownership

Since we selected DNS validation, ACM needs to verify that we own the domain.

1. Open the requested certificate.
2. Locate the domain validation section.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/e9e0b5b8-e058-4ba0-be35-3060521dadac.png align="center")

1. Click **Create records in Route 53**.
2. Confirm the creation of the DNS validation records.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/b17b979c-fb87-471e-a52d-a64a20593097.png align="center")

- After creating the records, open R**oute 53 → Hosted zones → Your domain.** You can see the newly created CNAME validation records in the hosted zone. These records are used by ACM to verify domain ownership.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8dc5a89d-c74e-43ee-9388-02b34fab70fd.png align="center")

- Wait for the validation process to complete. It may take a few minutes.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/cdc2a6e3-0bd0-41ed-a2a8-43adb7f077b3.png align="center")

- Once validation is successful, the certificate status changes to **Issued**.

---

## 9.3 Add Alternate Domain Names to CloudFront Distribution

- Now that our SSL certificate has been issued, we need to tell CloudFront which custom domain names we want to use for our application.
- Currently, CloudFront is accessed using its default domain name. By adding our custom domain names, we allow CloudFront to serve the application when users access URLs such as [`https://yourdomain.com`](https://yourdomain.com) or [`https://www.yourdomain.com`](https://www.yourdomain.com).
- In simple terms, we are connecting our custom domain names to the CloudFront distribution and attaching the SSL certificate so CloudFront can serve our application securely over HTTPS.

### Step 1: Open CloudFront Distribution

1. Open the **CloudFront Console**.
2. Select **Distributions**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/16c3908c-c249-46f3-bc84-86003aa0ec0f.png align="center")

1. Open the distribution currently hosting your frontend application.
2. Navigate to the **General** tab.
3. Click **Edit**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/2871288f-51c3-4f54-9f2b-58e57fe90d4c.png align="center")

### Step 2: Configure Alternate Domain Names

Under **Alternate domain names (CNAMEs)**, add item:

- `yourdomain.com`
- `www.yourdomain.com`

These entries tell CloudFront that users can access this distribution using the configured custom domain names.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/18526e6e-b4e4-493d-a24e-ba3d4a25263b.png align="center")

### Step 3: Select the Custom SSL Certificate

1. Locate the **Custom SSL certificate** setting.
2. Select the ACM certificate that we created in the `us-east-1` region.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/f54af5b5-5169-4c77-ac1c-27a7d67aad1e.png align="center")

1. Keep the remaining settings unchanged.
2. Click **Save changes**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/78c70d4f-2285-4d36-b455-faca72e989e1.png align="center")

- CloudFront will start deploying the updated configuration.
- Wait until the distribution deployment is complete before proceeding to the next step.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/0ab9b492-521b-49f8-9d69-4eff7c847e83.png align="center")

---

## 9.4 Create Route 53 Records

Now we need to connect our custom domain name to the CloudFront distribution.

For this, we will create two DNS records in the Route 53 Public Hosted Zone:

- **Root domain:** `yourdomain.com`
- **Subdomain:** `www.yourdomain.com`

Both records will point to the same CloudFront distribution using Alias records.

### Step 1: Create an A Record for the Root Domain

1. Open **Route 53 → Hosted zones**.
2. Select your domain's Public Hosted Zone.
3. Click **Create record**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/5d50b46c-ec5a-4960-a931-1a6c9f6bf728.png align="center")

1. Configure the following settings:
   - **Record name:** Leave empty.
   - **Record type:** A
   - **Alias:** Enable.
   - **Route traffic to:** Alias to CloudFront distribution.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/a3983348-ec59-4f9a-9146-91271792a725.png align="center")

- **Choose distribution:** Select your CloudFront distribution.
- **Routing policy:** Simple routing.Click **Create records**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/ee2de0fe-11fa-4e3e-a2b8-68e3b043b013.png align="center")

### Step 2: Create an A Record for the www Subdomain

Create another record with the following settings:

- **Record name:** `www`
- **Record type:** A
- **Alias:** Enable.
- **Route traffic to:** Alias to CloudFront distribution.
- **Choose distribution:** Select the same CloudFront distribution.
- **Routing policy:** Simple routing.

Click **Create records**.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/1f06e0eb-1bcc-4a5f-813d-3bdf7214e781.png align="center")

After creating both records, Route 53 will route requests for the root domain and www subdomain to the CloudFront distribution.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/5c9b07c0-e200-443d-8c53-b1fa8bffc774.png align="center")

## 9.6 Test the Application Using the Custom Domain

Now all the configurations are complete. Let's verify that our eCommerce application is accessible through the custom domain.

### Step 1: Open the Application

Open your browser and enter your custom domain:

`https://yourdomain.com`

Verify that the eCommerce application loads successfully.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/183de35c-b4c1-4ff6-a068-6f4d292ae6c9.png align="center")

### Step 2: Verify the SSL Certificate

1. Click the browser's site information or connection icon near the address bar.
2. Verify that the connection is secure.
3. Open the certificate details and confirm that the certificate is valid and issued by Amazon.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/16211762-9f2a-4777-8676-a943f6c2de39.png align="center")

### Step 3: Test Application Functionality

Verify that the existing application features are working:

- Browse products and verify that product images load.
- Sign in using your Cognito account.
- Add products to the cart.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/86dbf5ff-9dc9-4fd8-9385-136d405c780e.png align="center")

- Place a test order.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/0e1e2c1a-44ab-4d9b-b631-5ccebd0cd109.png align="center")

- Verify that the order is placed successfully.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/8f695233-d6b0-4de4-84cf-f8af1dcdc552.png align="center")

- Check your email for the order notification.

![](https://cdn.hashnode.com/uploads/covers/671f22b50bf91d9e407ce870/63faa92b-776e-4bc7-a15f-35c2ea2575d7.png align="center")

This confirms that the application is working end to end using the custom domain name.

---

## Conclusion

Congratulations! We have successfully configured a custom domain name and enabled HTTPS for our eCommerce application using AWS.

We have used:

- **Amazon Route 53** for DNS management.
- **AWS Certificate Manager (ACM)** to request and validate the SSL certificate.
- **Amazon CloudFront** to configure the custom domain and HTTPS access.

Our eCommerce application is now accessible through our own domain name instead of the default CloudFront URL.

## Next Steps

In the next module, we will discuss the final project enhancements and cleanup activities, including how to remove the AWS resources created during this course to avoid unnecessary ongoing costs.

Continue to **Module 10: Cleanup**.

---

# Module 10: AWS Resource Cleanup

## 1\. Overview

In this final module, we will clean up all AWS resources created during our full-stack e-commerce application deployment.

The purpose of this cleanup is to avoid unnecessary AWS charges and remove resources that are no longer required.

We will follow the correct deletion order to avoid dependency issues between AWS services.

**Important:** Before starting the cleanup, save any application data, configuration details, or resources you may need later. Deleting these resources may permanently remove application data.

---

## 2\. Cleanup Order

Follow the steps below in sequence to remove all project-related AWS resources.

### Step 1: Custom Domain and SSL Configuration (Module 9)

First, remove the custom domain configuration.

**Amazon Route 53**

- Delete the application-specific A (Alias) records pointing to CloudFront.
- Delete the CNAME records created for the application, if any.
- Delete other application-specific DNS records.
- Delete the Public Hosted Zone after removing the custom records.

**AWS Certificate Manager (ACM)**

- Open ACM in the `us-east-1` region.
- Remove the custom domain and SSL certificate association from the CloudFront distribution.
- Wait until the CloudFront configuration update is deployed.
- Delete the SSL/TLS certificate.

**Note:** If your domain registrar uses Route 53 nameservers, update the registrar's nameserver settings before deleting the hosted zone. This prevents your domain from losing its DNS configuration. Do not delete the domain registration itself unless you intend to.

### Step 2: Notification Services (Module 8)

Remove the notification resources created for order processing.

**Amazon SNS**

- Delete the email subscription.
- Delete the SQS subscription.
- Delete the `ecommerce-order-events` SNS topic.

**Amazon SQS**

- Delete the `ecommerce-order-shipping` queue.

**Systems Manager Parameter Store**

- Delete the SNS Topic ARN parameter:
  - `/ecommerce/dev/sns/topic-arn`

**Note:** This project uses direct SNS email subscriptions. Lambda and SES are not required for this cleanup unless you created them separately.

### Step 3: API Gateway and VPC Link (Module 6)

Remove the API Gateway resources and private integration.

**Amazon API Gateway**

- Delete the HTTP API (`ecommerce-api-http`).
- Its associated routes, integrations, JWT authorizer, and stages are removed with the API.

**VPC Link**

- Delete the VPC Link created for the internal ALB integration.
- Wait until the VPC Link deletion is complete.

The VPC Link security group will be removed later during networking cleanup.

### Step 4: Backend Infrastructure (Module 5)

Remove the backend services, load balancer, container repositories, and related resources.

**Amazon ECS**

- Delete the Product Service.
- Delete the Cart Service.
- Delete the User Service.
- Delete the Order Service.
- Delete the ECS cluster after all services have been deleted.
- Deregister the four ECS task definitions after their services are removed.

**Application Load Balancer (ALB)**

- Delete the listener rules.
- Delete the HTTP listener.
- Delete all four target groups.
- Delete the internal Application Load Balancer.

**Amazon ECR**

- Delete the Docker images stored in the repositories.
- Delete the following repositories:
  - `ecommerce/product-service`
  - `ecommerce/cart-service`
  - `ecommerce/user-service`
  - `ecommerce/order-service`

**AWS IAM**

- Delete the `ecommerce-ecs-task-role`.
- Delete the ECS task execution role if it was created specifically for this project and is no longer in use.

**Amazon CloudWatch**

- Delete the ECS log groups created for the backend services, if they are no longer required.

**Temporary EC2 Instance**

- Terminate the temporary EC2 instance if you created one for testing internal ALB connectivity.

### Step 5: Database and Configuration Resources (Module 4)

Remove the databases and application configuration parameters.

**Amazon RDS**

- Delete the PostgreSQL database instance.
- Delete the custom DB subnet group created for this project after the database is deleted.

**Amazon DynamoDB**

- Delete the `ecommerce-products` table.
- Delete the `ecommerce-cart` table.

**AWS Systems Manager Parameter Store**

Delete the following application parameters:

- `/ecommerce/dev/aws/region`
- `/ecommerce/dev/db/host`
- `/ecommerce/dev/db/password`
- Internal ALB service URL parameter created in Module 5.

Also delete any other application-specific parameters created during the project.

### Step 6: Frontend Hosting Resources (Module 3)

Remove the frontend hosting and content delivery resources.

**Amazon CloudFront**

- Disable the CloudFront distribution.
- Wait until its status changes to Disabled.
- Delete the CloudFront distribution.
- Delete the Origin Access Control (OAC) after it is no longer associated with the distribution.

**Amazon S3**

- Empty the frontend S3 bucket.
- Delete the React frontend build files.
- Delete the product images stored under `images/products/`.
- Delete all remaining objects and object versions, if bucket versioning is enabled.
- Delete the S3 bucket.

### Step 7: User Authentication (Module 2)

Remove the authentication resources.

**Amazon Cognito**

- Delete the Cognito User Pool created for the application.

The associated App Client and user accounts will also be removed when the User Pool is deleted.

### Step 8: Networking Infrastructure (Module 1)

Finally, remove the networking resources after all dependent services have been deleted.

**1\. NAT Gateway and Elastic IP**

- Delete the NAT Gateway.
- Wait until the NAT Gateway deletion is complete.
- Release the Elastic IP address associated with the NAT Gateway.

**2\. Internet Gateway**

- Detach the Internet Gateway from `ecommerce-vpc`.
- Delete the Internet Gateway.

**3\. Security Groups**

- Delete the project-specific security groups:
  - `ecommerce-alb-sg`
  - `ecommerce-ecs-sg`
  - VPC Link security group
  - RDS security group, if created specifically for this project.

Make sure no remaining network interfaces or resources are using these security groups.

**4\. Custom Route Tables**

- Remove the custom subnet associations.
- Delete the three custom route tables created for the project.
- Keep the main route table; it is removed along with the VPC.

**5\. Subnets**

Delete all six subnets created for the project:

- `ecommerce-public-subnet-1`
- `ecommerce-public-subnet-2`
- `ecommerce-private-ecs-1`
- `ecommerce-private-ecs-2`
- `ecommerce-private-database-1`
- `ecommerce-private-database-2`

**6\. Amazon VPC**

- Open the Amazon VPC Console.
- Select `ecommerce-vpc`.
- Delete the VPC after all dependent resources have been removed.

### Step 9: Final Verification

After completing the cleanup, review the AWS Management Console and verify that the project resources have been removed.

Check the following services:

- Amazon Route 53
- AWS Certificate Manager
- Amazon SNS and SQS
- Amazon API Gateway
- Amazon ECS and ECR
- Elastic Load Balancing
- AWS IAM
- Amazon RDS
- Amazon DynamoDB
- AWS Systems Manager Parameter Store
- Amazon CloudFront
- Amazon S3
- Amazon Cognito
- Amazon VPC
- Amazon CloudWatch

Also check AWS Billing and Cost Management for any remaining billable resources.

---

## Project Conclusion

In this project, we successfully designed and deployed a **full-stack e-commerce application** using **AWS services**. We configured the AWS networking infrastructure, including **VPC, public and private subnets, Internet Gateway, NAT Gateway, and route tables**.

We deployed the **React frontend** using **Amazon S3 and CloudFront**, and implemented user authentication using **Amazon Cognito**. The backend microservices (**Product, Cart, User, and Order**) were containerized using **Docker** and deployed on **Amazon ECS Fargate**, with **Amazon ECR** for image management and an **internal Application Load Balancer** for service routing.

We also integrated **Amazon API Gateway, RDS PostgreSQL, DynamoDB, Systems Manager Parameter Store, SNS, and SQS** to support API communication, data storage, configuration management, and order notifications. Finally, we configured **Route 53 and AWS Certificate Manager** for custom domain access and **HTTPS**.

Overall, this project provided practical, end-to-end experience in building and deploying a **scalable and secure microservices-based application on AWS**, covering **infrastructure setup, containerization, application deployment, service integration, and resource management**.
