commit cf79af8f65de705fac2fa36388ecb4b73ec2944d Author: ADITYANAIR01 Date: Fri Mar 20 12:01:02 2026 +0530 Code from github to self hosted with git tea in AWS diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3d1d668 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Local environment files +.env +*.env +aws-cloud-drive/CODE/.env +.DS_Store + +# Python artifacts +__pycache__/ +*.pyc +.venv/ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/ALB-README.md b/PRACTICE/APPLICATION-LOAD-BALANCER/ALB-README.md new file mode 100644 index 0000000..0eaa463 --- /dev/null +++ b/PRACTICE/APPLICATION-LOAD-BALANCER/ALB-README.md @@ -0,0 +1,279 @@ +# Application Load Balancer (ALB) + +## What is an Application Load Balancer? + +An **Application Load Balancer (ALB)** operates at **OSI Layer 7** (the application layer), meaning it understands HTTP and HTTPS traffic and can make routing decisions based on the content of each request. When a client sends a request to the ALB's DNS endpoint, the ALB inspects the request and forwards it to one of the healthy registered targets in its **target group**, distributing load across multiple backend instances. This means you expose a single DNS name to the internet, and the ALB handles spreading requests across your backend — no single instance bears all the traffic. ALB also supports advanced routing rules (path-based, host-based), sticky sessions, and continuous health checks that automatically remove failed instances from rotation. + +--- + +## Lab Overview + +| Resource | Name | +|---|---| +| Load Balancer | `demo-ALB` | +| Target Group | `demo-TG-ALB` | +| Security Group (ALB) | `demo-SG-ALB` | +| EC2 Instance 1 | `Demo-EC2-ALB-1` | +| EC2 Instance 2 | `Demo-EC2-ALB-2` | +| Region | `ap-south-1` (Mumbai) | +| Protocol | HTTP port `80` | +| Target type | EC2 instances | + +--- + +## Video Demo + +[![Application Load Balancer – Full Demo](https://img.youtube.com/vi/JrgvgSGxSdw/0.jpg)](https://youtu.be/JrgvgSGxSdw) + +--- + +## Step-by-Step Walkthrough + +### Step 1 — Open the AWS Console + +Log in to AWS and navigate to the Console Home. From the recently visited services or the search bar, click **EC2** to start launching instances. + +![AWS Console Home – click EC2](./images/application-load-balancer-01.png) + +--- + +### Step 2 — Launch EC2 Instances + +#### 2a — Set instance name and choose OS + +In the EC2 dashboard click **Launch instances**. Enter a name for the instance and select **Amazon Linux 2023** as the AMI. + +![EC2 launch wizard – name and OS](./images/application-load-balancer-02.png) + +#### 2b — Configure security group and allow HTTP + +Under **Network settings**, select an existing security group or create a new one. Make sure **Allow HTTP traffic from the internet** is checked so the web server is reachable on port 80. + +![Security group selection – allow HTTP](./images/application-load-balancer-03.png) + +#### 2c — Add the user data script + +Expand **Advanced details** and scroll down to the **User data** field. Paste the following script: + +```bash +#!/bin/bash +# Use this for your user data (script from top to bottom) +# install httpd (Linux 2 version) +yum update -y +yum install -y httpd +systemctl start httpd +systemctl enable httpd +echo "

Hello World from $(hostname -f)

" > /var/www/html/index.html +``` + +**What this script does:** + +| Command | Purpose | +|---|---| +| `yum update -y` | Updates all installed packages to the latest versions | +| `yum install -y httpd` | Installs Apache HTTP Server | +| `systemctl start httpd` | Starts the web server immediately after boot | +| `systemctl enable httpd` | Ensures the web server starts automatically on every reboot | +| `echo ... > /var/www/html/index.html` | Creates a simple HTML page displaying the instance's private hostname — this is how you can tell which backend instance responded when testing the ALB | + +Set **Number of instances** to `2`, then click **Launch instance**. + +![User data script entered – launching 2 instances](./images/application-load-balancer-04.png) + +#### 2d — Rename both instances + +Once both instances are running, rename them to `Demo-EC2-ALB-1` and `Demo-EC2-ALB-2` by clicking the pencil icon next to each name. + +![Both EC2 instances renamed](./images/application-load-balancer-05.png) + +--- + +### Step 3 — Navigate to Load Balancers + +In the EC2 left sidebar, scroll down to the **Load Balancing** section and click **Load Balancers**. Then click **Create load balancer**. + +![EC2 instances running](./images/application-load-balancer-06.png) + +![Left panel – navigate to Load Balancers](./images/application-load-balancer-07.png) + +#### 3a — Choose Application Load Balancer + +On the **Compare and select load balancer type** page, click **Create** under **Application Load Balancer**. + +![Load balancer type selection – choose ALB](./images/application-load-balancer-08.png) + +--- + +### Step 4 — Configure the ALB + +#### 4a — Basic configuration + +Enter the following settings: +- **Load balancer name:** `demo-ALB` +- **Scheme:** Internet-facing +- **IP address type:** IPv4 +- **VPC:** Select your default VPC (or your custom VPC) + +![ALB basic config – name, internet-facing, IPv4, VPC](./images/application-load-balancer-09.png) + +#### 4b — Select all availability zones + +Under **Mappings**, select all available availability zones. This ensures the ALB can route to instances in any AZ and improves fault tolerance. + +![All availability zones selected](./images/application-load-balancer-10.png) + +--- + +### Step 5 — Create and Assign a Security Group + +#### 5a — Create a new security group + +Click **Create new security group** next to the security group selector. This opens the Security Groups console in a new tab. + +![Create new security group form](./images/application-load-balancer-11.png) + +#### 5b — Fill in security group details + +Enter: +- **Security group name:** `demo-SG-ALB` +- **Description:** Security group for Application Load Balancer +- **VPC:** Select the same VPC chosen for the ALB + +![SG name, description, and VPC](./images/application-load-balancer-12.png) + +![Security group inbound rules section](./images/application-load-balancer-13.png) + +#### 5c — Add inbound rule + +Under **Inbound rules**, click **Add rule**. Set: +- **Type:** HTTP +- **Source:** `0.0.0.0/0` (allows all internet traffic on port 80) + +Then click **Create security group**. + +![Inbound rule – HTTP from 0.0.0.0/0](./images/application-load-balancer-14.png) + +![Security group created successfully](./images/application-load-balancer-15.png) + +#### 5d — Select the new security group in the ALB config + +Go back to the ALB creation tab, refresh the security group list, and select `demo-SG-ALB`. Scroll down to continue. + +![New SG selected in ALB config](./images/application-load-balancer-16.png) + +--- + +### Step 6 — Configure Listener and Target Group + +#### 6a — Set default listener action + +Under **Listeners and routing**, the default listener is HTTP port 80. Set the **Default action** to **Forward to** a target group. Click **Create target group** to open the target group wizard. + +![Listener default action – forward to target group](./images/application-load-balancer-17.png) + +#### 6b — Configure target group basics + +In the target group wizard, set: +- **Target type:** Instances +- **Target group name:** `demo-TG-ALB` +- **Protocol:** HTTP +- **Port:** 80 +- **IP address type:** IPv4 + +![Target group config – instances, HTTP 80, IPv4](./images/application-load-balancer-18.png) + +![Target group health check settings](./images/application-load-balancer-19.png) + +Scroll down and click **Next**. + +![Scroll down and click Next](./images/application-load-balancer-20.png) + +#### 6c — Register targets + +Select both EC2 instances (`Demo-EC2-ALB-1` and `Demo-EC2-ALB-2`) from the available instances list, then click **Include as pending below**. + +![Select all instances and include as pending](./images/application-load-balancer-21.png) + +Click **Next** to proceed to the review step. + +![Click Next – proceed to review](./images/application-load-balancer-22.png) + +Click **Create target group** to finalize. + +![Create target group](./images/application-load-balancer-23.png) + +![Target group created confirmation](./images/application-load-balancer-24.png) + +![Target group detail view](./images/application-load-balancer-25.png) + +#### 6d — Select the target group in the ALB config + +Return to the ALB creation tab and select `demo-TG-ALB` as the target group for the listener's default action. + +![Select newly created target group](./images/application-load-balancer-26.png) + +Scroll down and click **Create load balancer**. + +![Click Create load balancer](./images/application-load-balancer-27.png) + +--- + +### Step 7 — ALB Created Successfully + +The ALB is now created. You will see a success banner and the ALB will appear in the load balancers list with state **Active** once it finishes provisioning. + +![ALB created – success banner](./images/application-load-balancer-28.png) + +![ALB detail view – Active state](./images/application-load-balancer-29.png) + +--- + +### Step 8 — Test the ALB + +#### 8a — Copy the DNS name + +In the **Load Balancers** list, select `demo-ALB` and copy the **DNS name** from the details panel at the bottom. + +![Load balancer list – locate DNS name](./images/application-load-balancer-30.png) + +![DNS name copied](./images/application-load-balancer-31.png) + +#### 8b — Open in a browser + +Paste the DNS name into your browser's address bar and press Enter. You should see a page displaying one of the instance hostnames. + +![Browser – Hello World from instance 1](./images/application-load-balancer-32.png) + +#### 8c — Refresh to confirm load distribution + +Refresh the page. The ALB routes the second request to the other backend instance, and a different hostname appears — confirming the load balancer is distributing traffic across both EC2 instances. + +![Browser after refresh – Hello World from instance 2](./images/application-load-balancer-33.png) + +> **ALB in action:** The same DNS endpoint served two different responses from two different backend instances. This is exactly what ALB is designed to do — distribute incoming HTTP requests across all healthy registered targets, preventing any single instance from becoming a bottleneck. + +--- + +### Step 9 — Cleanup + +#### 9a — Delete the ALB + +In the **Load Balancers** console, select `demo-ALB`, click **Actions > Delete load balancer**, type `confirm` in the confirmation field, and click **Delete**. + +![Delete ALB – Actions menu, type confirm](./images/application-load-balancer-34.png) + +![ALB deleted successfully](./images/application-load-balancer-35.png) + +#### 9b — Delete the security group + +Navigate to **EC2 > Security Groups**, select `demo-SG-ALB`, and click **Actions > Delete security groups**. + +![Delete security group](./images/application-load-balancer-36.png) + +#### 9c — Terminate EC2 instances + +Navigate to **EC2 > Instances**, select both `Demo-EC2-ALB-1` and `Demo-EC2-ALB-2`, click **Instance state > Terminate instance**, and confirm. + +![Terminate EC2 instances](./images/application-load-balancer-37.png) + +All resources used in this lab have been deleted. diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-01.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-01.png new file mode 100644 index 0000000..9e3b054 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-01.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-02.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-02.png new file mode 100644 index 0000000..e1b2803 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-02.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-03.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-03.png new file mode 100644 index 0000000..d882037 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-03.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-04.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-04.png new file mode 100644 index 0000000..37caf53 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-04.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-05.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-05.png new file mode 100644 index 0000000..c18b90f Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-05.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-06.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-06.png new file mode 100644 index 0000000..16fdb08 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-06.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-07.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-07.png new file mode 100644 index 0000000..ee53fe7 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-07.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-08.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-08.png new file mode 100644 index 0000000..f6ec8a8 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-08.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-09.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-09.png new file mode 100644 index 0000000..95b8679 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-09.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-10.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-10.png new file mode 100644 index 0000000..6b366a1 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-10.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-11.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-11.png new file mode 100644 index 0000000..8d74196 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-11.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-12.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-12.png new file mode 100644 index 0000000..b40f7b7 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-12.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-13.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-13.png new file mode 100644 index 0000000..2215a24 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-13.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-14.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-14.png new file mode 100644 index 0000000..2ae4da8 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-14.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-15.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-15.png new file mode 100644 index 0000000..f67632b Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-15.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-16.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-16.png new file mode 100644 index 0000000..0817bf0 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-16.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-17.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-17.png new file mode 100644 index 0000000..e44c9ca Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-17.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-18.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-18.png new file mode 100644 index 0000000..4078eaa Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-18.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-19.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-19.png new file mode 100644 index 0000000..16fd650 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-19.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-20.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-20.png new file mode 100644 index 0000000..ab5d936 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-20.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-21.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-21.png new file mode 100644 index 0000000..658d203 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-21.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-22.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-22.png new file mode 100644 index 0000000..74c5e14 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-22.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-23.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-23.png new file mode 100644 index 0000000..d5593bd Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-23.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-24.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-24.png new file mode 100644 index 0000000..8776aad Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-24.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-25.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-25.png new file mode 100644 index 0000000..f6e4023 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-25.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-26.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-26.png new file mode 100644 index 0000000..f4bab91 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-26.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-27.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-27.png new file mode 100644 index 0000000..0609352 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-27.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-28.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-28.png new file mode 100644 index 0000000..f189ded Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-28.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-29.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-29.png new file mode 100644 index 0000000..dea9416 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-29.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-30.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-30.png new file mode 100644 index 0000000..2fa3136 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-30.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-31.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-31.png new file mode 100644 index 0000000..3b5091c Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-31.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-32.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-32.png new file mode 100644 index 0000000..ceb45ad Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-32.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-33.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-33.png new file mode 100644 index 0000000..b8b2e7c Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-33.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-34.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-34.png new file mode 100644 index 0000000..bea9c40 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-34.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-35.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-35.png new file mode 100644 index 0000000..7489942 Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-35.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-36.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-36.png new file mode 100644 index 0000000..c4eaf1a Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-36.png differ diff --git a/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-37.png b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-37.png new file mode 100644 index 0000000..3b42faa Binary files /dev/null and b/PRACTICE/APPLICATION-LOAD-BALANCER/images/application-load-balancer-37.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/ASG-README.md b/PRACTICE/AUTO-SCALING-GROUP-ELB/ASG-README.md new file mode 100644 index 0000000..148ebdf --- /dev/null +++ b/PRACTICE/AUTO-SCALING-GROUP-ELB/ASG-README.md @@ -0,0 +1,321 @@ +# Auto Scaling Group (ASG) with Elastic Load Balancer + +## What is an Auto Scaling Group? + +An **Auto Scaling Group (ASG)** is an AWS service that automatically manages a fleet of EC2 instances, maintaining the number you specify at all times. When paired with an **Elastic Load Balancer (ELB)**, the ASG registers its instances as targets in the load balancer's target group automatically — so every new instance spun up by the ASG is immediately eligible to receive traffic. The ELB continuously performs health checks on all registered instances and reports results back to the ASG; if an instance fails a health check, the ASG detects it, terminates the unhealthy instance, and launches a replacement — entirely without human intervention. This combination delivers **high availability**, **self-healing infrastructure**, and the ability to **scale in or out** based on demand. + +> **Prerequisites:** This lab assumes an Application Load Balancer (`ALB-ASG`) and Target Group (`ALB-ASG-TG`) already exist. If you haven't created them yet, refer to the [ALB README](../APPLICATION-LOAD-BALANCER/ALB-README.md) first. + +--- + +## Lab Overview + +| Resource | Name / Value | +|---|---| +| Auto Scaling Group | `ASG` | +| Launch Template | `ASG-launch-template` | +| AMI | Amazon Linux 2023 (ami-0317b0f0a0144b137) | +| Instance Type | `t3.micro` | +| Security Group (instances) | `launch-wizard-1` | +| Security Group (ALB) | `SG-ASG` | +| Load Balancer | `ALB-ASG` | +| Target Group | `ALB-ASG-TG` | +| Desired / Min / Max capacity | `2` / `1` / `4` | +| Availability Zones | ap-south-1a, ap-south-1b, ap-south-1c | +| Region | `ap-south-1` (Mumbai) | + +--- + +## Video Demo + +[![Auto Scaling Group with ELB – Full Demo](https://img.youtube.com/vi/gUS-584nZ7M/0.jpg)](https://youtu.be/gUS-584nZ7M) + +--- + +## Step-by-Step Walkthrough + +### Step 1 — Open EC2 and Navigate to Auto Scaling Groups + +Log in to the AWS Management Console. From the **Recently visited** services on Console Home, click **EC2**. + +![AWS Console Home – click EC2](./images/auto-scaling-group-elb-01.png) + +Inside the EC2 dashboard, locate the **Resources** panel and click **Auto Scaling Groups**. + +![EC2 Dashboard – click Auto Scaling Groups](./images/auto-scaling-group-elb-02.png) + +The Auto Scaling Groups landing page explains how ASG works. Click **Create Auto Scaling group** to begin. + +![Auto Scaling Groups landing page – Create Auto Scaling group](./images/auto-scaling-group-elb-03.png) + +--- + +### Step 2 — Create a Launch Template + +A **launch template** is a reusable blueprint that tells the ASG exactly how to configure every EC2 instance it launches — OS, instance type, security group, and startup script — so you never have to configure machines manually again. + +#### 2a — Name the ASG and open the launch template wizard + +In the **Name** field of the ASG wizard, enter `ASG`. Since no launch template exists yet, click **Create a launch template** to open the template wizard in a new tab. + +![ASG wizard – enter name and click Create a launch template](./images/auto-scaling-group-elb-04.png) + +#### 2b — Name and describe the template + +Enter the following details: +- **Launch template name:** `ASG-launch-template` +- **Template version description:** `A launch template for Auto Scaling Group` +- **Auto Scaling guidance:** check the box — *Provide guidance to help me set up a template that I can use with EC2 Auto Scaling* + +Then scroll down. + +![Launch template – name and description](./images/auto-scaling-group-elb-05.png) + +#### 2c — Choose AMI and instance type + +Under **Application and OS Images**, click **Quick Start**, select **Amazon Linux**, and choose **Amazon Linux 2023 AMI 2023.10** (Free tier eligible). + +![AMI selection – Amazon Linux 2023](./images/auto-scaling-group-elb-06.png) + +Under **Instance type**, open the dropdown and select **t3.micro** (Free tier eligible, 2 vCPU, 1 GiB Memory). + +![Instance type – select t3.micro](./images/auto-scaling-group-elb-07.png) + +#### 2d — Select security group, add user data, and create the template + +Under **Network settings**, choose **Select existing security group** and select **launch-wizard-1**. + +![Network settings – select launch-wizard-1 security group](./images/auto-scaling-group-elb-08.png) + +Scroll down and click **Advanced details** to expand it. + +![Expand Advanced details](./images/auto-scaling-group-elb-09.png) + +Scroll to the **User data** field and paste the following script. This script runs automatically the first time each instance boots, installing and starting a web server that displays the instance's private hostname — which is how you can confirm the load balancer is routing to different instances. + +```bash +#!/bin/bash +# Use this for your user data (script from top to bottom) +# install httpd (Linux 2 version) +yum update -y +yum install -y httpd +systemctl start httpd +systemctl enable httpd +echo "

Hello World from $(hostname -f)

" > /var/www/html/index.html +``` + +| Command | Purpose | +|---|---| +| `yum update -y` | Updates all installed packages | +| `yum install -y httpd` | Installs Apache HTTP Server | +| `systemctl start httpd` | Starts the web server immediately | +| `systemctl enable httpd` | Ensures the web server starts on every reboot | +| `echo ... > /var/www/html/index.html` | Creates a page showing the instance's private hostname | + +Click **Create launch template**. + +![User data script – click Create launch template](./images/auto-scaling-group-elb-10.png) + +--- + +### Step 3 — Select the Launch Template and Configure the Network + +Return to the ASG wizard tab. Open the **Launch template** dropdown and select **ASG-launch-template**. + +![Select ASG-launch-template from dropdown](./images/auto-scaling-group-elb-11.png) + +The template summary appears, confirming the AMI, instance type (`t3.micro`), and security group. Click **Next**. + +![Launch template selected – click Next](./images/auto-scaling-group-elb-12.png) + +On the **Choose instance launch options** step, under **Availability Zones and subnets**, select all three availability zones: +- **aps1-az1** (ap-south-1a) +- **aps1-az2** (ap-south-1c) +- **aps1-az3** (ap-south-1b) + +Leave **Availability Zone distribution** set to **Balanced best effort**. Click **Next**. + +![All 3 AZs selected – click Next](./images/auto-scaling-group-elb-13.png) + +> **Why select all AZs?** Spreading instances across multiple Availability Zones means that if one AZ has an outage, your application keeps running on instances in the remaining zones. + +--- + +### Step 4 — Attach to the Existing Load Balancer + +On the **Integrate with other services** step, under **Load balancing**, select **Attach to an existing load balancer**. + +Under **Select the load balancers to attach**, choose **Choose from your load balancer target groups**. Click the target group dropdown — it shows `ALB-ASG-TG | HTTP` backed by the `ALB-ASG` load balancer. Select it. + +![Attach to existing LB – select ALB-ASG-TG target group](./images/auto-scaling-group-elb-14.png) + +Scroll down to the **Health checks** section. Tick **Turn on Elastic Load Balancing health checks** (marked Recommended). Leave the health check grace period at **300 seconds**. Click **Next**. + +![ELB health checks enabled – click Next](./images/auto-scaling-group-elb-15.png) + +> **Why enable ELB health checks?** By default the ASG only uses EC2 health checks (is the instance running?). Enabling ELB health checks adds a second layer — the ASG will also replace instances that are running but failing to serve HTTP traffic. + +--- + +### Step 5 — Configure Group Size and Scaling + +On the **Configure group size and scaling** step, set the capacity values: + +| Setting | Value | +|---|---| +| Desired capacity | `2` | +| Min desired capacity | `1` | +| Max desired capacity | `4` | + +Under **Automatic scaling**, leave **No scaling policies** selected — the group will maintain exactly the desired capacity at all times. + +Under **Instance maintenance policy**, leave **No policy** selected. + +![Group size configured – Desired 2, Min 1, Max 4](./images/auto-scaling-group-elb-16.png) + +Scroll down, review the additional settings (all defaults), and click **Next**. + +![Additional settings – click Next](./images/auto-scaling-group-elb-17.png) + +On the **Add notifications** step, skip adding any SNS notifications and click **Next**. + +![Add notifications – click Next](./images/auto-scaling-group-elb-18.png) + +On the **Add tags** step, skip adding tags and click **Next**. + +![Add tags – click Next](./images/auto-scaling-group-elb-19.png) + +--- + +### Step 6 — Review and Create + +The **Review** page summarises every setting. Verify: +- **Auto Scaling group name:** `ASG` +- **Launch template:** `ASG-launch-template` (Default version) +- **Availability Zones:** aps1-az1, aps1-az2, aps1-az3 + +Scroll through and confirm the load balancer integration and group size settings are correct. + +![Review page – verify all settings](./images/auto-scaling-group-elb-20.png) + +Scroll to the bottom and click **Create Auto Scaling group**. + +![Review page scrolled – click Create Auto Scaling group](./images/auto-scaling-group-elb-21.png) + +--- + +### Step 7 — ASG Created — EC2 Instances Auto-Provisioned + +The ASG is created. Navigate to the **Activity** tab. The Activity history shows two **Successful** launch events — the ASG immediately provisioned the desired 2 instances using the launch template. + +![ASG Activity tab – 2 successful instance launches](./images/auto-scaling-group-elb-22.png) + +Navigate to **EC2 > Instances**. Two `t3.micro` instances are in **Running** state — one in `ap-south-1a`, one in `ap-south-1b`. + +![EC2 Instances – 2 running instances auto-created by ASG](./images/auto-scaling-group-elb-23.png) + +> **Key point:** You never clicked "Launch instance." The ASG read the launch template and provisioned both machines automatically as soon as it was created. + +--- + +### Step 8 — Test via the Load Balancer DNS + +Navigate to **EC2 > Load Balancers** and select **ALB-ASG**. In the details panel, locate and copy the **DNS name**: + +``` +ALB-ASG-1425310447.ap-south-1.elb.amazonaws.com +``` + +![Load Balancers – locate and copy DNS name](./images/auto-scaling-group-elb-24.png) + +![DNS name – copy to clipboard](./images/auto-scaling-group-elb-25.png) + +Paste the DNS name into your browser's address bar and press Enter. You will see a page served by one of the ASG-managed instances: + +![Browser – Hello World from ASG instance](./images/auto-scaling-group-elb-27.png) + +Refresh the page. The ALB routes the request to another instance, and you may see a different hostname — confirming the load balancer is distributing traffic across the ASG's instances. + +![Browser – refreshed, response from another instance](./images/auto-scaling-group-elb-28.png) + +--- + +### Step 9 — Test Auto-Healing + +To verify the self-healing capability, manually terminate one of the running instances. Go to **EC2 > Instances**, select one of the ASG-managed instances, click **Instance state > Terminate (delete) instance**. + +![EC2 Instances – terminate instance to test auto-healing](./images/auto-scaling-group-elb-29.png) + +Switch to **Auto Scaling groups > ASG > Instance management**. The terminated instance appears with a health status of **Unhealthy** (Lifecycle: Terminating), while the others remain **Healthy**. + +![ASG Instance management – one instance Unhealthy](./images/auto-scaling-group-elb-30.png) + +Within a few minutes, check the **Activity** tab. A new entry appears: + +> *"An instance was launched in response to an unhealthy instance needing to be replaced."* + +The ASG detected the unhealthy instance via the ELB health check, terminated it, and launched a fresh replacement — all automatically. + +![ASG Activity – new instance launched to replace unhealthy one](./images/auto-scaling-group-elb-31.png) + +> **Auto-healing in action:** No manual intervention was needed. The ASG + ELB health check loop detected the failure and self-healed the fleet within minutes. + +--- + +### Step 10 — Cleanup + +Delete resources in the following order to avoid dependency errors. + +#### 10a — Delete the Auto Scaling Group + +In **Auto Scaling groups**, select `ASG`, click **Actions > Delete**. Type `delete` in the confirmation field and click **Delete**. + +![Select ASG – Actions > Delete](./images/auto-scaling-group-elb-32.png) + +![Confirm deletion – type delete](./images/auto-scaling-group-elb-33.png) + +The ASG status changes to **Deleting** and the desired/min/max capacity drops to 0. + +![ASG status – Deleting](./images/auto-scaling-group-elb-37.png) + +The Activity history shows both instances being terminated. + +![ASG Activity – instances being terminated](./images/auto-scaling-group-elb-38.png) + +> **Important:** Deleting the ASG automatically terminates all EC2 instances it manages. You do not need to terminate them separately. + +Navigate to **EC2 > Instances** to confirm all instances are terminated. + +![EC2 Instances – all terminated after ASG deletion](./images/auto-scaling-group-elb-39.png) + +#### 10b — Delete the Load Balancer + +In **EC2 > Load Balancers**, select `ALB-ASG`, click **Actions > Delete load balancer** and confirm. A green banner confirms: *Successfully deleted load balancer: ALB-ASG*. + +![Load Balancers – ALB-ASG deleted successfully](./images/auto-scaling-group-elb-34.png) + +#### 10c — Delete the Target Group + +In **EC2 > Target Groups**, select `ALB-ASG-TG`, click **Actions > Delete** and confirm. + +![Target Groups – delete ALB-ASG-TG](./images/auto-scaling-group-elb-35.png) + +#### 10d — Delete the Security Group + +In **EC2 > Security Groups**, select `SG-ASG`, click **Actions > Delete security groups** and confirm. + +![Security Groups – delete SG-ASG](./images/auto-scaling-group-elb-36.png) + +All resources from this lab have been deleted. + +--- + +## Key Concepts Learned + +- **Auto Scaling Group (ASG):** Automatically maintains a desired number of EC2 instances. If an instance fails or is terminated, the ASG replaces it to keep the fleet at the desired count. +- **Launch Template:** A reusable configuration blueprint (AMI, instance type, security group, user data) used by the ASG to provision identical instances on demand — eliminating manual configuration. +- **Desired / Min / Max Capacity:** Defines the target fleet size (Desired), the floor (Min), and the ceiling (Max) the ASG will operate between. +- **ELB Health Checks:** When enabled, the ASG uses the load balancer's perspective of instance health (is the instance passing HTTP checks?) rather than just EC2 status (is the instance running?). This catches application-level failures. +- **Auto-Healing:** ASG automatically replaces any instance the ELB marks as unhealthy — no manual action required. +- **Multi-AZ Deployment:** Distributing instances across multiple Availability Zones ensures the application survives a single AZ failure. +- **Instance Maintenance Policy:** Controls whether the ASG launches a replacement before or after terminating an unhealthy instance during rebalancing events. diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-01.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-01.png new file mode 100644 index 0000000..f336b55 Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-01.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-02.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-02.png new file mode 100644 index 0000000..86f0b76 Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-02.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-03.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-03.png new file mode 100644 index 0000000..a516b60 Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-03.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-04.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-04.png new file mode 100644 index 0000000..e2ac0e6 Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-04.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-05.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-05.png new file mode 100644 index 0000000..38b3efe Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-05.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-06.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-06.png new file mode 100644 index 0000000..ecaca5e Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-06.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-07.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-07.png new file mode 100644 index 0000000..07a53fe Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-07.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-08.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-08.png new file mode 100644 index 0000000..197236d Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-08.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-09.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-09.png new file mode 100644 index 0000000..54405cd Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-09.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-10.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-10.png new file mode 100644 index 0000000..5fd6966 Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-10.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-11.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-11.png new file mode 100644 index 0000000..32c2bc1 Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-11.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-12.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-12.png new file mode 100644 index 0000000..6cffb9f Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-12.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-13.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-13.png new file mode 100644 index 0000000..cf92da3 Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-13.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-14.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-14.png new file mode 100644 index 0000000..164e0bc Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-14.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-15.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-15.png new file mode 100644 index 0000000..716c71f Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-15.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-16.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-16.png new file mode 100644 index 0000000..54b34aa Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-16.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-17.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-17.png new file mode 100644 index 0000000..ca06bd6 Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-17.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-18.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-18.png new file mode 100644 index 0000000..a2c667c Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-18.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-19.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-19.png new file mode 100644 index 0000000..7a0ec99 Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-19.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-20.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-20.png new file mode 100644 index 0000000..a7601b7 Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-20.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-21.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-21.png new file mode 100644 index 0000000..2ea6b2d Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-21.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-22.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-22.png new file mode 100644 index 0000000..86b781b Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-22.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-23.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-23.png new file mode 100644 index 0000000..130e8c2 Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-23.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-24.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-24.png new file mode 100644 index 0000000..2ef7a59 Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-24.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-25.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-25.png new file mode 100644 index 0000000..8db1628 Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-25.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-26.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-26.png new file mode 100644 index 0000000..414cdf5 Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-26.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-27.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-27.png new file mode 100644 index 0000000..d4e8d6e Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-27.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-28.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-28.png new file mode 100644 index 0000000..0e3502c Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-28.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-29.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-29.png new file mode 100644 index 0000000..8e98f7e Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-29.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-30.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-30.png new file mode 100644 index 0000000..9298434 Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-30.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-31.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-31.png new file mode 100644 index 0000000..b7ae175 Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-31.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-32.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-32.png new file mode 100644 index 0000000..716280b Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-32.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-33.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-33.png new file mode 100644 index 0000000..f5d63a9 Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-33.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-34.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-34.png new file mode 100644 index 0000000..e679c44 Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-34.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-35.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-35.png new file mode 100644 index 0000000..cdd8c0b Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-35.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-36.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-36.png new file mode 100644 index 0000000..ace60ee Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-36.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-37.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-37.png new file mode 100644 index 0000000..0f3439f Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-37.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-38.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-38.png new file mode 100644 index 0000000..71ec4cb Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-38.png differ diff --git a/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-39.png b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-39.png new file mode 100644 index 0000000..210b9ef Binary files /dev/null and b/PRACTICE/AUTO-SCALING-GROUP-ELB/images/auto-scaling-group-elb-39.png differ diff --git a/PRACTICE/EC2/EC2-README.md b/PRACTICE/EC2/EC2-README.md new file mode 100644 index 0000000..3acd5e1 --- /dev/null +++ b/PRACTICE/EC2/EC2-README.md @@ -0,0 +1,225 @@ +# EC2 - Elastic Compute Cloud + +> Practice completed on: February 13, 2026 + +## Video Tutorial + +[![EC2 – Full Demo](https://img.youtube.com/vi/0Vk-Q6gzWwU/0.jpg)](https://youtu.be/0Vk-Q6gzWwU) + +## Overview + +This practice demonstrates how to launch an EC2 instance in AWS, configure security groups, connect via SSH, and manage the instance lifecycle. EC2 (Elastic Compute Cloud) is AWS's virtual server service that provides scalable computing capacity in the cloud. + +## Prerequisites + +- [x] AWS Account (Free Tier eligible) +- [x] SSH client installed (PuTTY for Windows, or built-in ssh command) +- [x] Basic understanding of Linux command line +- [x] Basic networking knowledge (IP addresses, ports, security) + +## Step-by-Step Walkthrough + +### Step 1: AWS Management Console + +Starting point - accessing the AWS Management Console to begin the EC2 instance creation process. + +![Step 1: AWS Console Dashboard](ec2-01.png) + +**Key Points:** +- Navigate to EC2 service from the AWS Console +- Ensure you're in the correct AWS region (check top-right corner) + +--- + +### Step 2: Launch Instance + +Initiating the EC2 instance launch wizard to configure and create a new virtual machine. + +![Step 2: Launch Instance](ec2-02.png) + +**Key Points:** +- Click "Launch Instance" button to start the configuration process +- You'll configure multiple settings before the instance is created + +--- + +### Step 3: Instance Configuration + +Configuring basic instance details including name, AMI selection, and instance type. + +![Step 3: Instance Configuration](ec2-03.png) + +**Key Points:** +- Choose a meaningful name for your instance +- Select Amazon Machine Image (AMI) - Amazon Linux 2 or Ubuntu are common free tier options +- Select instance type - t2.micro is free tier eligible + +--- + +### Step 4: Key Pair Configuration + +Creating or selecting a key pair for secure SSH access to the EC2 instance. + +![Step 4: Key Pair Setup](ec2-04.png) + +**Key Points:** +- Key pairs are essential for SSH authentication +- Download the .pem file immediately - you cannot retrieve it later +- Store the key file securely - it's like a password for your instance + +--- + +### Step 5: Network Settings + +Configuring network and security group settings to control inbound and outbound traffic. + +![Step 5: Network and Security Settings](ec2-05.png) + +**Key Points:** +- Security groups act as virtual firewalls +- SSH (port 22) must be allowed for remote access +- Consider restricting SSH access to your IP address for better security + +--- + +### Step 6: Security Group Rules + +Detailed configuration of security group rules defining which ports and protocols are accessible. + +![Step 6: Security Group Configuration](ec2-06.png) + +**Key Points:** +- Default SSH rule allows connection from anywhere (0.0.0.0/0) +- For production, always restrict source IP addresses +- Additional rules can be added for HTTP (80), HTTPS (443), or custom applications + +--- + +### Step 7: Storage Configuration + +Configuring the root volume and additional storage for the EC2 instance. + +![Step 7: Storage Settings](ec2-07.png) + +**Key Points:** +- Default is 8 GB general purpose SSD (gp2/gp3) +- Free tier includes up to 30 GB of EBS storage +- Volume is deleted on instance termination by default (can be changed) + +--- + +### Step 8: Review and Launch + +Final review of all configurations before launching the instance. + +![Step 8: Review Configuration](ec2-08.png) + +**Key Points:** +- Double-check all settings before launching +- Instance type, security groups, and key pair cannot be changed after creation (without stopping) +- Confirm you have the key pair file downloaded + +--- + +### Step 9: Instance Launching + +Instance creation in progress - AWS is provisioning the virtual machine. + +![Step 9: Launch Status](ec2-09.png) + +**Key Points:** +- Instance goes through several states: pending → running +- Usually takes 1-2 minutes to fully initialize +- You can view instance details and status from the EC2 dashboard + +--- + +### Step 10: Instance Running + +Instance is now running and ready for connection - view instance details and public IP. + +![Step 10: Instance Running](ec2-10.png) + +**Key Points:** +- Instance state shows "Running" with a green indicator +- Public IPv4 address is assigned (needed for SSH connection) +- Instance ID uniquely identifies your EC2 instance + +--- + +### Step 11: SSH Connection Details + +Preparing to connect to the instance via SSH using the key pair and public IP address. + +![Step 11: Connection Information](ec2-11.png) + +**Key Points:** +- Use the Connect button to see detailed SSH instructions +- Connection command format: `ssh -i "keyfile.pem" ec2-user@public-ip` +- For Windows users: Use PuTTY or Windows SSH client +- First connection will prompt to verify the host fingerprint + +--- + +## Key Concepts Learned + +- **EC2 Instance:** Virtual server in AWS cloud that you can configure and control +- **AMI (Amazon Machine Image):** Pre-configured template containing the OS and software +- **Instance Type:** Defines the CPU, memory, storage, and networking capacity (e.g., t2.micro) +- **Key Pair:** Public-private key pair used for secure SSH authentication +- **Security Group:** Virtual firewall controlling inbound and outbound traffic rules +- **EBS (Elastic Block Store):** Persistent block storage volumes for EC2 instances +- **Public IP:** Internet-accessible IP address assigned to your instance +- **SSH (Secure Shell):** Encrypted protocol for secure remote access to Linux instances + +## Video Walkthrough + +Complete demonstration of launching an EC2 instance and connecting via SSH: + +![EC2 SSH Connection Demo](ec2.mp4) + +This video walks through the entire process from creating the instance to successfully establishing an SSH connection. + +## Troubleshooting + +### Issue: Cannot connect via SSH - Connection timeout +**Solution:** +- Check security group allows SSH (port 22) from your IP address +- Verify you're using the correct public IP address +- Ensure your internet connection isn't blocking outbound SSH traffic + +### Issue: Permission denied (publickey) error +**Solution:** +- Verify you're using the correct key pair file (.pem) +- Check key file permissions: `chmod 400 keyfile.pem` on Linux/Mac +- Ensure you're using the correct username (ec2-user for Amazon Linux, ubuntu for Ubuntu) + +### Issue: Key file permissions too open +**Solution:** +- On Linux/Mac: Run `chmod 400 keyfile.pem` +- On Windows: Right-click key file → Properties → Security → Advanced → Disable inheritance + +## Next Steps + +**Related AWS Services to Practice:** +- **EBS Volumes:** Attach additional storage to EC2 instances +- **Elastic IP:** Associate a static IP address to your instance +- **Load Balancers:** Distribute traffic across multiple EC2 instances +- **Auto Scaling:** Automatically adjust instance capacity based on demand +- **CloudWatch:** Monitor instance performance and set up alarms + +**Recommended Next Practice:** +- [Application Load Balancer](../ALB/README.md) - Learn to distribute traffic +- [S3 Integration](../S3/README.md) - Store and retrieve files from EC2 +- [VPC Configuration](../VPC/README.md) - Understand AWS networking + +## Resources + +- [AWS EC2 Documentation](https://docs.aws.amazon.com/ec2/) +- [EC2 Instance Types](https://aws.amazon.com/ec2/instance-types/) +- [Security Groups Best Practices](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html) +- [SSH Connection Guide](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html) + +--- + +[↑ Back to Practice Index](../../README.md) diff --git a/PRACTICE/EC2/ec2-01.png b/PRACTICE/EC2/ec2-01.png new file mode 100644 index 0000000..4f3ee1e Binary files /dev/null and b/PRACTICE/EC2/ec2-01.png differ diff --git a/PRACTICE/EC2/ec2-02.png b/PRACTICE/EC2/ec2-02.png new file mode 100644 index 0000000..fcf744d Binary files /dev/null and b/PRACTICE/EC2/ec2-02.png differ diff --git a/PRACTICE/EC2/ec2-03.png b/PRACTICE/EC2/ec2-03.png new file mode 100644 index 0000000..10f7945 Binary files /dev/null and b/PRACTICE/EC2/ec2-03.png differ diff --git a/PRACTICE/EC2/ec2-04.png b/PRACTICE/EC2/ec2-04.png new file mode 100644 index 0000000..9230de4 Binary files /dev/null and b/PRACTICE/EC2/ec2-04.png differ diff --git a/PRACTICE/EC2/ec2-05.png b/PRACTICE/EC2/ec2-05.png new file mode 100644 index 0000000..bd2b718 Binary files /dev/null and b/PRACTICE/EC2/ec2-05.png differ diff --git a/PRACTICE/EC2/ec2-06.png b/PRACTICE/EC2/ec2-06.png new file mode 100644 index 0000000..c3fbee3 Binary files /dev/null and b/PRACTICE/EC2/ec2-06.png differ diff --git a/PRACTICE/EC2/ec2-07.png b/PRACTICE/EC2/ec2-07.png new file mode 100644 index 0000000..18d42da Binary files /dev/null and b/PRACTICE/EC2/ec2-07.png differ diff --git a/PRACTICE/EC2/ec2-08.png b/PRACTICE/EC2/ec2-08.png new file mode 100644 index 0000000..22d0956 Binary files /dev/null and b/PRACTICE/EC2/ec2-08.png differ diff --git a/PRACTICE/EC2/ec2-09.png b/PRACTICE/EC2/ec2-09.png new file mode 100644 index 0000000..dcd46ab Binary files /dev/null and b/PRACTICE/EC2/ec2-09.png differ diff --git a/PRACTICE/EC2/ec2-10.png b/PRACTICE/EC2/ec2-10.png new file mode 100644 index 0000000..d53aea8 Binary files /dev/null and b/PRACTICE/EC2/ec2-10.png differ diff --git a/PRACTICE/EC2/ec2-11.png b/PRACTICE/EC2/ec2-11.png new file mode 100644 index 0000000..15d31c4 Binary files /dev/null and b/PRACTICE/EC2/ec2-11.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/S3-STATIC-WEBSITE-HOSTING-README.md b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/S3-STATIC-WEBSITE-HOSTING-README.md new file mode 100644 index 0000000..c4872c6 --- /dev/null +++ b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/S3-STATIC-WEBSITE-HOSTING-README.md @@ -0,0 +1,292 @@ +# S3 Static Website Hosting + +## What is S3 Static Website Hosting? + +**Amazon S3 (Simple Storage Service)** can host static websites directly from a bucket without requiring a traditional web server. When you enable static website hosting on an S3 bucket, AWS provides a unique endpoint URL that serves your HTML, CSS, JavaScript, and image files to anyone on the internet — as long as you configure the bucket for public read access and attach the appropriate bucket policy. This is a cost-effective, highly available, and scalable solution for hosting personal websites, single-page applications, documentation sites, landing pages, and portfolios. Unlike dynamic websites that require backend servers and databases, S3 static hosting is ideal for content that doesn't change based on user interactions or server-side processing. + +--- + +## Lab Overview + +| Resource | Name / Value | +|---|---| +| S3 Bucket | `your-unique-bucket-name` | +| Bucket Policy | Allow public `GetObject` | +| Static Website Hosting | Enabled | +| Index Document | `index.html` | +| Region | `ap-south-1` (or your preferred region) | +| Bucket Website Endpoint | `http://.s3-website-.amazonaws.com` | + +--- + +## Video Demo + +[![S3 Static Website Hosting – Full Demo](https://img.youtube.com/vi/klEGEw2O6q0/0.jpg)](https://youtu.be/klEGEw2O6q0) + +--- + +## Step-by-Step Walkthrough + +### Step 1 — Open AWS Console and Navigate to S3 + +Log in to the AWS Management Console and search for **S3** in the services search bar, then click on S3 to open the S3 dashboard. + +![AWS Console – navigate to S3](./images/s3-static-website-hosting-01.png) + +--- + +### Step 2 — Create an S3 Bucket + +#### 2a — Click Create bucket + +In the S3 dashboard, click **Create bucket** to begin the bucket creation wizard. + +![S3 Dashboard – click Create bucket](./images/s3-static-website-hosting-02.png) + +#### 2b — Enter a unique bucket name + +Enter a **globally unique bucket name**. Bucket names must be unique across all AWS accounts worldwide and must follow DNS naming conventions (lowercase letters, numbers, and hyphens only). + +![Bucket creation wizard – enter unique bucket name](./images/s3-static-website-hosting-03.png) + +#### 2c — Uncheck "Block all public access" + +Scroll down to the **Block Public Access settings** section and **uncheck "Block all public access"**. For static website hosting to work, your bucket must allow public read access to its objects. + +> ⚠️ **Important:** Unchecking this setting means anyone with the URL can access files in this bucket. Only use this for content you intend to make publicly available. + +![Uncheck Block all public access](./images/s3-static-website-hosting-04.png) + +#### 2d — Acknowledge the warning and create the bucket + +Check the acknowledgment box confirming you understand the bucket will be public, then click **Create bucket** at the bottom of the page. + +![Acknowledge and click Create bucket](./images/s3-static-website-hosting-05.png) + +--- + +### Step 3 — Upload Website Files + +#### 3a — Open the bucket + +The bucket is created successfully. Click on the **bucket name** to open it and view its contents. + +![Bucket created successfully – click on bucket name](./images/s3-static-website-hosting-06.png) + +#### 3b — Click Upload + +Inside the bucket, click the **Upload** button to open the file upload wizard. + +![Inside bucket – click Upload](./images/s3-static-website-hosting-07.png) + +#### 3c — Add files + +Click **Add files** or **Add folder** and select the static website files from your local machine (HTML, CSS, JavaScript, images, etc.). + +![Upload wizard – add files](./images/s3-static-website-hosting-08.png) + +#### 3d — Upload the files + +After selecting your files, scroll down and click **Upload** to transfer them to the S3 bucket. + +![Click Upload to upload files](./images/s3-static-website-hosting-09.png) + +--- + +### Step 4 — Enable Static Website Hosting + +#### 4a — Navigate to Properties tab + +Click on the **Properties** tab at the top of the bucket page. + +![Click Properties tab](./images/s3-static-website-hosting-10.png) + +#### 4b — Find Static website hosting section + +Scroll down to the **Static website hosting** section at the bottom of the Properties page. + +![Scroll to Static website hosting](./images/s3-static-website-hosting-11.png) + +#### 4c — Enable static website hosting + +Click **Edit**, then select **Enable**. Choose **Host a static website** as the hosting type, and enter `index.html` as the **Index document** (the default page visitors see when they access your site). + +![Enable static website hosting and set index document](./images/s3-static-website-hosting-12.png) + +#### 4d — Save changes + +Scroll down and click **Save changes** to apply the static website hosting configuration. + +![Save changes](./images/s3-static-website-hosting-13.png) + +--- + +### Step 5 — Configure Bucket Policy + +#### 5a — Navigate to Permissions and edit bucket policy + +Click the **Permissions** tab, scroll down to **Bucket policy**, and click **Edit**. + +![Permissions tab – edit bucket policy](./images/s3-static-website-hosting-14.png) + +#### 5b — Add a statement or use Policy generator + +Click **Add statement** or **Policy generator** to create a new bucket policy. The policy generator helps you build the JSON policy without writing it manually. + +![Click Add statement or Policy generator](./images/s3-static-website-hosting-15.png) + +--- + +### Step 6 — Generate and Apply the Bucket Policy + +#### 6a — Open AWS Policy Generator + +If using the AWS Policy Generator, you'll see a form. Configure it as follows to allow public read access to all objects in the bucket. + +![AWS Policy Generator interface](./images/s3-static-website-hosting-17.png) + +**Policy Settings:** + +| Field | Value | +|---|---| +| **Effect** | Allow | +| **Principal** | `*` (anyone) | +| **Actions** | `GetObject` | +| **ARN** | `arn:aws:s3:::your-bucket-name/*` (replace with your bucket name and add `/*` at the end) | + +#### 6b — Add statement and generate policy + +After entering the settings above, click **Add Statement**, then click **Generate Policy**. + +![Add Statement button](./images/s3-static-website-hosting-18.png) + +#### 6c — Copy the generated policy + +The policy generator will output a JSON policy. Click **Copy** to copy the entire policy to your clipboard. + +![Copy the generated policy](./images/s3-static-website-hosting-19.png) + +**Sample Bucket Policy:** + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "PublicReadGetObject", + "Effect": "Allow", + "Principal": "*", + "Action": "s3:GetObject", + "Resource": "arn:aws:s3:::your-bucket-name/*" + } + ] +} +``` + +> **Note:** Replace `your-bucket-name` with your actual bucket name. + +#### 6d — Paste the policy and save changes + +Return to the bucket policy editor, paste the policy JSON, and click **Save changes**. + +![Paste policy in bucket policy editor](./images/s3-static-website-hosting-20.png) + +#### 6e — Bucket policy created successfully + +You'll see a confirmation that the bucket policy has been successfully applied. + +![Bucket policy successfully created](./images/s3-static-website-hosting-21.png) + +--- + +### Step 7 — Access Your Website + +#### 7a — Return to Properties tab + +Click the **Properties** tab again. + +![Properties tab](./images/s3-static-website-hosting-22.png) + +#### 7b — Copy the bucket website endpoint + +Scroll down to the **Static website hosting** section and copy the **Bucket website endpoint** URL. + +![Copy bucket website endpoint](./images/s3-static-website-hosting-23.png) + +#### 7c — Open the website in your browser + +Paste the endpoint URL into your browser's address bar and press Enter. Your static website is now live and publicly accessible! 🎉 + +![Website successfully hosted on S3](./images/s3-static-website-hosting-24.png) + +--- + +### Step 8 — Clean Up Resources (Empty the Bucket) + +#### 8a — Empty the bucket + +To delete a bucket, you must first empty it. Select the bucket (or open it) and click **Empty**. + +![Select bucket and click Empty](./images/s3-static-website-hosting-25.png) + +#### 8b — Confirm deletion of objects + +Type `permanently delete` in the confirmation field and click **Delete objects**. + +![Type permanently delete and confirm](./images/s3-static-website-hosting-26.png) + +--- + +### Step 9 — Delete the S3 Bucket + +#### 9a — Select the bucket and click Delete + +After emptying the bucket, select it from the S3 dashboard and click **Delete**. + +![Select bucket and click Delete](./images/s3-static-website-hosting-27.png) + +#### 9b — Confirm bucket deletion + +Type the **bucket name** exactly as shown to confirm permanent deletion, then click **Delete bucket**. + +![Type bucket name to confirm deletion](./images/s3-static-website-hosting-28.png) + +#### 9c — Bucket deleted successfully + +The bucket and all its configurations are now permanently deleted. + +![Bucket successfully deleted](./images/s3-static-website-hosting-29.png) + +--- + +## Key Points to Remember + +| Point | Details | +|---|---| +| **Bucket names must be globally unique** | No two buckets can have the same name across all AWS accounts worldwide | +| **Public access is required** | Static website hosting requires public read access via bucket policy | +| **Bucket policy syntax** | Must allow `s3:GetObject` action for `Principal: "*"` on `Resource: "arn:aws:s3:::bucket-name/*"` | +| **Index document** | Always specify `index.html` or your custom default page | +| **Endpoint URL format** | `http://.s3-website-.amazonaws.com` | +| **HTTPS not supported by default** | Use Amazon CloudFront for HTTPS and custom domains | +| **Cost efficiency** | Free tier includes 5GB storage, 20,000 GET requests, and 2,000 PUT requests per month (first 12 months) | +| **Always clean up** | Delete buckets and objects when no longer needed to avoid charges | + +--- + +## Use Cases + +- 📄 Personal websites and blogs +- 🚀 Single-page applications (SPAs) +- 📚 Documentation sites +- 🎨 Portfolio websites +- 📱 Landing pages +- 🧪 Prototype hosting and demos + +--- + +## Additional Resources + +- [AWS S3 Static Website Hosting Documentation](https://docs.aws.amazon.com/AmazonS3/latest/userguide/WebsiteHosting.html) +- [S3 Pricing](https://aws.amazon.com/s3/pricing/) +- [Using CloudFront with S3 for HTTPS](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web.html) diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-01.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-01.png new file mode 100644 index 0000000..20e31c8 Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-01.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-02.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-02.png new file mode 100644 index 0000000..5f329b0 Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-02.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-03.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-03.png new file mode 100644 index 0000000..ef0e7d2 Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-03.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-04.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-04.png new file mode 100644 index 0000000..80f31f5 Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-04.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-05.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-05.png new file mode 100644 index 0000000..b037a33 Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-05.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-06.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-06.png new file mode 100644 index 0000000..2a03989 Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-06.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-07.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-07.png new file mode 100644 index 0000000..733a261 Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-07.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-08.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-08.png new file mode 100644 index 0000000..635846e Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-08.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-09.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-09.png new file mode 100644 index 0000000..3fe90a5 Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-09.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-10.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-10.png new file mode 100644 index 0000000..dfc83b9 Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-10.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-11.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-11.png new file mode 100644 index 0000000..f4e9d8a Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-11.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-12.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-12.png new file mode 100644 index 0000000..49c0659 Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-12.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-13.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-13.png new file mode 100644 index 0000000..6e8ac7c Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-13.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-14.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-14.png new file mode 100644 index 0000000..c89f8be Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-14.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-15.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-15.png new file mode 100644 index 0000000..4f5f052 Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-15.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-16.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-16.png new file mode 100644 index 0000000..2afe5fd Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-16.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-17.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-17.png new file mode 100644 index 0000000..4d1d72b Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-17.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-18.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-18.png new file mode 100644 index 0000000..b21394e Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-18.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-19.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-19.png new file mode 100644 index 0000000..bb93267 Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-19.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-20.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-20.png new file mode 100644 index 0000000..5b02384 Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-20.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-21.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-21.png new file mode 100644 index 0000000..a1cca5f Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-21.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-22.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-22.png new file mode 100644 index 0000000..a39d15f Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-22.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-23.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-23.png new file mode 100644 index 0000000..11c70fb Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-23.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-24.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-24.png new file mode 100644 index 0000000..9cc67fd Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-24.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-25.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-25.png new file mode 100644 index 0000000..b7b6389 Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-25.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-26.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-26.png new file mode 100644 index 0000000..78c8464 Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-26.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-27.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-27.png new file mode 100644 index 0000000..1ef48a9 Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-27.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-28.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-28.png new file mode 100644 index 0000000..260bbb6 Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-28.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-29.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-29.png new file mode 100644 index 0000000..2d6c8fc Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-29.png differ diff --git a/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-30.png b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-30.png new file mode 100644 index 0000000..0116f64 Binary files /dev/null and b/PRACTICE/S3-STATIC-WEBSITE-HOSTING/images/s3-static-website-hosting-30.png differ diff --git a/Portfolio/PORTFOLIO-README.md b/Portfolio/PORTFOLIO-README.md new file mode 100644 index 0000000..658bf87 --- /dev/null +++ b/Portfolio/PORTFOLIO-README.md @@ -0,0 +1,199 @@ +# AWS-PORTFOLIO + +# PortfolioEdge | Personal Portfolio on AWS + +> Personal portfolio website hosted on AWS S3 + CloudFront with automated CI/CD via GitHub Actions and a custom domain with HTTPS. + +🌐 **Live:** [https://www.adityanair.tech](https://www.adityanair.tech) + +> 💡 Portfolio source code is kept in a private repository. +> The CI/CD pipeline, infrastructure setup, and architecture are fully documented here. + +--- +## 📸 Screenshots + +Images are located in the `images/` folder. + +### Live Site at Custom Domain +![Live Site preview](./images/portfolio-43.png) +--- +![Live Site URL preview ](./images/portfolio-42.png) +--- +### S3 Bucket — Files Uploaded +![S3 Bucket](./images/portfolio-05.png) + +### CloudFront Distribution — Enabled +![CloudFront](./images/portfolio-14.png) + +### ACM Certificate — Issued +![ACM](./images/portfolio-38.png) + +### GitHub Actions — Successful Deploy +![GitHub Actions](./images/portfolio-33.png) + +### Custom Domain with .tech domain redeemed with Github Education Plan +![.tech domain](./images/portfolio-34.png) + + + + +--- +## Architecture + +``` +User visits https://adityanair.tech + | + ▼ +Domain Forwarding at get.tech registrar +(301 redirect to www.adityanair.tech) + | + ▼ +https://www.adityanair.tech + | + ▼ + +-------------------------+ + | AWS CloudFront (CDN) | + | - Global edge caching | + | - HTTPS via ACM cert | + | - OAC to S3 | + +-------------------------+ + | + ▼ + +-------------------------+ + | AWS S3 (Private Bucket)| + | - No public access | + | - OAC policy only | + +-------------------------+ + ^ + | + +---------------------------+ + | GitHub Actions CI/CD | + | - Triggered on push | + | - S3 sync + CF invalidate| + +---------------------------+ +``` + +--- + +## AWS Services Used + +| Service | Purpose | +|---|---| +| Amazon S3 | Static file hosting (private bucket) | +| Amazon CloudFront | CDN, HTTPS termination, global edge caching | +| AWS ACM | Free SSL/TLS certificate for custom domain | +| AWS IAM | Scoped credentials for GitHub Actions | +| GitHub Actions | CI/CD pipeline — auto-deploy on push | + +--- + +## Key Cloud Concepts Demonstrated + +- **Private S3 + OAC** — Bucket is fully private; CloudFront accesses it via Origin Access Control (OAC), the modern replacement for OAI +- **CDN edge caching** — CloudFront serves cached content from global edge locations for low latency worldwide +- **HTTPS everywhere** — ACM-issued SSL certificate attached to CloudFront; HTTP auto-redirects to HTTPS +- **Custom domain** — `.tech` domain pointed to CloudFront via CNAME; root domain forwarded to `www` subdomain +- **CI/CD pipeline** — GitHub Actions automatically syncs S3 and invalidates CloudFront cache on every `git push` to `main` +- **Cache invalidation** — `/*` invalidation ensures visitors always get the latest version after each deploy +- **Least privilege IAM** — GitHub Actions uses a dedicated IAM user with only S3 and CloudFront permissions + +--- + +## CI/CD Pipeline + +Every push to `main` in the private portfolio repo triggers the following workflow: + +```yaml +name: Deploy StaticEdge to S3 + CloudFront + +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ secrets.AWS_REGION }} + + - name: Sync files to S3 + run: | + aws s3 sync . s3://${{ secrets.S3_BUCKET }} \ + --delete \ + --cache-control "max-age=86400" \ + --exclude ".git/*" \ + --exclude ".github/*" \ + --exclude "README.md" + + - name: Invalidate CloudFront cache + run: | + aws cloudfront create-invalidation \ + --distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} \ + --paths "/*" +``` + +**Deploy time: ~16 seconds** ⚡ + +--- + +## Infrastructure Setup + +### S3 Bucket +- **Region:** ap-south-1 (Mumbai) +- **Access:** Private — all public access blocked +- **Bucket policy:** OAC policy allowing only CloudFront distribution `E16DKG9VJI9P0H` + +### CloudFront Distribution +- **Distribution ID:** `E16DKG9VJI9P0H` +- **Domain:** `d3diu0sa9fueaw.cloudfront.net` +- **Alternate domains:** `adityanair.tech`, `www.adityanair.tech` +- **Origin access:** OAC (Origin Access Control) — recommended method +- **Viewer protocol:** Redirect HTTP → HTTPS +- **Default root object:** `index.html` +- **Price class:** All edge locations + +### SSL Certificate (ACM) +- **Region:** us-east-1 (required for CloudFront) +- **Domains covered:** `adityanair.tech`, `www.adityanair.tech` +- **Validation:** DNS validation via CNAME records at get.tech + +### Custom Domain +- **Registrar:** get.tech (via GitHub Education Student Pack — free 1 year) +- **Root domain:** `adityanair.tech` → forwarded to `www.adityanair.tech` via Domain Forwarding +- **www subdomain:** CNAME → `d3diu0sa9fueaw.cloudfront.net` + +### IAM +- **User:** `github-actions-staticedge` +- **Policies:** `AmazonS3FullAccess`, `CloudFrontFullAccess` +- **Usage:** GitHub Actions secrets (never hardcoded) + +--- + +## GitHub Secrets Required + +| Secret | Description | +|---|---| +| `AWS_ACCESS_KEY_ID` | IAM user access key | +| `AWS_SECRET_ACCESS_KEY` | IAM user secret key | +| `AWS_REGION` | `ap-south-1` | +| `S3_BUCKET` | S3 bucket name | +| `CLOUDFRONT_DISTRIBUTION_ID` | CloudFront distribution ID | + +--- + + +## Author + +**Aditya Nair** +- GitHub: [@ADITYANAIR01](https://github.com/ADITYANAIR01) +- Portfolio: [https://www.adityanair.tech](https://www.adityanair.tech) \ No newline at end of file diff --git a/Portfolio/images/portfolio-01.png b/Portfolio/images/portfolio-01.png new file mode 100644 index 0000000..ed80d8a Binary files /dev/null and b/Portfolio/images/portfolio-01.png differ diff --git a/Portfolio/images/portfolio-02.png b/Portfolio/images/portfolio-02.png new file mode 100644 index 0000000..e2a7504 Binary files /dev/null and b/Portfolio/images/portfolio-02.png differ diff --git a/Portfolio/images/portfolio-03.png b/Portfolio/images/portfolio-03.png new file mode 100644 index 0000000..89489fc Binary files /dev/null and b/Portfolio/images/portfolio-03.png differ diff --git a/Portfolio/images/portfolio-04.png b/Portfolio/images/portfolio-04.png new file mode 100644 index 0000000..6845537 Binary files /dev/null and b/Portfolio/images/portfolio-04.png differ diff --git a/Portfolio/images/portfolio-05.png b/Portfolio/images/portfolio-05.png new file mode 100644 index 0000000..70af2a1 Binary files /dev/null and b/Portfolio/images/portfolio-05.png differ diff --git a/Portfolio/images/portfolio-06.png b/Portfolio/images/portfolio-06.png new file mode 100644 index 0000000..a4ba06b Binary files /dev/null and b/Portfolio/images/portfolio-06.png differ diff --git a/Portfolio/images/portfolio-07.png b/Portfolio/images/portfolio-07.png new file mode 100644 index 0000000..fb922e4 Binary files /dev/null and b/Portfolio/images/portfolio-07.png differ diff --git a/Portfolio/images/portfolio-08.png b/Portfolio/images/portfolio-08.png new file mode 100644 index 0000000..c0f0ac4 Binary files /dev/null and b/Portfolio/images/portfolio-08.png differ diff --git a/Portfolio/images/portfolio-09.png b/Portfolio/images/portfolio-09.png new file mode 100644 index 0000000..7e1f3d5 Binary files /dev/null and b/Portfolio/images/portfolio-09.png differ diff --git a/Portfolio/images/portfolio-10.png b/Portfolio/images/portfolio-10.png new file mode 100644 index 0000000..9983450 Binary files /dev/null and b/Portfolio/images/portfolio-10.png differ diff --git a/Portfolio/images/portfolio-11.png b/Portfolio/images/portfolio-11.png new file mode 100644 index 0000000..150b53d Binary files /dev/null and b/Portfolio/images/portfolio-11.png differ diff --git a/Portfolio/images/portfolio-12.png b/Portfolio/images/portfolio-12.png new file mode 100644 index 0000000..8e541dc Binary files /dev/null and b/Portfolio/images/portfolio-12.png differ diff --git a/Portfolio/images/portfolio-13.png b/Portfolio/images/portfolio-13.png new file mode 100644 index 0000000..559c764 Binary files /dev/null and b/Portfolio/images/portfolio-13.png differ diff --git a/Portfolio/images/portfolio-14.png b/Portfolio/images/portfolio-14.png new file mode 100644 index 0000000..2379042 Binary files /dev/null and b/Portfolio/images/portfolio-14.png differ diff --git a/Portfolio/images/portfolio-15.png b/Portfolio/images/portfolio-15.png new file mode 100644 index 0000000..0cb4048 Binary files /dev/null and b/Portfolio/images/portfolio-15.png differ diff --git a/Portfolio/images/portfolio-16.png b/Portfolio/images/portfolio-16.png new file mode 100644 index 0000000..af618c8 Binary files /dev/null and b/Portfolio/images/portfolio-16.png differ diff --git a/Portfolio/images/portfolio-17.png b/Portfolio/images/portfolio-17.png new file mode 100644 index 0000000..61c6d60 Binary files /dev/null and b/Portfolio/images/portfolio-17.png differ diff --git a/Portfolio/images/portfolio-18.png b/Portfolio/images/portfolio-18.png new file mode 100644 index 0000000..00d45f9 Binary files /dev/null and b/Portfolio/images/portfolio-18.png differ diff --git a/Portfolio/images/portfolio-19.png b/Portfolio/images/portfolio-19.png new file mode 100644 index 0000000..1b96e85 Binary files /dev/null and b/Portfolio/images/portfolio-19.png differ diff --git a/Portfolio/images/portfolio-20.png b/Portfolio/images/portfolio-20.png new file mode 100644 index 0000000..6358f87 Binary files /dev/null and b/Portfolio/images/portfolio-20.png differ diff --git a/Portfolio/images/portfolio-21.png b/Portfolio/images/portfolio-21.png new file mode 100644 index 0000000..5c5c356 Binary files /dev/null and b/Portfolio/images/portfolio-21.png differ diff --git a/Portfolio/images/portfolio-22.png b/Portfolio/images/portfolio-22.png new file mode 100644 index 0000000..3f27753 Binary files /dev/null and b/Portfolio/images/portfolio-22.png differ diff --git a/Portfolio/images/portfolio-23.png b/Portfolio/images/portfolio-23.png new file mode 100644 index 0000000..52f249f Binary files /dev/null and b/Portfolio/images/portfolio-23.png differ diff --git a/Portfolio/images/portfolio-24.png b/Portfolio/images/portfolio-24.png new file mode 100644 index 0000000..bba084c Binary files /dev/null and b/Portfolio/images/portfolio-24.png differ diff --git a/Portfolio/images/portfolio-25.png b/Portfolio/images/portfolio-25.png new file mode 100644 index 0000000..be9eb85 Binary files /dev/null and b/Portfolio/images/portfolio-25.png differ diff --git a/Portfolio/images/portfolio-26.png b/Portfolio/images/portfolio-26.png new file mode 100644 index 0000000..75327fe Binary files /dev/null and b/Portfolio/images/portfolio-26.png differ diff --git a/Portfolio/images/portfolio-27.png b/Portfolio/images/portfolio-27.png new file mode 100644 index 0000000..8a2c99d Binary files /dev/null and b/Portfolio/images/portfolio-27.png differ diff --git a/Portfolio/images/portfolio-28.png b/Portfolio/images/portfolio-28.png new file mode 100644 index 0000000..40f3495 Binary files /dev/null and b/Portfolio/images/portfolio-28.png differ diff --git a/Portfolio/images/portfolio-29.png b/Portfolio/images/portfolio-29.png new file mode 100644 index 0000000..82dcae7 Binary files /dev/null and b/Portfolio/images/portfolio-29.png differ diff --git a/Portfolio/images/portfolio-30.png b/Portfolio/images/portfolio-30.png new file mode 100644 index 0000000..c7d748c Binary files /dev/null and b/Portfolio/images/portfolio-30.png differ diff --git a/Portfolio/images/portfolio-31.png b/Portfolio/images/portfolio-31.png new file mode 100644 index 0000000..f0342d4 Binary files /dev/null and b/Portfolio/images/portfolio-31.png differ diff --git a/Portfolio/images/portfolio-32.png b/Portfolio/images/portfolio-32.png new file mode 100644 index 0000000..29d1c2a Binary files /dev/null and b/Portfolio/images/portfolio-32.png differ diff --git a/Portfolio/images/portfolio-33.png b/Portfolio/images/portfolio-33.png new file mode 100644 index 0000000..e4e87bf Binary files /dev/null and b/Portfolio/images/portfolio-33.png differ diff --git a/Portfolio/images/portfolio-34.png b/Portfolio/images/portfolio-34.png new file mode 100644 index 0000000..1014d1f Binary files /dev/null and b/Portfolio/images/portfolio-34.png differ diff --git a/Portfolio/images/portfolio-35.png b/Portfolio/images/portfolio-35.png new file mode 100644 index 0000000..2f52d58 Binary files /dev/null and b/Portfolio/images/portfolio-35.png differ diff --git a/Portfolio/images/portfolio-36.png b/Portfolio/images/portfolio-36.png new file mode 100644 index 0000000..bed837b Binary files /dev/null and b/Portfolio/images/portfolio-36.png differ diff --git a/Portfolio/images/portfolio-37.png b/Portfolio/images/portfolio-37.png new file mode 100644 index 0000000..82f0935 Binary files /dev/null and b/Portfolio/images/portfolio-37.png differ diff --git a/Portfolio/images/portfolio-38.png b/Portfolio/images/portfolio-38.png new file mode 100644 index 0000000..33bb756 Binary files /dev/null and b/Portfolio/images/portfolio-38.png differ diff --git a/Portfolio/images/portfolio-39.png b/Portfolio/images/portfolio-39.png new file mode 100644 index 0000000..744ecaa Binary files /dev/null and b/Portfolio/images/portfolio-39.png differ diff --git a/Portfolio/images/portfolio-40.png b/Portfolio/images/portfolio-40.png new file mode 100644 index 0000000..9885379 Binary files /dev/null and b/Portfolio/images/portfolio-40.png differ diff --git a/Portfolio/images/portfolio-41.png b/Portfolio/images/portfolio-41.png new file mode 100644 index 0000000..413e697 Binary files /dev/null and b/Portfolio/images/portfolio-41.png differ diff --git a/Portfolio/images/portfolio-42.png b/Portfolio/images/portfolio-42.png new file mode 100644 index 0000000..992d74c Binary files /dev/null and b/Portfolio/images/portfolio-42.png differ diff --git a/Portfolio/images/portfolio-43.png b/Portfolio/images/portfolio-43.png new file mode 100644 index 0000000..1aba7f1 Binary files /dev/null and b/Portfolio/images/portfolio-43.png differ diff --git a/aws-scalable-webapp-alb/AWS-SCALABLE-WEBAPP-ALB-README.md b/aws-scalable-webapp-alb/AWS-SCALABLE-WEBAPP-ALB-README.md new file mode 100644 index 0000000..b9cdc3a --- /dev/null +++ b/aws-scalable-webapp-alb/AWS-SCALABLE-WEBAPP-ALB-README.md @@ -0,0 +1,189 @@ +# 🚀 AWS Scalable Web Application — ALB + ASG + +A production-grade, self-healing web application deployed on AWS using an Application Load Balancer, Auto Scaling Group, and EC2 across multiple Availability Zones. Mimics how platforms like **Heroku / Railway** manage scalable deployments automatically. + +--- + +## 📸 Screenshots + +> All build screenshots are available in the [`/images`](./images) folder, numbered `1` through `81` in chronological build order. + +**Key screenshots inline:** + +### App Running via ALB DNS +![App running via ALB](./images/aws-scalable-webapp-alb-63.png) +> App served through the Application Load Balancer DNS — no direct EC2 IP + +### Target Group — Both Instances Healthy in different AZ's +![Target group healthy](./images/aws-scalable-webapp-alb-43.png) +> Both EC2 instances passing health checks across two Availability Zones + +### Auto Scaling Group — Instance Management after deleting +![ASG instances](./images/aws-scalable-webapp-alb-65.png) +> ASG maintaining desired capacity across `ap-south-1a` and `ap-south-1b` after terminating an ec2 machine to check for auto healing + +### AWS Notification -Subscription email +![Email subscription confirmation for cloudwatch notification](./images/aws-scalable-webapp-alb-78.png) + +![CONFIRMED](./images/aws-scalable-webapp-alb-79.png) +>Confirmed notification subscription + +### CloudWatch Monitoring Dashboard +![CloudWatch dashboard](./images/aws-scalable-webapp-alb-81.png) +> Real-time metrics — CPU, request count, response time, instance count + +---- + +## 🏗️ Architecture + +``` +Internet + │ + ▼ +Internet Gateway (scalable-webapp-igw) + │ + ▼ +┌─────────────────────────────────────────────────────────┐ +│ VPC: scalable-webapp-vpc (10.0.0.0/16) │ +│ │ +│ ┌──────────────────┐ ┌──────────────────┐ │ +│ │ public-subnet-1a │ │ public-subnet-1b │ │ +│ │ 10.0.1.0/24 │ │ 10.0.2.0/24 │ │ +│ └──────────────────┘ └──────────────────┘ │ +│ │ │ │ +│ ▼ ▼ │ +│ Application Load Balancer (webapp-alb) │ +│ │ HTTP :80 Listener │ +│ ▼ │ +│ Target Group (webapp-target-group) │ +│ ├──▶ EC2 t3.micro — ap-south-1a ✓ healthy │ +│ └──▶ EC2 t3.micro — ap-south-1b ✓ healthy │ +│ ▲ │ +│ Auto Scaling Group (webapp-asg) │ +│ min: 1 │ desired: 2 │ max: 4 │ +│ CPU > 50% → scale out │ +│ CPU < 30% → scale in │ +│ │ │ +│ Launch Template (webapp-launch-template) │ +│ Amazon Linux 2023 + nginx │ +└─────────────────────────────────────────────────────────┘ +``` + +--- + +## ⚙️ AWS Services Used + +| Service | Purpose | +|---|---| +| **VPC** | Isolated network with public subnets across 2 AZs | +| **EC2** | Compute — runs nginx web server | +| **Application Load Balancer** | Distributes traffic, health checks, single DNS entry | +| **Auto Scaling Group** | Automatically adds/removes instances based on CPU | +| **Launch Template** | Blueprint for identical EC2 instances | +| **IAM Role** | EC2 permissions for S3 and CloudWatch | +| **CloudWatch** | Metrics dashboard + CPU alarm via SNS email | +| **Security Groups** | Layered firewall — ALB faces internet, EC2 accepts ALB only | + +--- + +## 🔐 Security Design + +``` +Internet → alb-sg (HTTP 0.0.0.0/0) + ↓ + ec2-sg (HTTP from alb-sg ONLY) +``` + +EC2 instances are **not directly reachable** from the internet. All public traffic flows through the ALB only. This is a standard production security pattern. + +--- + +## 📦 Infrastructure Summary + +### VPC & Networking +| Resource | Value | +|---|---| +| VPC CIDR | `10.0.0.0/16` | +| Public Subnet 1 | `10.0.1.0/24` — ap-south-1a | +| Public Subnet 2 | `10.0.2.0/24` — ap-south-1b | +| Internet Gateway | Attached to VPC | +| Route Table | `0.0.0.0/0 → IGW` | + +### Compute +| Resource | Value | +|---|---| +| AMI | Amazon Linux 2023 | +| Instance Type | t3.micro | +| Web Server | nginx | +| Deployment | User data script on launch | + +### Auto Scaling +| Setting | Value | +|---|---| +| Minimum | 1 | +| Desired | 2 | +| Maximum | 4 | +| Scale-out trigger | CPU > 50% | +| Scale-in trigger | CPU < 30% | + +### Monitoring +| Resource | Value | +|---|---| +| Dashboard | `webapp-monitoring` | +| Alarm | `webapp-high-cpu` — triggers at 70% | +| Notification | SNS email alert | + +--- + +## 🛠️ Build Phases + +| Phase | What was built | +|---|---| +| 1 — Networking | VPC, subnets, IGW, route table, security groups | +| 2 — Compute | Launch template, IAM role, test EC2, nginx + web app | +| 3 — Auto Scaling | ASG with target tracking scaling policy | +| 4 — Load Balancer | ALB, target group, listener, ASG integration | +| 5 — Monitoring | CloudWatch dashboard, CPU alarm, SNS notification | + +--- + +## 💡 Key Cloud Concepts Demonstrated + +- **High Availability** — workload spread across 2 Availability Zones +- **Horizontal Scaling** — ASG adds instances under load, removes them when idle +- **Self-healing Infrastructure** — failed instances are automatically replaced by ASG +- **Security Layering** — EC2 not exposed directly, traffic flows ALB → EC2 only +- **Infrastructure as a Blueprint** — Launch Templates ensure every instance is identical +- **Observability** — CloudWatch dashboard + proactive alerting via SNS + +--- + +## 🧹 Cleanup + +To avoid AWS charges after testing: + +1. Delete **Auto Scaling Group** (this terminates all EC2 instances) +2. Delete **Application Load Balancer** +3. Delete **Target Group** +4. Delete **Launch Template** +5. Delete **CloudWatch Alarms and Dashboard** +6. Delete **VPC** (also removes subnets, IGW, route tables, security groups) +7. Delete **IAM Role** `ec2-webapp-role` +8. Delete **SNS Topic** `webapp-cpu-alert` + +--- + +## 📁 Repository Structure + +``` +aws-scalable-webapp-alb/ +├── index.html # Web app deployed on EC2 via nginx +├── README.md # This file +└── images/ # All build screenshots (1–70, in chronological order) +``` + +--- + +## Learning Journey + +Built as part of a hands-on AWS Cloud & DevOps learning journey. \ No newline at end of file diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-01.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-01.png new file mode 100644 index 0000000..9537721 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-01.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-02.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-02.png new file mode 100644 index 0000000..99d596a Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-02.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-03.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-03.png new file mode 100644 index 0000000..4cb4979 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-03.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-04.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-04.png new file mode 100644 index 0000000..23418b7 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-04.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-05.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-05.png new file mode 100644 index 0000000..2703dc5 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-05.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-06.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-06.png new file mode 100644 index 0000000..4d6aee5 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-06.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-07.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-07.png new file mode 100644 index 0000000..b51a347 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-07.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-08.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-08.png new file mode 100644 index 0000000..a79d7b9 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-08.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-09.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-09.png new file mode 100644 index 0000000..25960c8 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-09.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-10.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-10.png new file mode 100644 index 0000000..82e9668 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-10.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-11.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-11.png new file mode 100644 index 0000000..415f5b9 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-11.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-12.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-12.png new file mode 100644 index 0000000..9a3e0e4 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-12.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-13.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-13.png new file mode 100644 index 0000000..bf50ed6 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-13.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-14.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-14.png new file mode 100644 index 0000000..9680af3 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-14.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-15.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-15.png new file mode 100644 index 0000000..4e02f7c Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-15.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-16.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-16.png new file mode 100644 index 0000000..7f7a335 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-16.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-17.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-17.png new file mode 100644 index 0000000..29b6183 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-17.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-18.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-18.png new file mode 100644 index 0000000..6ce501d Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-18.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-19.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-19.png new file mode 100644 index 0000000..6952e05 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-19.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-20.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-20.png new file mode 100644 index 0000000..616a6c7 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-20.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-21.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-21.png new file mode 100644 index 0000000..c7191c1 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-21.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-22.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-22.png new file mode 100644 index 0000000..59cec7d Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-22.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-23.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-23.png new file mode 100644 index 0000000..a7fbc83 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-23.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-24.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-24.png new file mode 100644 index 0000000..a56a3c1 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-24.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-25.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-25.png new file mode 100644 index 0000000..fe8287a Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-25.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-26.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-26.png new file mode 100644 index 0000000..7de13a8 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-26.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-27.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-27.png new file mode 100644 index 0000000..a2910e6 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-27.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-28.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-28.png new file mode 100644 index 0000000..e47b459 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-28.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-29.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-29.png new file mode 100644 index 0000000..72f853a Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-29.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-30.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-30.png new file mode 100644 index 0000000..31eaf89 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-30.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-31.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-31.png new file mode 100644 index 0000000..ff89b1e Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-31.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-32.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-32.png new file mode 100644 index 0000000..538f3e6 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-32.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-33.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-33.png new file mode 100644 index 0000000..f58435c Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-33.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-34.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-34.png new file mode 100644 index 0000000..df6526e Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-34.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-35.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-35.png new file mode 100644 index 0000000..f0b0051 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-35.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-36.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-36.png new file mode 100644 index 0000000..3a58a7e Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-36.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-37.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-37.png new file mode 100644 index 0000000..433d53c Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-37.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-38.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-38.png new file mode 100644 index 0000000..6afcd61 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-38.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-39.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-39.png new file mode 100644 index 0000000..6e8cd70 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-39.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-40.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-40.png new file mode 100644 index 0000000..4ea8e3b Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-40.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-41.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-41.png new file mode 100644 index 0000000..7cd3633 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-41.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-42.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-42.png new file mode 100644 index 0000000..29c98de Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-42.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-43.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-43.png new file mode 100644 index 0000000..e71220b Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-43.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-44.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-44.png new file mode 100644 index 0000000..c365347 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-44.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-45.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-45.png new file mode 100644 index 0000000..8e81c20 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-45.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-46.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-46.png new file mode 100644 index 0000000..4358ac3 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-46.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-47.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-47.png new file mode 100644 index 0000000..fea64f3 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-47.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-48.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-48.png new file mode 100644 index 0000000..09186f5 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-48.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-49.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-49.png new file mode 100644 index 0000000..8329104 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-49.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-50.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-50.png new file mode 100644 index 0000000..7bab37b Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-50.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-51.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-51.png new file mode 100644 index 0000000..be21a85 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-51.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-52.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-52.png new file mode 100644 index 0000000..5a2e0ca Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-52.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-53.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-53.png new file mode 100644 index 0000000..44826ff Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-53.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-54.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-54.png new file mode 100644 index 0000000..90156ad Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-54.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-55.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-55.png new file mode 100644 index 0000000..90e5444 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-55.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-56.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-56.png new file mode 100644 index 0000000..e95cb3a Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-56.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-57.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-57.png new file mode 100644 index 0000000..776930b Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-57.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-58.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-58.png new file mode 100644 index 0000000..ec46915 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-58.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-59.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-59.png new file mode 100644 index 0000000..56a33ec Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-59.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-60.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-60.png new file mode 100644 index 0000000..b1a95ab Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-60.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-61.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-61.png new file mode 100644 index 0000000..8a22649 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-61.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-62.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-62.png new file mode 100644 index 0000000..837742c Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-62.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-63.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-63.png new file mode 100644 index 0000000..778ce07 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-63.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-64.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-64.png new file mode 100644 index 0000000..2ee7067 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-64.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-65.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-65.png new file mode 100644 index 0000000..062d7e0 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-65.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-66.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-66.png new file mode 100644 index 0000000..0dfd11c Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-66.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-67.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-67.png new file mode 100644 index 0000000..98bc162 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-67.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-68.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-68.png new file mode 100644 index 0000000..f2d018b Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-68.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-69.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-69.png new file mode 100644 index 0000000..884ff02 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-69.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-70.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-70.png new file mode 100644 index 0000000..568711b Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-70.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-71.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-71.png new file mode 100644 index 0000000..8484d0c Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-71.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-72.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-72.png new file mode 100644 index 0000000..3337ed8 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-72.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-73.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-73.png new file mode 100644 index 0000000..5063263 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-73.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-74.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-74.png new file mode 100644 index 0000000..de9e855 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-74.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-75.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-75.png new file mode 100644 index 0000000..f2d3b05 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-75.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-76.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-76.png new file mode 100644 index 0000000..bd5f269 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-76.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-77.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-77.png new file mode 100644 index 0000000..5d050f9 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-77.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-78.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-78.png new file mode 100644 index 0000000..751f1e3 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-78.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-79.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-79.png new file mode 100644 index 0000000..fb8c3a7 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-79.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-80.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-80.png new file mode 100644 index 0000000..58faed1 Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-80.png differ diff --git a/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-81.png b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-81.png new file mode 100644 index 0000000..7c2ae5e Binary files /dev/null and b/aws-scalable-webapp-alb/images/aws-scalable-webapp-alb-81.png differ diff --git a/aws-vpc-from-scratch/AWS-VPC-FROM-SCRATCH-README.md b/aws-vpc-from-scratch/AWS-VPC-FROM-SCRATCH-README.md new file mode 100644 index 0000000..e90e356 --- /dev/null +++ b/aws-vpc-from-scratch/AWS-VPC-FROM-SCRATCH-README.md @@ -0,0 +1,126 @@ +# AWS VPC From Scratch + +A production-style AWS Virtual Private Cloud (VPC) built +from scratch with public/private subnet isolation, +Bastion Host access pattern, NAT Gateway, +and multi-layer network security. + +--- +## 📸 Screenshots + +> All build screenshots are available in the [`/images`](./images) folder, numbered `1` through `36` in chronological build order. +--- + +### VPC Dashboard +![VPC](./images/aws-vpc-from-scratch-02.png) + +### SSH into Bastion +![Bastion SSH](./images/aws-vpc-from-scratch-23.png) + +### SSH Jump to Private EC2 +![Private EC2 SSH](./images/aws-vpc-from-scratch-24.png) + +### Internet Test +![Ping](./images/aws-vpc-from-scratch-25.png) +--- +## Architecture +``` +┌─────────────────────────────────────────────────┐ +│ VPC (10.0.0.0/16) │ +│ │ +│ ┌───────────────────────┐ │ +│ │ Public Subnet │ │ +│ │ 10.0.1.0/24 │ │ +│ │ │ │ +│ │ [Bastion Host EC2] │ │ +│ │ [NAT Gateway] │ │ +│ └──────────┬────────────┘ │ +│ │ SSH Jump │ +│ ┌──────────▼────────────┐ │ +│ │ Private Subnet │ │ +│ │ 10.0.2.0/24 │ │ +│ │ │ │ +│ │ [Private EC2] │ │ +│ │ (No Public IP) │ │ +│ └───────────────────────┘ │ +│ │ +│ public-rt → IGW (internet in + out) │ +│ private-rt → NAT (outbound only) │ +└─────────────────────┬───────────────────────────┘ + │ + [Internet Gateway] + │ + Internet +``` + +--- + +## AWS Services Used + +- VPC + Subnets (Public & Private) +- Internet Gateway +- NAT Gateway +- Route Tables +- Security Groups +- Network ACLs (NACLs) +- EC2 (Bastion Host + Private Instance) + +--- + +## Network Design + +| Component | Value | +|---|---| +| VPC CIDR | 10.0.0.0/16 | +| Public Subnet | 10.0.1.0/24 | +| Private Subnet | 10.0.2.0/24 | +| Bastion Host | Public Subnet — t2.micro | +| Private EC2 | Private Subnet — t2.micro (no public IP) | + +--- + +## Security Design + +### Security Groups (Instance Level) +| SG | Inbound Rule | +|---|---| +| bastion-sg | SSH port 22 from my IP only (/32) | +| private-ec2-sg | SSH port 22 from bastion-sg only | + +### NACLs (Subnet Level) +| NACL | Inbound Rules | +|---|---| +| public-nacl | Allow SSH (22) + Ephemeral ports (1024-65535) | +| private-nacl | Allow SSH from 10.0.1.0/24 + Ephemeral ports | + +--- + +## Key Concepts Demonstrated + +- Public vs Private subnet isolation +- Internet Gateway vs NAT Gateway +- Bastion Host / Jump Server pattern +- Stateful (SG) vs Stateless (NACL) firewalls +- Defense-in-depth network security +- CIDR block planning + +--- + +## Test Results + +| Test | Result | +|---|---| +| SSH into Bastion Host | ✅ | +| SSH jump to Private EC2 via Bastion | ✅ | +| ping google.com from Private EC2 | ✅ | +| Private EC2 unreachable from internet directly | ✅ | +| NACL IP block test | ✅ | + +--- +## 👨‍💻 Author + +**Aditya Nair** +- GitHub: [@ADITYANAIR01](https://github.com/ADITYANAIR01) +- LinkedIn: [linkedin.com/in/adityanair001](https://www.linkedin.com/in/adityanair001) + +--- \ No newline at end of file diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-01.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-01.png new file mode 100644 index 0000000..13acfd6 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-01.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-02.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-02.png new file mode 100644 index 0000000..4e1209b Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-02.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-03.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-03.png new file mode 100644 index 0000000..feb5441 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-03.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-04.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-04.png new file mode 100644 index 0000000..fb22f0f Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-04.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-05.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-05.png new file mode 100644 index 0000000..48f29aa Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-05.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-06.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-06.png new file mode 100644 index 0000000..968ee81 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-06.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-07.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-07.png new file mode 100644 index 0000000..76b1351 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-07.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-08.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-08.png new file mode 100644 index 0000000..ab04dd1 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-08.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-09.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-09.png new file mode 100644 index 0000000..4c03ddd Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-09.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-10.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-10.png new file mode 100644 index 0000000..ef9342a Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-10.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-11.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-11.png new file mode 100644 index 0000000..f72a138 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-11.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-12.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-12.png new file mode 100644 index 0000000..5f7f215 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-12.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-13.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-13.png new file mode 100644 index 0000000..c3bc128 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-13.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-14.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-14.png new file mode 100644 index 0000000..bb0977e Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-14.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-15.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-15.png new file mode 100644 index 0000000..76ba430 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-15.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-16.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-16.png new file mode 100644 index 0000000..d82ad3d Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-16.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-17.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-17.png new file mode 100644 index 0000000..658f4b0 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-17.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-18.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-18.png new file mode 100644 index 0000000..a55d5f0 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-18.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-19.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-19.png new file mode 100644 index 0000000..e7141a2 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-19.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-20.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-20.png new file mode 100644 index 0000000..e656f62 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-20.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-21.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-21.png new file mode 100644 index 0000000..5a19305 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-21.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-22.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-22.png new file mode 100644 index 0000000..1fd6692 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-22.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-23.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-23.png new file mode 100644 index 0000000..085a0f6 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-23.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-24.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-24.png new file mode 100644 index 0000000..d183328 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-24.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-25.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-25.png new file mode 100644 index 0000000..ad9ed2e Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-25.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-26.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-26.png new file mode 100644 index 0000000..4722093 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-26.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-27.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-27.png new file mode 100644 index 0000000..82ac34f Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-27.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-28.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-28.png new file mode 100644 index 0000000..186bf43 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-28.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-29.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-29.png new file mode 100644 index 0000000..2cbac9f Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-29.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-30.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-30.png new file mode 100644 index 0000000..5b21cf8 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-30.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-31.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-31.png new file mode 100644 index 0000000..1ed4d41 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-31.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-32.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-32.png new file mode 100644 index 0000000..f2a7e16 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-32.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-33.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-33.png new file mode 100644 index 0000000..a029c0d Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-33.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-34.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-34.png new file mode 100644 index 0000000..9a901e5 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-34.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-35.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-35.png new file mode 100644 index 0000000..3f8d6f1 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-35.png differ diff --git a/aws-vpc-from-scratch/images/aws-vpc-from-scratch-36.png b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-36.png new file mode 100644 index 0000000..f4b83e7 Binary files /dev/null and b/aws-vpc-from-scratch/images/aws-vpc-from-scratch-36.png differ diff --git a/mini-dropbox/CODE/.env.example b/mini-dropbox/CODE/.env.example new file mode 100644 index 0000000..4bb285e --- /dev/null +++ b/mini-dropbox/CODE/.env.example @@ -0,0 +1,16 @@ +FLASK_SECRET_KEY=your-secret-key-here +FLASK_ENV=production + +GOOGLE_CLIENT_ID=your-google-client-id +GOOGLE_CLIENT_SECRET=your-google-client-secret +GOOGLE_REDIRECT_URI=http://your-ec2-public-ip/auth/callback + +AWS_REGION=us-east-1 +S3_BUCKET_NAME=your-s3-bucket-name +CLOUDFRONT_DOMAIN=your-cloudfront-domain.cloudfront.net + +DB_HOST=your-rds-endpoint.amazonaws.com +DB_PORT=5432 +DB_NAME=clouddrive +DB_USER=dbadmin +DB_PASSWORD=your-db-password diff --git a/mini-dropbox/CODE/README.md b/mini-dropbox/CODE/README.md new file mode 100644 index 0000000..6e241bb --- /dev/null +++ b/mini-dropbox/CODE/README.md @@ -0,0 +1,375 @@ +# AWS Cloud Drive + +A modern, full-stack cloud file storage application — a mini Dropbox clone built with Python Flask and AWS services. Users can sign in with Google, upload files to S3, manage them via a beautiful dark-themed UI, and share files with others. + +![AWS Cloud Drive](https://img.shields.io/badge/AWS-Cloud%20Drive-blue) +![Python](https://img.shields.io/badge/Python-3.11-green) +![Flask](https://img.shields.io/badge/Flask-3.0-red) +![License](https://img.shields.io/badge/License-MIT-yellow) + +--- + +## 🚀 Features + +- **Google OAuth Authentication** — Secure sign-in with Google accounts +- **File Upload & Storage** — Upload files up to 100MB, stored securely on AWS S3 +- **File Management** — View, download, delete, and organize your files +- **File Sharing** — Generate shareable links with 7-day expiration +- **File Preview** — Preview images and PDFs directly in browser +- **Modern UI** — Dark theme with smooth animations and responsive design +- **Cloud Infrastructure** — Built on AWS S3, RDS PostgreSQL, and CloudFront + +--- + +## 🏗️ Tech Stack + +### Backend +- **Python 3.11** — Core programming language +- **Flask 3.0** — Web framework +- **PostgreSQL (RDS)** — Relational database for metadata +- **AWS S3** — Object storage for files +- **AWS CloudFront** — CDN for fast file delivery +- **Gunicorn** — Production WSGI server + +### Frontend +- **HTML5** — Semantic markup +- **Tailwind CSS** — Utility-first CSS framework +- **Vanilla JavaScript** — No frameworks, pure JS + +### Authentication +- **Google OAuth 2.0** — Via Authlib + +### Python Libraries +- `Flask` — Web framework +- `Authlib` — OAuth client +- `boto3` — AWS SDK for Python +- `psycopg2` — PostgreSQL adapter +- `python-dotenv` — Environment variable management +- `gunicorn` — Production server + +--- + +## 📁 Project Structure + +``` +aws-cloud-drive/ +│ +├── app/ +│ ├── __init__.py # Flask app factory +│ ├── config.py # Configuration loader +│ ├── auth.py # Google OAuth blueprint +│ ├── files.py # File management blueprint +│ ├── db.py # Database operations +│ ├── s3.py # S3 operations +│ │ +│ ├── templates/ +│ │ ├── base.html # Base template with navbar +│ │ ├── login.html # Login page +│ │ └── dashboard.html # Main dashboard +│ │ +│ └── static/ +│ ├── css/ +│ │ └── style.css # Custom styles +│ └── js/ +│ └── dashboard.js # Frontend logic +│ +├── sql/ +│ └── schema.sql # Database schema +│ +├── .env.example # Environment variables template +├── requirements.txt # Python dependencies +├── run.py # Application entry point +├── gunicorn.conf.py # Gunicorn configuration +└── README.md # This file +``` + +--- + +## 🔧 Setup Instructions + +### Prerequisites + +- Python 3.11+ +- PostgreSQL database (AWS RDS recommended) +- AWS account with S3 bucket +- Google Cloud project with OAuth credentials + +### 1. Clone the Repository + +```bash +git clone https://github.com/yourusername/aws-cloud-drive.git +cd aws-cloud-drive +``` + +### 2. Install Dependencies + +```bash +pip install -r requirements.txt +``` + +### 3. Configure Environment Variables + +Copy `.env.example` to `.env` and fill in your credentials: + +```bash +cp .env.example .env +``` + +Edit `.env`: + +```env +FLASK_SECRET_KEY=your-secret-key-here +FLASK_ENV=production + +GOOGLE_CLIENT_ID=your-google-client-id +GOOGLE_CLIENT_SECRET=your-google-client-secret +GOOGLE_REDIRECT_URI=http://your-domain.com/auth/callback + +AWS_REGION=us-east-1 +S3_BUCKET_NAME=your-s3-bucket-name +CLOUDFRONT_DOMAIN=your-cloudfront-domain.cloudfront.net + +DB_HOST=your-rds-endpoint.amazonaws.com +DB_PORT=5432 +DB_NAME=clouddrive +DB_USER=dbadmin +DB_PASSWORD=your-db-password +``` + +### 4. Set Up AWS Resources + +#### S3 Bucket +1. Create an S3 bucket in your AWS console +2. Note the bucket name for `.env` +3. Configure IAM role with S3 permissions + +#### RDS PostgreSQL +1. Create a PostgreSQL instance in RDS +2. Note the endpoint, port, database name, and credentials +3. Ensure security group allows connections from your EC2 instance + +#### CloudFront (Optional) +1. Create a CloudFront distribution pointing to your S3 bucket +2. Note the CloudFront domain for `.env` + +#### EC2 IAM Role +Attach an IAM role to your EC2 instance with these policies: +- `AmazonS3FullAccess` (or custom S3 policy) +- `AmazonRDSDataFullAccess` (or custom RDS policy) + +### 5. Set Up Google OAuth + +1. Go to [Google Cloud Console](https://console.cloud.google.com/) +2. Create a new project +3. Enable Google+ API +4. Create OAuth 2.0 credentials: + - Application type: Web application + - Authorized redirect URIs: `http://your-domain.com/auth/callback` +5. Copy Client ID and Client Secret to `.env` + +### 6. Initialize Database + +The database will be initialized automatically on first run, or manually: + +```bash +python -c "from app.db import init_db; init_db()" +``` + +### 7. Run the Application + +#### Development + +```bash +python run.py +``` + +#### Production (with Gunicorn) + +```bash +gunicorn -c gunicorn.conf.py run:app +``` + +--- + +## 🚀 Deployment on AWS EC2 + +### 1. Launch EC2 Instance + +- **AMI:** Ubuntu 22.04 LTS +- **Instance Type:** t2.micro (free tier) or t2.small +- **Security Group:** + - Allow SSH (port 22) + - Allow HTTP (port 80) + - Allow HTTPS (port 443) + - Allow Custom TCP (port 5000) for testing + +### 2. SSH into Instance + +```bash +ssh -i your-key.pem ubuntu@your-ec2-public-ip +``` + +### 3. Install Dependencies + +```bash +sudo apt update +sudo apt install python3-pip python3-venv nginx -y +``` + +### 4. Clone and Set Up Application + +```bash +git clone https://github.com/yourusername/aws-cloud-drive.git +cd aws-cloud-drive +python3 -m venv venv +source venv/bin/activate +pip install -r requirements.txt +``` + +### 5. Configure Nginx (Optional) + +Create `/etc/nginx/sites-available/clouddrive`: + +```nginx +server { + listen 80; + server_name your-domain.com; + + location / { + proxy_pass http://127.0.0.1:5000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} +``` + +Enable the site: + +```bash +sudo ln -s /etc/nginx/sites-available/clouddrive /etc/nginx/sites-enabled/ +sudo nginx -t +sudo systemctl restart nginx +``` + +### 6. Run with Gunicorn + +Install and configure Gunicorn: + +```bash +pip install gunicorn +``` + +Create a systemd service `/etc/systemd/system/clouddrive.service`: + +```ini +[Unit] +Description=Cloud Drive Flask App +After=network.target + +[Service] +User=ubuntu +WorkingDirectory=/home/ubuntu/aws-cloud-drive +Environment="PATH=/home/ubuntu/aws-cloud-drive/venv/bin" +ExecStart=/home/ubuntu/aws-cloud-drive/venv/bin/gunicorn -c gunicorn.conf.py run:app + +[Install] +WantedBy=multi-user.target +``` + +Start the service: + +```bash +sudo systemctl daemon-reload +sudo systemctl start clouddrive +sudo systemctl enable clouddrive +``` + +--- + +## 🔒 Security Best Practices + +1. **Never commit `.env` file** — Use `.env.example` as a template +2. **Use IAM roles** — Avoid hardcoding AWS credentials +3. **HTTPS only** — Use SSL certificates (Let's Encrypt) +4. **Secure session cookies** — Already configured in app +5. **File size limits** — 100MB enforced +6. **Input validation** — Filenames sanitized with `secure_filename` +7. **User isolation** — All file operations verify ownership + +--- + +## 📝 API Endpoints + +### Authentication +- `GET /auth/login` — Redirect to Google OAuth +- `GET /auth/callback` — Handle OAuth callback +- `GET /auth/logout` — Log out user + +### Files +- `GET /files/` — Dashboard (requires login) +- `POST /files/upload` — Upload file (requires login) +- `GET /files/download/` — Download file (requires login) +- `POST /files/delete/` — Delete file (requires login) +- `POST /files/share/` — Generate share link (requires login) +- `GET /files/preview/` — Preview file (requires login) + +--- + +## 🎨 UI Screenshots + +### Login Page +Clean, modern login with Google OAuth + +### Dashboard +Dark-themed file manager with upload, download, share, and delete actions + +--- + +## 🛠️ Development + +### Run in Development Mode + +```bash +export FLASK_ENV=development +python run.py +``` + +### Database Schema + +The schema is defined in `sql/schema.sql`: + +- **users** — User accounts from Google OAuth +- **files** — File metadata with S3 keys and share tokens + +--- + +## 📄 License + +This project is licensed under the MIT License. + +--- + +## 🤝 Contributing + +Contributions are welcome! Please open an issue or submit a pull request. + +--- + +## 📧 Contact + +For questions or support, please open an issue on GitHub. + +--- + +## 🙏 Acknowledgments + +- Flask for the excellent web framework +- AWS for reliable cloud infrastructure +- Tailwind CSS for beautiful styling +- Google OAuth for secure authentication + +--- + +**Built with ❤️ using Python, Flask, and AWS** diff --git a/mini-dropbox/CODE/app/__init__.py b/mini-dropbox/CODE/app/__init__.py new file mode 100644 index 0000000..1bf54bc --- /dev/null +++ b/mini-dropbox/CODE/app/__init__.py @@ -0,0 +1,40 @@ +# === FILE: app/__init__.py === +from flask import Flask, render_template +from app.config import Config + + +def create_app(): + """Flask application factory.""" + app = Flask(__name__) + + # Load configuration + app.config['SECRET_KEY'] = Config.FLASK_SECRET_KEY + app.config['SESSION_COOKIE_HTTPONLY'] = True + app.config['SESSION_COOKIE_SAMESITE'] = 'Lax' + + # Initialize OAuth + from app.auth import init_oauth + init_oauth(app) + + # Register blueprints + from app.auth import auth_bp + from app.files import files_bp + + app.register_blueprint(auth_bp) + app.register_blueprint(files_bp) + + # Root route - redirect to login page + @app.route('/') + def index(): + return render_template('login.html') + + # Error handlers + @app.errorhandler(404) + def not_found(e): + return render_template('login.html'), 404 + + @app.errorhandler(500) + def server_error(e): + return "Internal Server Error", 500 + + return app diff --git a/mini-dropbox/CODE/app/auth.py b/mini-dropbox/CODE/app/auth.py new file mode 100644 index 0000000..7eeeaed --- /dev/null +++ b/mini-dropbox/CODE/app/auth.py @@ -0,0 +1,97 @@ +# === FILE: app/auth.py === +from functools import wraps +from flask import Blueprint, session, redirect, url_for, request, flash +from authlib.integrations.flask_client import OAuth +from app.config import Config +from app import db + + +auth_bp = Blueprint('auth', __name__, url_prefix='/auth') + +# OAuth client will be initialized in __init__.py +oauth = None + + +def init_oauth(app): + """Initialize OAuth with the Flask app.""" + global oauth + oauth = OAuth(app) + oauth.register( + name='google', + client_id=Config.GOOGLE_CLIENT_ID, + client_secret=Config.GOOGLE_CLIENT_SECRET, + server_metadata_url='https://accounts.google.com/.well-known/openid-configuration', + client_kwargs={ + 'scope': 'openid email profile' + } + ) + + +def login_required(f): + """Decorator to protect routes that require authentication.""" + @wraps(f) + def decorated_function(*args, **kwargs): + if 'user' not in session: + flash('Please log in to access this page.', 'error') + return redirect(url_for('auth.login')) + return f(*args, **kwargs) + return decorated_function + + +@auth_bp.route('/login') +def login(): + """Redirect to Google OAuth login page.""" + redirect_uri = Config.GOOGLE_REDIRECT_URI + return oauth.google.authorize_redirect(redirect_uri) + + +@auth_bp.route('/callback') +def callback(): + """Handle Google OAuth callback.""" + try: + # Exchange authorization code for access token + token = oauth.google.authorize_access_token() + + # Get user info from Google + userinfo = token.get('userinfo') + + if not userinfo: + flash('Failed to get user information from Google.', 'error') + return redirect(url_for('auth.login')) + + # Extract user data + google_id = userinfo.get('sub') + email = userinfo.get('email') + name = userinfo.get('name') + profile_picture = userinfo.get('picture') + + # Get or create user in database + user = db.get_or_create_user(google_id, email, name, profile_picture) + + if not user: + flash('Failed to create user account.', 'error') + return redirect(url_for('auth.login')) + + # Store user in session + session['user'] = { + 'id': user['id'], + 'email': user['email'], + 'name': user['name'], + 'profile_picture': user['profile_picture'] + } + + flash(f'Welcome back, {user["name"]}!', 'success') + return redirect(url_for('files.dashboard')) + + except Exception as e: + print(f"Error during OAuth callback: {e}") + flash('An error occurred during login. Please try again.', 'error') + return redirect(url_for('auth.login')) + + +@auth_bp.route('/logout') +def logout(): + """Log out the current user.""" + session.clear() + flash('You have been logged out successfully.', 'success') + return redirect(url_for('auth.login')) diff --git a/mini-dropbox/CODE/app/config.py b/mini-dropbox/CODE/app/config.py new file mode 100644 index 0000000..10935f7 --- /dev/null +++ b/mini-dropbox/CODE/app/config.py @@ -0,0 +1,27 @@ +# === FILE: app/config.py === +import os + + +class Config: + """Application configuration loaded from environment variables.""" + + # Flask settings + FLASK_SECRET_KEY = os.environ.get('FLASK_SECRET_KEY', 'dev-secret-key-change-in-production') + FLASK_ENV = os.environ.get('FLASK_ENV', 'production') + + # Google OAuth settings + GOOGLE_CLIENT_ID = os.environ.get('GOOGLE_CLIENT_ID') + GOOGLE_CLIENT_SECRET = os.environ.get('GOOGLE_CLIENT_SECRET') + GOOGLE_REDIRECT_URI = os.environ.get('GOOGLE_REDIRECT_URI') + + # AWS settings + AWS_REGION = os.environ.get('AWS_REGION', 'us-east-1') + S3_BUCKET_NAME = os.environ.get('S3_BUCKET_NAME') + CLOUDFRONT_DOMAIN = os.environ.get('CLOUDFRONT_DOMAIN') + + # Database settings + DB_HOST = os.environ.get('DB_HOST') + DB_PORT = os.environ.get('DB_PORT', '5432') + DB_NAME = os.environ.get('DB_NAME', 'clouddrive') + DB_USER = os.environ.get('DB_USER') + DB_PASSWORD = os.environ.get('DB_PASSWORD') diff --git a/mini-dropbox/CODE/app/db.py b/mini-dropbox/CODE/app/db.py new file mode 100644 index 0000000..5faa651 --- /dev/null +++ b/mini-dropbox/CODE/app/db.py @@ -0,0 +1,270 @@ +# === FILE: app/db.py === +import psycopg +from psycopg.rows import dict_row +from app.config import Config + + +def get_connection(): + """Create and return a PostgreSQL database connection.""" + conn = psycopg.connect( + host=Config.DB_HOST, + port=Config.DB_PORT, + dbname=Config.DB_NAME, + user=Config.DB_USER, + password=Config.DB_PASSWORD + ) + return conn + + +def init_db(): + """Initialize the database by creating tables from schema.sql.""" + conn = None + try: + conn = get_connection() + cursor = conn.cursor() + + # Read and execute schema.sql + with open('sql/schema.sql', 'r') as f: + schema = f.read() + cursor.execute(schema) + + conn.commit() + cursor.close() + print("Database initialized successfully") + except Exception as e: + print(f"Error initializing database: {e}") + if conn: + conn.rollback() + raise + finally: + if conn: + conn.close() + + +def get_user_by_google_id(google_id): + """Fetch user by Google ID.""" + conn = None + try: + conn = get_connection() + cursor = conn.cursor(row_factory=dict_row) + + cursor.execute( + "SELECT * FROM users WHERE google_id = %s", + (google_id,) + ) + user = cursor.fetchone() + cursor.close() + + return dict(user) if user else None + except Exception as e: + print(f"Error fetching user: {e}") + return None + finally: + if conn: + conn.close() + + +def create_user(google_id, email, name, profile_picture): + """Create a new user and return user dict.""" + conn = None + try: + conn = get_connection() + cursor = conn.cursor(row_factory=dict_row) + + cursor.execute( + """ + INSERT INTO users (google_id, email, name, profile_picture) + VALUES (%s, %s, %s, %s) + RETURNING * + """, + (google_id, email, name, profile_picture) + ) + + user = cursor.fetchone() + conn.commit() + cursor.close() + + return dict(user) if user else None + except Exception as e: + print(f"Error creating user: {e}") + if conn: + conn.rollback() + raise + finally: + if conn: + conn.close() + + +def get_or_create_user(google_id, email, name, profile_picture): + """Get existing user or create new one (upsert logic).""" + user = get_user_by_google_id(google_id) + + if user: + return user + else: + return create_user(google_id, email, name, profile_picture) + + +def get_files_by_user(user_id): + """Get all files for a user, ordered by upload date (newest first).""" + conn = None + try: + conn = get_connection() + cursor = conn.cursor(row_factory=dict_row) + + cursor.execute( + """ + SELECT * FROM files + WHERE user_id = %s + ORDER BY uploaded_at DESC + """, + (user_id,) + ) + + files = cursor.fetchall() + cursor.close() + + return [dict(f) for f in files] if files else [] + except Exception as e: + print(f"Error fetching files: {e}") + return [] + finally: + if conn: + conn.close() + + +def add_file(user_id, filename, original_name, s3_key, file_size, file_type): + """Add a new file record to the database.""" + conn = None + try: + conn = get_connection() + cursor = conn.cursor(row_factory=dict_row) + + cursor.execute( + """ + INSERT INTO files (user_id, filename, original_name, s3_key, file_size, file_type) + VALUES (%s, %s, %s, %s, %s, %s) + RETURNING * + """, + (user_id, filename, original_name, s3_key, file_size, file_type) + ) + + file = cursor.fetchone() + conn.commit() + cursor.close() + + return dict(file) if file else None + except Exception as e: + print(f"Error adding file: {e}") + if conn: + conn.rollback() + raise + finally: + if conn: + conn.close() + + +def delete_file(file_id, user_id): + """Delete a file record (with ownership verification).""" + conn = None + try: + conn = get_connection() + cursor = conn.cursor() + + cursor.execute( + "DELETE FROM files WHERE id = %s AND user_id = %s", + (file_id, user_id) + ) + + deleted_count = cursor.rowcount + conn.commit() + cursor.close() + + return deleted_count > 0 + except Exception as e: + print(f"Error deleting file: {e}") + if conn: + conn.rollback() + return False + finally: + if conn: + conn.close() + + +def get_file(file_id, user_id): + """Get a single file by ID (with ownership verification).""" + conn = None + try: + conn = get_connection() + cursor = conn.cursor(row_factory=dict_row) + + cursor.execute( + "SELECT * FROM files WHERE id = %s AND user_id = %s", + (file_id, user_id) + ) + + file = cursor.fetchone() + cursor.close() + + return dict(file) if file else None + except Exception as e: + print(f"Error fetching file: {e}") + return None + finally: + if conn: + conn.close() + + +def set_share_token(file_id, user_id, token): + """Set a share token for a file (with ownership verification).""" + conn = None + try: + conn = get_connection() + cursor = conn.cursor() + + cursor.execute( + """ + UPDATE files + SET share_token = %s + WHERE id = %s AND user_id = %s + """, + (token, file_id, user_id) + ) + + updated_count = cursor.rowcount + conn.commit() + cursor.close() + + return updated_count > 0 + except Exception as e: + print(f"Error setting share token: {e}") + if conn: + conn.rollback() + return False + finally: + if conn: + conn.close() + + +def get_file_by_share_token(token): + """Get a file by its share token (no user_id check for public access).""" + conn = None + try: + conn = get_connection() + cursor = conn.cursor(row_factory=dict_row) + + cursor.execute( + "SELECT * FROM files WHERE share_token = %s", + (token,) + ) + + file = cursor.fetchone() + cursor.close() + + return dict(file) if file else None + except Exception as e: + print(f"Error fetching file by share token: {e}") + return None + finally: + if conn: + conn.close() diff --git a/mini-dropbox/CODE/app/files.py b/mini-dropbox/CODE/app/files.py new file mode 100644 index 0000000..04ec568 --- /dev/null +++ b/mini-dropbox/CODE/app/files.py @@ -0,0 +1,258 @@ +# === FILE: app/files.py === +import os +import uuid +import secrets +from datetime import datetime +from flask import Blueprint, render_template, request, jsonify, session, redirect, url_for +from werkzeug.utils import secure_filename +from app.auth import login_required +from app import db, s3 + + +files_bp = Blueprint('files', __name__, url_prefix='/files') + + +# Maximum file size: 100MB +MAX_FILE_SIZE = 100 * 1024 * 1024 # 100 MB in bytes + + +def format_file_size(size_bytes): + """Convert bytes to human-readable format.""" + if size_bytes < 1024: + return f"{size_bytes} B" + elif size_bytes < 1024 * 1024: + return f"{size_bytes / 1024:.2f} KB" + elif size_bytes < 1024 * 1024 * 1024: + return f"{size_bytes / (1024 * 1024):.2f} MB" + else: + return f"{size_bytes / (1024 * 1024 * 1024):.2f} GB" + + +def get_file_category(file_type): + """Determine file category based on MIME type.""" + if not file_type: + return 'other' + + file_type = file_type.lower() + + if file_type.startswith('image/'): + return 'image' + elif file_type == 'application/pdf': + return 'pdf' + elif file_type.startswith('video/'): + return 'video' + elif file_type.startswith('audio/'): + return 'audio' + elif file_type in ['application/zip', 'application/x-zip-compressed', 'application/x-rar-compressed', 'application/x-7z-compressed']: + return 'archive' + elif file_type in ['application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'text/plain', 'text/csv']: + return 'document' + else: + return 'other' + + +def format_date(dt): + """Format datetime to readable string.""" + if not dt: + return '' + + if isinstance(dt, str): + try: + dt = datetime.fromisoformat(dt) + except: + return dt + + return dt.strftime('%b %d, %Y') + + +@files_bp.route('/') +@login_required +def dashboard(): + """Display the main dashboard with user's files.""" + user_id = session['user']['id'] + + # Get all files for the user + files = db.get_files_by_user(user_id) + + # Enhance file data with formatted information + for file in files: + file['size_formatted'] = format_file_size(file['file_size'] or 0) + file['category'] = get_file_category(file['file_type']) + file['date_formatted'] = format_date(file['uploaded_at']) + + return render_template( + 'dashboard.html', + user=session['user'], + files=files + ) + + +@files_bp.route('/upload', methods=['POST']) +@login_required +def upload(): + """Handle file upload.""" + try: + # Check if file is present + if 'file' not in request.files: + return jsonify({'success': False, 'message': 'No file provided'}), 400 + + file = request.files['file'] + + if file.filename == '': + return jsonify({'success': False, 'message': 'No file selected'}), 400 + + # Get file size + file.seek(0, os.SEEK_END) + file_size = file.tell() + file.seek(0) + + # Validate file size + if file_size > MAX_FILE_SIZE: + return jsonify({'success': False, 'message': 'File size exceeds 100MB limit'}), 400 + + # Get user ID + user_id = session['user']['id'] + + # Secure the filename + original_name = file.filename + safe_filename = secure_filename(original_name) + + # Generate unique S3 key + unique_id = uuid.uuid4() + s3_key = f"{user_id}/{unique_id}_{safe_filename}" + + # Get content type + content_type = file.content_type or 'application/octet-stream' + + # Upload to S3 + s3.upload_file(file, s3_key, content_type) + + # Save metadata to database + db.add_file( + user_id=user_id, + filename=safe_filename, + original_name=original_name, + s3_key=s3_key, + file_size=file_size, + file_type=content_type + ) + + return jsonify({'success': True, 'message': 'File uploaded successfully'}), 200 + + except Exception as e: + print(f"Error uploading file: {e}") + return jsonify({'success': False, 'message': 'Failed to upload file'}), 500 + + +@files_bp.route('/download/') +@login_required +def download(file_id): + """Generate presigned URL and redirect for file download.""" + try: + user_id = session['user']['id'] + + # Get file from database (verifies ownership) + file = db.get_file(file_id, user_id) + + if not file: + return jsonify({'success': False, 'message': 'File not found'}), 404 + + # Generate presigned URL (15 minutes) + presigned_url = s3.generate_presigned_url(file['s3_key'], expiry=900) + + # Redirect to presigned URL + return redirect(presigned_url) + + except Exception as e: + print(f"Error downloading file: {e}") + return jsonify({'success': False, 'message': 'Failed to download file'}), 500 + + +@files_bp.route('/delete/', methods=['POST']) +@login_required +def delete(file_id): + """Delete a file.""" + try: + user_id = session['user']['id'] + + # Get file from database (verifies ownership) + file = db.get_file(file_id, user_id) + + if not file: + return jsonify({'success': False, 'message': 'File not found'}), 404 + + # Delete from S3 + s3.delete_file(file['s3_key']) + + # Delete from database + db.delete_file(file_id, user_id) + + return jsonify({'success': True, 'message': 'File deleted successfully'}), 200 + + except Exception as e: + print(f"Error deleting file: {e}") + return jsonify({'success': False, 'message': 'Failed to delete file'}), 500 + + +@files_bp.route('/share/', methods=['POST']) +@login_required +def share(file_id): + """Generate a shareable link for a file.""" + try: + user_id = session['user']['id'] + + # Get file from database (verifies ownership) + file = db.get_file(file_id, user_id) + + if not file: + return jsonify({'success': False, 'message': 'File not found'}), 404 + + # Generate share token + share_token = secrets.token_urlsafe(32) + + # Save token to database + db.set_share_token(file_id, user_id, share_token) + + # Generate 7-day presigned URL + share_url = s3.generate_share_url(file['s3_key'], expiry=604800) + + return jsonify({ + 'success': True, + 'share_url': share_url + }), 200 + + except Exception as e: + print(f"Error sharing file: {e}") + return jsonify({'success': False, 'message': 'Failed to generate share link'}), 500 + + +@files_bp.route('/preview/') +@login_required +def preview(file_id): + """Generate a preview URL for images and PDFs.""" + try: + user_id = session['user']['id'] + + # Get file from database (verifies ownership) + file = db.get_file(file_id, user_id) + + if not file: + return jsonify({'success': False, 'message': 'File not found'}), 404 + + # Check if file is previewable (image or PDF) + file_type = file.get('file_type', '').lower() + + if file_type.startswith('image/') or file_type == 'application/pdf': + # Return CloudFront URL for preview + preview_url = s3.get_cloudfront_url(file['s3_key']) + return jsonify({'success': True, 'preview_url': preview_url}), 200 + else: + # Not previewable, redirect to download + return redirect(url_for('files.download', file_id=file_id)) + + except Exception as e: + print(f"Error generating preview: {e}") + return jsonify({'success': False, 'message': 'Failed to generate preview'}), 500 diff --git a/mini-dropbox/CODE/app/s3.py b/mini-dropbox/CODE/app/s3.py new file mode 100644 index 0000000..5930d58 --- /dev/null +++ b/mini-dropbox/CODE/app/s3.py @@ -0,0 +1,123 @@ +# === FILE: app/s3.py === +import boto3 +from botocore.exceptions import ClientError +from app.config import Config + + +def get_s3_client(): + """Create and return an S3 client using IAM role credentials.""" + return boto3.client('s3', region_name=Config.AWS_REGION) + + +def upload_file(file_obj, s3_key, content_type): + """ + Upload a file to S3. + + Args: + file_obj: File object to upload + s3_key: S3 key (path) for the file + content_type: MIME type of the file + + Returns: + True if successful, raises exception otherwise + """ + try: + s3_client = get_s3_client() + + s3_client.upload_fileobj( + file_obj, + Config.S3_BUCKET_NAME, + s3_key, + ExtraArgs={ + 'ContentType': content_type + } + ) + + return True + except ClientError as e: + print(f"Error uploading file to S3: {e}") + raise + + +def delete_file(s3_key): + """ + Delete a file from S3. + + Args: + s3_key: S3 key (path) of the file to delete + + Returns: + True if successful, False otherwise + """ + try: + s3_client = get_s3_client() + + s3_client.delete_object( + Bucket=Config.S3_BUCKET_NAME, + Key=s3_key + ) + + return True + except ClientError as e: + print(f"Error deleting file from S3: {e}") + return False + + +def generate_presigned_url(s3_key, expiry=900): + """ + Generate a presigned URL for downloading a file. + + Args: + s3_key: S3 key (path) of the file + expiry: Expiration time in seconds (default 15 minutes) + + Returns: + Presigned URL string + """ + try: + s3_client = get_s3_client() + + url = s3_client.generate_presigned_url( + 'get_object', + Params={ + 'Bucket': Config.S3_BUCKET_NAME, + 'Key': s3_key + }, + ExpiresIn=expiry + ) + + return url + except ClientError as e: + print(f"Error generating presigned URL: {e}") + raise + + +def generate_share_url(s3_key, expiry=604800): + """ + Generate a presigned URL for sharing a file (valid for 7 days). + + Args: + s3_key: S3 key (path) of the file + expiry: Expiration time in seconds (default 7 days) + + Returns: + Presigned URL string + """ + return generate_presigned_url(s3_key, expiry) + + +def get_cloudfront_url(s3_key): + """ + Generate a CloudFront URL for a file (for preview purposes). + + Args: + s3_key: S3 key (path) of the file + + Returns: + CloudFront URL string + """ + if Config.CLOUDFRONT_DOMAIN: + return f"https://{Config.CLOUDFRONT_DOMAIN}/{s3_key}" + else: + # Fallback to S3 URL if CloudFront is not configured + return f"https://{Config.S3_BUCKET_NAME}.s3.{Config.AWS_REGION}.amazonaws.com/{s3_key}" diff --git a/mini-dropbox/CODE/app/static/css/style.css b/mini-dropbox/CODE/app/static/css/style.css new file mode 100644 index 0000000..91420b1 --- /dev/null +++ b/mini-dropbox/CODE/app/static/css/style.css @@ -0,0 +1,226 @@ +/* === FILE: app/static/css/style.css === */ + +/* Custom scrollbar */ +::-webkit-scrollbar { + width: 12px; + height: 12px; +} + +::-webkit-scrollbar-track { + background: #0f1117; + border-radius: 10px; +} + +::-webkit-scrollbar-thumb { + background: linear-gradient(180deg, #6366f1, #8b5cf6); + border-radius: 10px; + border: 2px solid #0f1117; +} + +::-webkit-scrollbar-thumb:hover { + background: linear-gradient(180deg, #4f46e5, #7c3aed); +} + +/* Drop zone states */ +#drop-zone { + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +#drop-zone.drag-over { + border-color: #6366f1 !important; + background-color: rgba(99, 102, 241, 0.1) !important; + transform: scale(1.02); +} + +/* File card hover effect */ +.file-card { + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +.file-card:hover { + transform: translateY(-4px) scale(1.02); +} + +/* Progress bar animation */ +#progress-bar { + transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1); + position: relative; + overflow: hidden; +} + +#progress-bar::after { + content: ""; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + background: linear-gradient( + 90deg, + transparent, + rgba(255, 255, 255, 0.3), + transparent + ); + animation: shimmer 2s infinite; +} + +@keyframes shimmer { + 0% { + transform: translateX(-100%); + } + 100% { + transform: translateX(100%); + } +} + +/* Button loading spinner */ +.btn-loading { + position: relative; + pointer-events: none; + opacity: 0.7; +} + +.btn-loading::after { + content: ""; + position: absolute; + width: 20px; + height: 20px; + top: 50%; + left: 50%; + margin-left: -10px; + margin-top: -10px; + border: 3px solid rgba(255, 255, 255, 0.3); + border-radius: 50%; + border-top-color: #ffffff; + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +/* Modal animations */ +.modal-enter { + animation: modalFadeIn 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +@keyframes modalFadeIn { + from { + opacity: 0; + transform: scale(0.95) translateY(20px); + } + to { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +.modal-exit { + animation: modalFadeOut 0.2s cubic-bezier(0.4, 0, 1, 1); +} + +@keyframes modalFadeOut { + from { + opacity: 1; + transform: scale(1) translateY(0); + } + to { + opacity: 0; + transform: scale(0.95) translateY(20px); + } +} + +/* Smooth transitions */ +button, a, .action-btn { + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); +} + +/* Action button styles */ +.action-btn:hover { + transform: translateY(-2px); +} + +.action-btn:active { + transform: translateY(0); +} + +/* Flash message animation */ +.flash-message { + animation: slideInRight 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +@keyframes slideInRight { + from { + opacity: 0; + transform: translateX(100%); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +/* Pulse animation for empty state */ +@keyframes pulse { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} + +.animate-pulse { + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +/* Gradient text animation */ +@keyframes gradient { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} + +.bg-clip-text { + -webkit-background-clip: text; + background-clip: text; +} + +/* Glassmorphism effect */ +.glass { + background: rgba(26, 29, 39, 0.8); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.1); +} + +/* Hover glow effect */ +.hover-glow { + position: relative; +} + +.hover-glow::before { + content: ""; + position: absolute; + inset: 0; + border-radius: inherit; + padding: 2px; + background: linear-gradient(45deg, #6366f1, #8b5cf6, #ec4899); + -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + opacity: 0; + transition: opacity 0.3s; +} + +.hover-glow:hover::before { + opacity: 1; +} diff --git a/mini-dropbox/CODE/app/static/js/dashboard.js b/mini-dropbox/CODE/app/static/js/dashboard.js new file mode 100644 index 0000000..1709850 --- /dev/null +++ b/mini-dropbox/CODE/app/static/js/dashboard.js @@ -0,0 +1,398 @@ +// === FILE: app/static/js/dashboard.js === + +// Global variables +let selectedFile = null; +let deleteFileId = null; + +// ======================================== +// UPLOAD FUNCTIONALITY +// ======================================== + +// Open upload modal +document.getElementById('open-upload-modal').addEventListener('click', function() { + const modal = document.getElementById('upload-modal'); + modal.classList.remove('hidden'); + modal.classList.add('flex', 'modal-enter'); + resetUploadModal(); +}); + +// Close upload modal +document.getElementById('cancel-upload').addEventListener('click', function() { + closeUploadModal(); +}); + +// Close upload modal with X button +document.getElementById('close-upload-modal').addEventListener('click', function() { + closeUploadModal(); +}); + +function closeUploadModal() { + const modal = document.getElementById('upload-modal'); + modal.classList.add('modal-exit'); + setTimeout(() => { + modal.classList.add('hidden'); + modal.classList.remove('flex', 'modal-enter', 'modal-exit'); + }, 200); +} + +// Reset upload modal +function resetUploadModal() { + selectedFile = null; + document.getElementById('file-input').value = ''; + document.getElementById('selected-file').classList.add('hidden'); + document.getElementById('progress-container').classList.add('hidden'); + document.getElementById('upload-error').classList.add('hidden'); + document.getElementById('progress-bar').style.width = '0%'; + document.getElementById('upload-btn').disabled = true; +} + +// Drop zone click +document.getElementById('drop-zone').addEventListener('click', function() { + document.getElementById('file-input').click(); +}); + +// File input change +document.getElementById('file-input').addEventListener('change', function(e) { + if (e.target.files.length > 0) { + handleFileSelect(e.target.files[0]); + } +}); + +// Drag and drop handlers +const dropZone = document.getElementById('drop-zone'); + +dropZone.addEventListener('dragover', function(e) { + e.preventDefault(); + e.stopPropagation(); + dropZone.classList.add('drag-over'); +}); + +dropZone.addEventListener('dragleave', function(e) { + e.preventDefault(); + e.stopPropagation(); + dropZone.classList.remove('drag-over'); +}); + +dropZone.addEventListener('drop', function(e) { + e.preventDefault(); + e.stopPropagation(); + dropZone.classList.remove('drag-over'); + + if (e.dataTransfer.files.length > 0) { + handleFileSelect(e.dataTransfer.files[0]); + } +}); + +// Handle file selection +function handleFileSelect(file) { + // Check file size (100MB max) + const maxSize = 100 * 1024 * 1024; // 100MB + if (file.size > maxSize) { + showUploadError('File size exceeds 100MB limit'); + return; + } + + selectedFile = file; + document.getElementById('selected-file-name').textContent = file.name; + document.getElementById('selected-file').classList.remove('hidden'); + document.getElementById('upload-btn').disabled = false; + document.getElementById('upload-error').classList.add('hidden'); +} + +// Show upload error +function showUploadError(message) { + const errorDiv = document.getElementById('upload-error'); + const errorText = document.getElementById('upload-error-text'); + if (errorText) { + errorText.textContent = message; + } else { + errorDiv.textContent = message; + } + errorDiv.classList.remove('hidden'); +} + +// Upload button click +document.getElementById('upload-btn').addEventListener('click', function() { + if (!selectedFile) { + showUploadError('Please select a file'); + return; + } + + uploadFile(selectedFile); +}); + +// Upload file function +function uploadFile(file) { + const formData = new FormData(); + formData.append('file', file); + + // Show progress bar + document.getElementById('progress-container').classList.remove('hidden'); + document.getElementById('upload-btn').disabled = true; + document.getElementById('cancel-upload').disabled = true; + + // Use XMLHttpRequest for progress tracking + const xhr = new XMLHttpRequest(); + + // Progress handler + xhr.upload.addEventListener('progress', function(e) { + if (e.lengthComputable) { + const percentage = (e.loaded / e.total) * 100; + const roundedPercent = Math.round(percentage); + document.getElementById('progress-bar').style.width = percentage + '%'; + document.getElementById('progress-text').textContent = `Uploading...`; + const progressPercent = document.getElementById('progress-percent'); + if (progressPercent) { + progressPercent.textContent = roundedPercent + '%'; + } + } + }); + + // Load handler (upload complete) + xhr.addEventListener('load', function() { + if (xhr.status === 200) { + try { + const response = JSON.parse(xhr.responseText); + if (response.success) { + // Success - reload page to show new file + window.location.reload(); + } else { + showUploadError(response.message || 'Upload failed'); + document.getElementById('upload-btn').disabled = false; + document.getElementById('cancel-upload').disabled = false; + } + } catch (e) { + showUploadError('Upload failed'); + document.getElementById('upload-btn').disabled = false; + document.getElementById('cancel-upload').disabled = false; + } + } else { + showUploadError('Upload failed. Please try again.'); + document.getElementById('upload-btn').disabled = false; + document.getElementById('cancel-upload').disabled = false; + } + }); + + // Error handler + xhr.addEventListener('error', function() { + showUploadError('Network error. Please try again.'); + document.getElementById('upload-btn').disabled = false; + document.getElementById('cancel-upload').disabled = false; + }); + + // Send request + xhr.open('POST', '/files/upload'); + xhr.send(formData); +} + +// ======================================== +// DELETE FUNCTIONALITY +// ======================================== + +function confirmDelete(fileId, fileName) { + deleteFileId = fileId; + document.getElementById('delete-file-name').textContent = fileName; + + const modal = document.getElementById('delete-modal'); + modal.classList.remove('hidden'); + modal.classList.add('flex', 'modal-enter'); +} + +// Cancel delete +document.getElementById('cancel-delete').addEventListener('click', function() { + closeDeleteModal(); +}); + +function closeDeleteModal() { + const modal = document.getElementById('delete-modal'); + modal.classList.add('modal-exit'); + setTimeout(() => { + modal.classList.add('hidden'); + modal.classList.remove('flex', 'modal-enter', 'modal-exit'); + deleteFileId = null; + }, 200); +} + +// Confirm delete +document.getElementById('confirm-delete-btn').addEventListener('click', function() { + if (!deleteFileId) return; + + const btn = this; + btn.classList.add('btn-loading'); + btn.disabled = true; + + fetch(`/files/delete/${deleteFileId}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + // Remove file card from DOM + const fileCard = document.querySelector(`.file-card[data-file-id="${deleteFileId}"]`); + if (fileCard) { + fileCard.style.opacity = '0'; + fileCard.style.transform = 'scale(0.9)'; + setTimeout(() => { + fileCard.remove(); + + // Check if no files left + const filesGrid = document.getElementById('files-grid'); + if (filesGrid && filesGrid.children.length === 0) { + window.location.reload(); + } + }, 300); + } + + closeDeleteModal(); + showFlashMessage('File deleted successfully', 'success'); + } else { + showFlashMessage(data.message || 'Failed to delete file', 'error'); + btn.classList.remove('btn-loading'); + btn.disabled = false; + } + }) + .catch(error => { + console.error('Error:', error); + showFlashMessage('Failed to delete file', 'error'); + btn.classList.remove('btn-loading'); + btn.disabled = false; + }); +}); + +// ======================================== +// SHARE FUNCTIONALITY +// ======================================== + +function shareFile(fileId) { + fetch(`/files/share/${fileId}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + document.getElementById('share-url').value = data.share_url; + + const modal = document.getElementById('share-modal'); + modal.classList.remove('hidden'); + modal.classList.add('flex', 'modal-enter'); + } else { + showFlashMessage(data.message || 'Failed to generate share link', 'error'); + } + }) + .catch(error => { + console.error('Error:', error); + showFlashMessage('Failed to generate share link', 'error'); + }); +} + +// Close share modal +document.getElementById('close-share-modal').addEventListener('click', function() { + closeShareModal(); +}); + +function closeShareModal() { + const modal = document.getElementById('share-modal'); + modal.classList.add('modal-exit'); + setTimeout(() => { + modal.classList.add('hidden'); + modal.classList.remove('flex', 'modal-enter', 'modal-exit'); + document.getElementById('copy-feedback').classList.add('hidden'); + }, 200); +} + +// Copy link button +document.getElementById('copy-link-btn').addEventListener('click', function() { + const shareUrl = document.getElementById('share-url'); + shareUrl.select(); + shareUrl.setSelectionRange(0, 99999); // For mobile devices + + // Copy to clipboard + navigator.clipboard.writeText(shareUrl.value) + .then(() => { + const feedback = document.getElementById('copy-feedback'); + feedback.classList.remove('hidden'); + + // Hide after 2 seconds + setTimeout(() => { + feedback.classList.add('hidden'); + }, 2000); + }) + .catch(err => { + console.error('Failed to copy:', err); + showFlashMessage('Failed to copy link', 'error'); + }); +}); + +// ======================================== +// PREVIEW FUNCTIONALITY +// ======================================== + +function previewFile(fileId) { + fetch(`/files/preview/${fileId}`) + .then(response => response.json()) + .then(data => { + if (data.success && data.preview_url) { + window.open(data.preview_url, '_blank'); + } else { + showFlashMessage('Preview not available', 'error'); + } + }) + .catch(error => { + console.error('Error:', error); + showFlashMessage('Failed to generate preview', 'error'); + }); +} + +// ======================================== +// HELPER FUNCTIONS +// ======================================== + +function showFlashMessage(message, category) { + const flashContainer = document.getElementById('flash-messages') || createFlashContainer(); + + const messageDiv = document.createElement('div'); + messageDiv.className = `flash-message px-6 py-3 rounded-lg shadow-lg text-white`; + + if (category === 'success') { + messageDiv.classList.add('bg-[#22c55e]'); + } else if (category === 'error') { + messageDiv.classList.add('bg-[#ef4444]'); + } else { + messageDiv.classList.add('bg-[#6366f1]'); + } + + messageDiv.textContent = message; + flashContainer.appendChild(messageDiv); + + // Auto-dismiss after 4 seconds + setTimeout(() => { + messageDiv.style.opacity = '0'; + messageDiv.style.transform = 'translateX(100%)'; + messageDiv.style.transition = 'all 0.3s ease-out'; + setTimeout(() => { + messageDiv.remove(); + }, 300); + }, 4000); +} + +function createFlashContainer() { + const container = document.createElement('div'); + container.id = 'flash-messages'; + container.className = 'fixed top-4 right-4 z-50 space-y-2'; + document.body.appendChild(container); + return container; +} + +// ======================================== +// INITIALIZATION +// ======================================== + +document.addEventListener('DOMContentLoaded', function() { + console.log('Dashboard loaded'); +}); diff --git a/mini-dropbox/CODE/app/templates/base.html b/mini-dropbox/CODE/app/templates/base.html new file mode 100644 index 0000000..ad9a1ec --- /dev/null +++ b/mini-dropbox/CODE/app/templates/base.html @@ -0,0 +1,97 @@ + + + + + + + {% block title %}Cloud Drive{% endblock %} + + + + + + + + + + + + + + + + + {% if session.user %} + + {% endif %} + + + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
+ {% for category, message in messages %} +
+ {{ message }} +
+ {% endfor %} +
+ {% endif %} + {% endwith %} + + +
+ {% block content %}{% endblock %} +
+ + + + + {% block scripts %}{% endblock %} + + diff --git a/mini-dropbox/CODE/app/templates/dashboard.html b/mini-dropbox/CODE/app/templates/dashboard.html new file mode 100644 index 0000000..3e3e048 --- /dev/null +++ b/mini-dropbox/CODE/app/templates/dashboard.html @@ -0,0 +1,346 @@ + +{% extends "base.html" %} + +{% block title %}Dashboard - Cloud Drive{% endblock %} + +{% block content %} +
+ +
+
+
+

+ Hello, {{ user.name }} 👋 +

+

Manage your files in the cloud

+
+ +
+ + +
+
+
+
+ + + +
+
+

{{ files|length }}

+

Total Files

+
+
+
+ +
+
+
+ + + +
+
+

+

Storage Available

+
+
+
+ +
+
+
+ + + +
+
+

100%

+

Secure Storage

+
+
+
+
+
+ + + {% if files %} +
+

+ + + + Your Files +

+
+ +
+ {% for file in files %} +
+ +
+
+ + {% if file.category == 'image' %}🖼️ + {% elif file.category == 'pdf' %}📄 + {% elif file.category == 'video' %}🎬 + {% elif file.category == 'audio' %}🎵 + {% elif file.category == 'archive' %}📦 + {% elif file.category == 'document' %}📝 + {% else %}📎 + {% endif %} + +
+
+

+ {{ file.original_name }} +

+
+ {{ file.size_formatted }} + {{ file.date_formatted }} +
+
+
+ + +
+
+ + + + + Download + + +
+
+ {% if file.category in ['image', 'pdf'] %} + + {% endif %} + +
+
+
+ {% endfor %} +
+ {% else %} + +
+
+
+
+
+ + + +
+

No files yet

+

Start uploading your files to the cloud. Your data is encrypted and secure.

+ +
+ {% endif %} +
+ + + + + + + + + +{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/mini-dropbox/CODE/app/templates/login.html b/mini-dropbox/CODE/app/templates/login.html new file mode 100644 index 0000000..413d6fe --- /dev/null +++ b/mini-dropbox/CODE/app/templates/login.html @@ -0,0 +1,80 @@ + + + + + + + Cloud Drive - Sign In + + + + + + + + + + + + +
+ +
+ +
+
+ + + +
+

Cloud Drive

+

Your personal file storage, powered by AWS

+
+ + + + + + + +
+
+ 🔒 Secure + + 🔐 Private + + ⚡ Fast +
+
+
+ + +
+

Powered by AWS S3, RDS, and CloudFront

+
+
+ + diff --git a/mini-dropbox/CODE/gunicorn.conf.py b/mini-dropbox/CODE/gunicorn.conf.py new file mode 100644 index 0000000..266244d --- /dev/null +++ b/mini-dropbox/CODE/gunicorn.conf.py @@ -0,0 +1,13 @@ +# === FILE: gunicorn.conf.py === +bind = "0.0.0.0:5000" +workers = 2 +threads = 2 +worker_class = "sync" +worker_connections = 1000 +timeout = 120 +keepalive = 5 +max_requests = 1000 +max_requests_jitter = 100 +accesslog = "/var/log/gunicorn/access.log" +errorlog = "/var/log/gunicorn/error.log" +loglevel = "info" diff --git a/mini-dropbox/CODE/requirements.txt b/mini-dropbox/CODE/requirements.txt new file mode 100644 index 0000000..6f7bbe2 --- /dev/null +++ b/mini-dropbox/CODE/requirements.txt @@ -0,0 +1,8 @@ +Flask==3.0.0 +Authlib==1.3.0 +boto3==1.34.0 +psycopg[binary] +python-dotenv==1.0.0 +gunicorn==21.2.0 +Werkzeug==3.0.1 +requests==2.31.0 diff --git a/mini-dropbox/CODE/run.py b/mini-dropbox/CODE/run.py new file mode 100644 index 0000000..155bc9b --- /dev/null +++ b/mini-dropbox/CODE/run.py @@ -0,0 +1,31 @@ +# === FILE: run.py === +import os +from dotenv import load_dotenv + +# Load environment variables from .env file +load_dotenv() + +from app import create_app +from app.db import init_db + +# Create Flask app +app = create_app() + +if __name__ == '__main__': + # Initialize database on first run + # try: + # init_db() + # except Exception as e: + # print(f"Database initialization warning: {e}") + # print("Make sure database is accessible and schema.sql exists") + print("Skipping database initialization - configure DB connection first") + + # Run the application + # host 0.0.0.0 makes the app accessible externally + # port 5000 is the default Flask port + # debug should be False in production + app.run( + host='0.0.0.0', + port=5000, + debug=os.environ.get('FLASK_ENV') == 'development' + ) diff --git a/mini-dropbox/CODE/sql/schema.sql b/mini-dropbox/CODE/sql/schema.sql new file mode 100644 index 0000000..a0c0370 --- /dev/null +++ b/mini-dropbox/CODE/sql/schema.sql @@ -0,0 +1,20 @@ +CREATE TABLE IF NOT EXISTS users ( + id SERIAL PRIMARY KEY, + google_id VARCHAR(255) UNIQUE NOT NULL, + email VARCHAR(255) UNIQUE NOT NULL, + name VARCHAR(255), + profile_picture TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS files ( + id SERIAL PRIMARY KEY, + user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, + filename VARCHAR(255) NOT NULL, + original_name VARCHAR(255) NOT NULL, + s3_key TEXT NOT NULL, + file_size BIGINT, + file_type VARCHAR(100), + uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + share_token VARCHAR(64) UNIQUE +); diff --git a/mini-dropbox/MINI-DROPBOX-README.md b/mini-dropbox/MINI-DROPBOX-README.md new file mode 100644 index 0000000..97a19a4 --- /dev/null +++ b/mini-dropbox/MINI-DROPBOX-README.md @@ -0,0 +1,385 @@ +# 📦 Mini Dropbox — Cloud File Storage on AWS + +> A fully functional cloud file storage platform built on AWS — mimicking core Dropbox features including file upload, download, delete, and shareable links with Google OAuth authentication. + +> **App URL:** `https://dzn1pcqz3oqep.cloudfront.net/files/` +> **Status: Not Live** on AWS EC2 (ap-south-1 · Mumbai) + +--- + +## 📸 Screenshots + +> All build screenshots are available in the [`/images`](./images) folder, numbered `1` through `45` in chronological build order. + +### Login Page +![Login](./images/mini-dropbox-24.png) + +-------- + +### Dashboard +![Dashboard](./images/mini-dropbox-25.png) +--- +### File upload +![File upload](./images/mini-dropbox-35.png) +--- +### Dashboard view with files +![Dashboard view with uploaded files](./images/mini-dropbox-36.png) +--- +### Share link +![Sharing PreSigned URL](./images/mini-dropbox-37.png) +--- + +### Preview Link Fron mini-dropbox +![Preview Link](./images/mini-dropbox-38.png) + +--- + +## 🧰 Tech Stack + +| Layer | Technology | +|---|---| +| Backend | Python 3.11 + Flask | +| Frontend | HTML5 + Tailwind CSS + Vanilla JS | +| Auth | Google OAuth 2.0 (Authlib) | +| File Storage | AWS S3 | +| Database | AWS RDS PostgreSQL 16 | +| CDN | AWS CloudFront | +| Web Server | Nginx + Gunicorn | +| Compute | AWS EC2 (Ubuntu 22.04) | +| Monitoring | AWS CloudWatch | + +--- + +## ☁️ AWS Architecture + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ INTERNET │ +└───────────────────────────┬─────────────────────────────────────────┘ + │ + │ HTTPS / HTTP + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ AWS CLOUD (ap-south-1 · Mumbai) │ +│ │ +│ ┌──────────────────────────────────────────────────────────────┐ │ +│ │ DEFAULT VPC │ │ +│ │ │ │ +│ │ ┌──────────────────────┐ │ │ +│ │ │ CloudFront (CDN) │ ◄── Static files & previews │ │ +│ │ │ (Global Edge Cache) │ served globally │ │ +│ │ └──────────┬───────────┘ │ │ +│ │ │ │ │ +│ │ │ Origin request │ │ +│ │ ▼ │ │ +│ │ ┌──────────────────────────────────────────────────────┐ │ │ +│ │ │ EC2 (t2.micro · Ubuntu 22.04) │ │ │ +│ │ │ Security Group: mini-dropbox-ec2-sg │ │ │ +│ │ │ Ports: 22 (SSH) · 80 (HTTP) · 5000 │ │ │ +│ │ │ │ │ │ +│ │ │ ┌─────────────┐ ┌─────────────────────────┐ │ │ │ +│ │ │ │ Nginx │──────►│ Gunicorn (WSGI) │ │ │ │ +│ │ │ │ (Port 80) │ │ 2 workers · Port 5000 │ │ │ │ +│ │ │ │ Rev. Proxy │ └──────────┬──────────────┘ │ │ │ +│ │ │ └─────────────┘ │ │ │ │ +│ │ │ ▼ │ │ │ +│ │ │ ┌───────────────────────┐ │ │ | +│ │ │ │ Flask Application │ │ │ | +│ │ │ │ ┌─────────────────┐ │ │ │ | +│ │ │ │ │ auth.py │ │ │ │ | +│ │ │ │ │ Google OAuth │ │ │ │ | +│ │ │ │ └─────────────────┘ │ │ │ | +│ │ │ │ ┌─────────────────┐ │ │ │ | +│ │ │ │ │ files.py │ │ │ │ | +│ │ │ │ │ upload/delete │ │ │ │ | +│ │ │ │ │ share/preview │ │ │ │ | +│ │ │ │ └─────────────────┘ │ │ │ | +│ │ │ │ ┌─────────────────┐ │ │ │ | +│ │ │ │ │ s3.py + db.py │ │ │ │ | +│ │ │ │ └─────────────────┘ │ │ │ | +│ │ │ └──────┬──────────┬──────┘ │ │ | +│ │ │ IAM Role attached │ │ │ │ | +│ │ │ (No hardcoded keys) │ │ │ │ | +│ │ └───────────────────────────────┼──────────┼───────────┘ │ | +│ │ │ │ | │ +│ │ ┌────────────────────┘ └──────────────┐| | +│ │ | | +│ │ │ S3 API (boto3) psycopg2 :5432 │ │ +│ │ ▼ ▼ │ +│ │ ┌─────────────────────┐ ┌──────────────────────────┐ │ +│ │ │ AWS S3 │ │ RDS PostgreSQL 16 │ │ +│ │ │ mini-dropbox-files │ │ mini-dropbox-db │ │ +│ │ │ │ │ (db.t3.micro) │ │ +│ │ │ ┌───────────────┐ │ │ SG: rds-sg │ │ +│ │ │ │ user_id/uuid_ │ │ │ (only EC2 SG allowed) │ │ +│ │ │ │ filename.ext │ │ │ │ │ +│ │ │ │ (private) │ │ │ ┌──────────────────┐ │ │ +│ │ │ └───────────────┘ │ │ │ users table │ │ │ +│ │ │ │ │ │ files table │ │ │ +│ │ │ Access via: │ │ └──────────────────┘ │ │ +│ │ │ Pre-signed URLs │ └──────────────────────────┘ │ +│ │ │ (15min download) │ │ │ +│ │ │ (7day share link) │ │ │ +│ │ └─────────────────────┘ │ │ +│ │ │ │ +│ └──────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │ +│ │ IAM │ │ CloudWatch │ │ Google OAuth │ │ +│ │ EC2 Role │ │ Monitoring │ │ (External · HTTPS) │ │ +│ │ S3 Access │ │ Logs+Alarms │ │ email+profile+id │ │ +│ └──────────────┘ └──────────────┘ └──────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +### Request Flow + +``` + Upload File Download / Share + ─────────── ──────────────── + Browser Browser + │ POST /files/upload │ GET /files/download/ + ▼ ▼ + Nginx → Flask Nginx → Flask + │ secure_filename() │ verify ownership (RDS) + │ generate s3_key │ generate pre-signed URL + ▼ ▼ + S3 (store file) S3 (generate temp URL) + │ │ + ▼ ▼ + RDS (store metadata) Redirect → Browser downloads +``` + +### Security Design + +``` + Internet + │ + │ ✅ HTTP/HTTPS only + ▼ + EC2 (mini-dropbox-ec2-sg) + │ Port 22 ← Your IP only (SSH) + │ Port 80 ← Anyone (web traffic) + │ + │ ✅ IAM Role — no AWS keys in code + │ ✅ Pre-signed URLs — S3 never public + ▼ + RDS (mini-dropbox-rds-sg) + │ Port 5432 ← EC2 Security Group ONLY + │ (not open to internet) + ▼ + S3 Bucket + │ Block All Public Access ✅ + │ CORS configured ✅ + │ SSE-S3 Encryption ✅ +``` + +--- + +## ✨ Features + +| Feature | Description | +|---|---| +| 🔐 Google Sign In | OAuth 2.0 login — no password needed | +| 📤 File Upload | Drag & drop or click to upload, up to 100MB | +| 📋 File Dashboard | View all your files with type, size, and upload date | +| ⬇️ Download | Secure time-limited pre-signed S3 URL (15 min) | +| 🗑️ Delete | Remove file from S3 and metadata from RDS instantly | +| 🔗 Share Link | Generate a 7-day shareable pre-signed URL | +| 👁️ Preview | Open images and PDFs directly in browser via CloudFront | +| 📊 File Icons | Visual icons per file type (PDF, image, video, zip, etc.) | + +--- + +## 🗂️ Project Structure + +``` +mini-dropbox/ +|---CODE +├── app/ +│ ├── __init__.py # Flask app factory +│ ├── config.py # Environment config +│ ├── auth.py # Google OAuth routes +│ ├── files.py # Upload, download, delete, share routes +│ ├── db.py # RDS PostgreSQL operations +│ ├── s3.py # S3 operations + pre-signed URLs +│ │ +│ ├── templates/ +│ │ ├── base.html # Base layout with navbar +│ │ ├── login.html # Google Sign-In page +│ │ └── dashboard.html # File manager UI +│ │ +│ └── static/ +│ ├── css/style.css # Custom styles +│ └── js/dashboard.js # Upload, delete, share, preview logic +│ +├── sql/ +│ └── schema.sql # RDS table definitions +│ +├── docs/ +│ └── screenshots/ # UI screenshots +│ +├── .env.example # Environment variable template +├── requirements.txt # Python dependencies +├── run.py # App entry point +├── gunicorn.conf.py # Production server config +└── README.md +``` + +--- + +## 🗄️ Database Schema + +```sql +-- Users (from Google OAuth) +users (id, google_id, email, name, profile_picture, created_at) + +-- Files (metadata only — actual files live in S3) +files (id, user_id, filename, original_name, s3_key, + file_size, file_type, uploaded_at, share_token) +``` + +--- + +## 🚀 How to Deploy + +### Prerequisites +- AWS Account with Free Tier +- Google Cloud Console account +- EC2 instance (Ubuntu 22.04, t2.micro) +- RDS PostgreSQL instance (db.t3.micro) +- S3 bucket (private) + +### 1. Clone the repo +```bash +git clone https://github.com/ADITYANAIR01/AWS.git +cd mini-dropbox +``` + +### 2. Install dependencies +```bash +sudo apt update && sudo apt install -y python3 python3-pip nginx postgresql-client +sudo pip3 install -r requirements.txt +``` + +### 3. Configure environment +```bash +cp .env.example .env +nano .env # Fill in all values +``` + +### 4. Initialize database +```bash +python3 -c " +from app import create_app +from app.db import init_db +app = create_app() +with app.app_context(): + init_db() +" +``` + +### 5. Configure Nginx +```bash +sudo nano /etc/nginx/sites-available/mini-dropbox +# Paste nginx config (see docs below) +sudo ln -s /etc/nginx/sites-available/mini-dropbox /etc/nginx/sites-enabled/ +sudo systemctl restart nginx +``` + +### 6. Run with Gunicorn +```bash +sudo systemctl start mini-dropbox +sudo systemctl enable mini-dropbox +``` + +--- + +## 🔧 Environment Variables + +```bash +# Flask +FLASK_SECRET_KEY=your-secret-key +FLASK_ENV=production + +# Google OAuth +GOOGLE_CLIENT_ID=xxxx.apps.googleusercontent.com +GOOGLE_CLIENT_SECRET=GOCSPX-xxxx +GOOGLE_REDIRECT_URI=http://YOUR-EC2-IP/auth/callback + +# AWS +AWS_REGION=ap-south-1 +S3_BUCKET_NAME=your-bucket-name +CLOUDFRONT_DOMAIN=your-cloudfront-domain.cloudfront.net + +# RDS +DB_HOST=your-rds-endpoint.amazonaws.com +DB_PORT=5432 +DB_NAME=minidropbox +DB_USER=dbadmin +DB_PASSWORD=your-password +``` + +--- + +## 🔐 AWS IAM & Security Design + +- **IAM Role** (`mini-dropbox-ec2-role`) attached to EC2 — no AWS keys in code +- **S3 bucket** is fully private — no public access allowed +- **Files served** via pre-signed URLs with expiry (download: 15 min, share: 7 days) +- **RDS** has no public access — only reachable from EC2 via Security Group +- **Security Groups** — RDS SG allows inbound only from EC2 SG (not open to internet) + +--- + +## 📡 API Routes + +| Method | Route | Description | +|---|---|---| +| GET | `/auth/login` | Redirect to Google OAuth | +| GET | `/auth/callback` | Handle Google OAuth callback | +| GET | `/auth/logout` | Clear session + redirect to login | +| GET | `/` | Dashboard — list user's files | +| POST | `/files/upload` | Upload file to S3 + save metadata | +| GET | `/files/download/` | Generate pre-signed download URL | +| POST | `/files/delete/` | Delete from S3 + RDS | +| POST | `/files/share/` | Generate 7-day shareable link | +| GET | `/files/preview/` | Get CloudFront preview URL | + +--- + +## 💡 Cloud Concepts Demonstrated + +- **Object Storage** — S3 as scalable file storage (not server disk) +- **Pre-signed URLs** — Secure, temporary access to private S3 objects +- **CDN** — CloudFront for fast global file delivery +- **Multi-tier architecture** — EC2 (compute) + S3 (storage) + RDS (database) separated +- **IAM Roles** — EC2 to S3 auth without hardcoded credentials +- **Security Groups** — Network-level access control between services +- **Managed Database** — RDS handles backups, patching, availability +- **Process Management** — Gunicorn + systemd for production reliability +- **Reverse Proxy** — Nginx routing traffic to Flask app + +--- + +## 📊 AWS Services Used + +| Service | Purpose | Free Tier | +|---|---|---| +| EC2 t2.micro | App server | 750 hrs/month | +| RDS db.t3.micro | PostgreSQL database | 750 hrs/month | +| S3 | File storage | 5GB storage | +| CloudFront | CDN / file delivery | 1TB transfer/month | +| IAM | Roles & permissions | Always free | +| CloudWatch | Monitoring & logs | 10 metrics free | +GCP Google Oauth Client| for login with google + +--- +## 👨‍💻 Author + +**Aditya Nair** +- GitHub: [@ADITYANAIR01](https://github.com/ADITYANAIR01) +- LinkedIn: [linkedin.com/in/adityanair001](https://www.linkedin.com/in/adityanair001) + +--- \ No newline at end of file diff --git a/mini-dropbox/images/mini-dropbox-01.png b/mini-dropbox/images/mini-dropbox-01.png new file mode 100644 index 0000000..2e4a078 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-01.png differ diff --git a/mini-dropbox/images/mini-dropbox-02.png b/mini-dropbox/images/mini-dropbox-02.png new file mode 100644 index 0000000..fba0241 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-02.png differ diff --git a/mini-dropbox/images/mini-dropbox-03.png b/mini-dropbox/images/mini-dropbox-03.png new file mode 100644 index 0000000..a58b027 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-03.png differ diff --git a/mini-dropbox/images/mini-dropbox-04.png b/mini-dropbox/images/mini-dropbox-04.png new file mode 100644 index 0000000..9038049 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-04.png differ diff --git a/mini-dropbox/images/mini-dropbox-05.png b/mini-dropbox/images/mini-dropbox-05.png new file mode 100644 index 0000000..7c527d2 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-05.png differ diff --git a/mini-dropbox/images/mini-dropbox-06.png b/mini-dropbox/images/mini-dropbox-06.png new file mode 100644 index 0000000..b7e4d54 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-06.png differ diff --git a/mini-dropbox/images/mini-dropbox-07.png b/mini-dropbox/images/mini-dropbox-07.png new file mode 100644 index 0000000..ceba2e5 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-07.png differ diff --git a/mini-dropbox/images/mini-dropbox-08.png b/mini-dropbox/images/mini-dropbox-08.png new file mode 100644 index 0000000..3af7158 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-08.png differ diff --git a/mini-dropbox/images/mini-dropbox-09.png b/mini-dropbox/images/mini-dropbox-09.png new file mode 100644 index 0000000..ddf5d47 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-09.png differ diff --git a/mini-dropbox/images/mini-dropbox-10.png b/mini-dropbox/images/mini-dropbox-10.png new file mode 100644 index 0000000..7f83ebb Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-10.png differ diff --git a/mini-dropbox/images/mini-dropbox-11.png b/mini-dropbox/images/mini-dropbox-11.png new file mode 100644 index 0000000..84fd22c Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-11.png differ diff --git a/mini-dropbox/images/mini-dropbox-12.png b/mini-dropbox/images/mini-dropbox-12.png new file mode 100644 index 0000000..e027b5a Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-12.png differ diff --git a/mini-dropbox/images/mini-dropbox-13.png b/mini-dropbox/images/mini-dropbox-13.png new file mode 100644 index 0000000..ec4085e Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-13.png differ diff --git a/mini-dropbox/images/mini-dropbox-14.png b/mini-dropbox/images/mini-dropbox-14.png new file mode 100644 index 0000000..52f7afb Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-14.png differ diff --git a/mini-dropbox/images/mini-dropbox-15.png b/mini-dropbox/images/mini-dropbox-15.png new file mode 100644 index 0000000..f58d7c6 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-15.png differ diff --git a/mini-dropbox/images/mini-dropbox-16.png b/mini-dropbox/images/mini-dropbox-16.png new file mode 100644 index 0000000..a2b1420 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-16.png differ diff --git a/mini-dropbox/images/mini-dropbox-17.png b/mini-dropbox/images/mini-dropbox-17.png new file mode 100644 index 0000000..bdde484 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-17.png differ diff --git a/mini-dropbox/images/mini-dropbox-18.png b/mini-dropbox/images/mini-dropbox-18.png new file mode 100644 index 0000000..a9613c4 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-18.png differ diff --git a/mini-dropbox/images/mini-dropbox-19.png b/mini-dropbox/images/mini-dropbox-19.png new file mode 100644 index 0000000..2c07f31 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-19.png differ diff --git a/mini-dropbox/images/mini-dropbox-20.png b/mini-dropbox/images/mini-dropbox-20.png new file mode 100644 index 0000000..687006f Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-20.png differ diff --git a/mini-dropbox/images/mini-dropbox-21.png b/mini-dropbox/images/mini-dropbox-21.png new file mode 100644 index 0000000..aacc201 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-21.png differ diff --git a/mini-dropbox/images/mini-dropbox-22.png b/mini-dropbox/images/mini-dropbox-22.png new file mode 100644 index 0000000..f368b0e Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-22.png differ diff --git a/mini-dropbox/images/mini-dropbox-23.png b/mini-dropbox/images/mini-dropbox-23.png new file mode 100644 index 0000000..c9fa63c Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-23.png differ diff --git a/mini-dropbox/images/mini-dropbox-24.png b/mini-dropbox/images/mini-dropbox-24.png new file mode 100644 index 0000000..abdc13a Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-24.png differ diff --git a/mini-dropbox/images/mini-dropbox-25.png b/mini-dropbox/images/mini-dropbox-25.png new file mode 100644 index 0000000..7735425 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-25.png differ diff --git a/mini-dropbox/images/mini-dropbox-26.png b/mini-dropbox/images/mini-dropbox-26.png new file mode 100644 index 0000000..030fe2d Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-26.png differ diff --git a/mini-dropbox/images/mini-dropbox-27.png b/mini-dropbox/images/mini-dropbox-27.png new file mode 100644 index 0000000..f97190c Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-27.png differ diff --git a/mini-dropbox/images/mini-dropbox-28.png b/mini-dropbox/images/mini-dropbox-28.png new file mode 100644 index 0000000..ad54c4e Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-28.png differ diff --git a/mini-dropbox/images/mini-dropbox-29.png b/mini-dropbox/images/mini-dropbox-29.png new file mode 100644 index 0000000..a6d690f Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-29.png differ diff --git a/mini-dropbox/images/mini-dropbox-30.png b/mini-dropbox/images/mini-dropbox-30.png new file mode 100644 index 0000000..fb3839c Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-30.png differ diff --git a/mini-dropbox/images/mini-dropbox-31.png b/mini-dropbox/images/mini-dropbox-31.png new file mode 100644 index 0000000..5c05171 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-31.png differ diff --git a/mini-dropbox/images/mini-dropbox-32.png b/mini-dropbox/images/mini-dropbox-32.png new file mode 100644 index 0000000..b57e2cb Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-32.png differ diff --git a/mini-dropbox/images/mini-dropbox-33.png b/mini-dropbox/images/mini-dropbox-33.png new file mode 100644 index 0000000..5b0ac9f Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-33.png differ diff --git a/mini-dropbox/images/mini-dropbox-34.png b/mini-dropbox/images/mini-dropbox-34.png new file mode 100644 index 0000000..eca13f9 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-34.png differ diff --git a/mini-dropbox/images/mini-dropbox-35.png b/mini-dropbox/images/mini-dropbox-35.png new file mode 100644 index 0000000..9cde393 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-35.png differ diff --git a/mini-dropbox/images/mini-dropbox-36.png b/mini-dropbox/images/mini-dropbox-36.png new file mode 100644 index 0000000..9d16ff6 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-36.png differ diff --git a/mini-dropbox/images/mini-dropbox-37.png b/mini-dropbox/images/mini-dropbox-37.png new file mode 100644 index 0000000..e144a04 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-37.png differ diff --git a/mini-dropbox/images/mini-dropbox-38.png b/mini-dropbox/images/mini-dropbox-38.png new file mode 100644 index 0000000..60a4223 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-38.png differ diff --git a/mini-dropbox/images/mini-dropbox-39.png b/mini-dropbox/images/mini-dropbox-39.png new file mode 100644 index 0000000..a925cb0 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-39.png differ diff --git a/mini-dropbox/images/mini-dropbox-40.png b/mini-dropbox/images/mini-dropbox-40.png new file mode 100644 index 0000000..c68ccff Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-40.png differ diff --git a/mini-dropbox/images/mini-dropbox-41.png b/mini-dropbox/images/mini-dropbox-41.png new file mode 100644 index 0000000..482f6f9 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-41.png differ diff --git a/mini-dropbox/images/mini-dropbox-42.png b/mini-dropbox/images/mini-dropbox-42.png new file mode 100644 index 0000000..b4a33b2 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-42.png differ diff --git a/mini-dropbox/images/mini-dropbox-43.png b/mini-dropbox/images/mini-dropbox-43.png new file mode 100644 index 0000000..4e027a2 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-43.png differ diff --git a/mini-dropbox/images/mini-dropbox-44.png b/mini-dropbox/images/mini-dropbox-44.png new file mode 100644 index 0000000..d4b90ac Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-44.png differ diff --git a/mini-dropbox/images/mini-dropbox-45.png b/mini-dropbox/images/mini-dropbox-45.png new file mode 100644 index 0000000..b8c2906 Binary files /dev/null and b/mini-dropbox/images/mini-dropbox-45.png differ diff --git a/rename-practice.py b/rename-practice.py new file mode 100644 index 0000000..c79746b --- /dev/null +++ b/rename-practice.py @@ -0,0 +1,310 @@ +#!/usr/bin/env python3 +""" +AWS Practice File Renaming Tool +Automatically renames screenshots sequentially and videos to match folder name. +""" + +import os +import re +import shutil +from pathlib import Path +from typing import List, Tuple + + +def natural_sort_key(filename: str) -> List: + """ + Generate a key for natural sorting (handles numbers correctly). + Example: ['1.png', '2.png', '10.png'] instead of ['1.png', '10.png', '2.png'] + """ + return [int(text) if text.isdigit() else text.lower() + for text in re.split(r'(\d+)', filename)] + + +def get_folder_name(folder_path: str) -> str: + """Extract folder name from path and convert to lowercase.""" + path = Path(folder_path) + folder_name = path.name + return folder_name.lower() + + +def is_already_renamed(filename: str, prefix: str) -> bool: + """Check if file already matches the target naming pattern.""" + # Pattern: prefix-01.ext or prefix.ext (for videos) + pattern = rf'^{re.escape(prefix)}(-\d+)?\.[\w]+$' + return bool(re.match(pattern, filename)) + + +def find_media_files(folder_path: str) -> Tuple[List[str], List[str]]: + """ + Find all image and video files in the folder. + Returns: (image_files, video_files) + """ + image_extensions = {'.png', '.jpg', '.jpeg', '.gif', '.bmp'} + video_extensions = {'.mp4', '.avi', '.mov', '.mkv', '.webm', '.flv'} + + images = [] + videos = [] + + try: + folder = Path(folder_path) + if not folder.exists(): + return [], [] + + for file in folder.iterdir(): + if file.is_file(): + ext = file.suffix.lower() + if ext in image_extensions: + images.append(file.name) + elif ext in video_extensions: + videos.append(file.name) + + # Sort naturally + images.sort(key=natural_sort_key) + videos.sort(key=natural_sort_key) + + return images, videos + + except Exception as e: + print(f"Error scanning folder: {e}") + return [], [] + + +def generate_rename_plan(folder_path: str, images: List[str], videos: List[str], prefix: str) -> List[Tuple[str, str]]: + """ + Generate a list of (old_name, new_name) tuples for renaming. + Returns empty list if no changes needed. + """ + rename_plan = [] + folder = Path(folder_path) + + # Process images + if images: + # Determine zero-padding based on total count + total_images = len(images) + padding = len(str(total_images)) # 2 digits for <100, 3 for <1000, etc. + if padding < 2: + padding = 2 # Minimum 2 digits + + for idx, old_name in enumerate(images, start=1): + # Skip if already in correct format + if is_already_renamed(old_name, prefix): + continue + + ext = Path(old_name).suffix + new_name = f"{prefix}-{str(idx).zfill(padding)}{ext}" + + # Avoid duplicate names + if new_name != old_name and not (folder / new_name).exists(): + rename_plan.append((old_name, new_name)) + + # Process videos + if videos: + if len(videos) == 1: + # Single video: rename to {prefix}.ext + old_name = videos[0] + ext = Path(old_name).suffix + new_name = f"{prefix}{ext}" + + if not is_already_renamed(old_name, prefix) and new_name != old_name: + rename_plan.append((old_name, new_name)) + else: + # Multiple videos: rename to {prefix}-part1.ext, {prefix}-part2.ext, etc. + for idx, old_name in enumerate(videos, start=1): + if is_already_renamed(old_name, prefix): + continue + + ext = Path(old_name).suffix + new_name = f"{prefix}-part{idx}{ext}" + + if new_name != old_name and not (folder / new_name).exists(): + rename_plan.append((old_name, new_name)) + + return rename_plan + + +def display_preview(rename_plan: List[Tuple[str, str]]) -> None: + """Display a preview of all rename operations.""" + if not rename_plan: + print("\n✓ All files are already properly named. No changes needed.") + return + + print("\nPreview of changes:") + print("-" * 60) + + # Find max length for alignment + max_old_len = max(len(old) for old, _ in rename_plan) + + for old_name, new_name in rename_plan: + print(f" {old_name.ljust(max_old_len)} → {new_name}") + + print("-" * 60) + print(f"Total: {len(rename_plan)} file(s) to rename") + + +def execute_renaming(folder_path: str, rename_plan: List[Tuple[str, str]]) -> int: + """ + Execute the renaming operations. + Returns: number of files successfully renamed + """ + folder = Path(folder_path) + success_count = 0 + + for old_name, new_name in rename_plan: + try: + old_path = folder / old_name + new_path = folder / new_name + + old_path.rename(new_path) + success_count += 1 + + except Exception as e: + print(f" ✗ Error renaming {old_name}: {e}") + + return success_count + + +def move_to_images_folder(folder_path: str) -> int: + """ + Create an 'images' folder and move all media files into it. + Returns: number of files successfully moved + """ + folder = Path(folder_path) + images_folder = folder / "images" + + # Create images folder if it doesn't exist + try: + images_folder.mkdir(exist_ok=True) + print(f"\n📁 Created folder: {images_folder}") + except Exception as e: + print(f"\n✗ Error creating images folder: {e}") + return 0 + + # Find all media files in the current folder + media_extensions = {'.png', '.jpg', '.jpeg', '.gif', '.bmp', + '.mp4', '.avi', '.mov', '.mkv', '.webm', '.flv'} + + moved_count = 0 + + for file in folder.iterdir(): + if file.is_file() and file.suffix.lower() in media_extensions: + try: + destination = images_folder / file.name + shutil.move(str(file), str(destination)) + moved_count += 1 + except Exception as e: + print(f" ✗ Error moving {file.name}: {e}") + + return moved_count + + +def create_readme_file(folder_path: str, prefix: str) -> bool: + """ + Create a README.md file with the folder name. + Returns: True if successful, False otherwise + """ + folder = Path(folder_path) + readme_name = f"{prefix.upper()}-README.md" + readme_path = folder / readme_name + + try: + # Create README with basic template + with open(readme_path, 'w', encoding='utf-8') as f: + f.write(f"# {prefix.upper()}\n\n") + f.write(f"## Overview\n\n") + f.write(f"\n\n## Screenshots\n\n") + f.write(f"Images are located in the `images/` folder.\n") + + print(f"\n📝 Created README: {readme_name}") + return True + + except Exception as e: + print(f"\n✗ Error creating README file: {e}") + return False + + +def main(): + """Main function.""" + print("\n" + "=" * 60) + print(" AWS Practice File Renaming Tool") + print("=" * 60) + + # Get folder path from user + print("\nEnter the folder path (e.g., PRACTICE/EC2):") + print("or press Ctrl+C to exit") + + try: + folder_path = input("\nFolder path: ").strip() + except KeyboardInterrupt: + print("\n\nExited by user.") + return + + if not folder_path: + print("Error: No folder path provided.") + return + + # Validate folder exists + folder = Path(folder_path) + if not folder.exists(): + print(f"\n✗ Error: Folder '{folder_path}' does not exist.") + return + + if not folder.is_dir(): + print(f"\n✗ Error: '{folder_path}' is not a directory.") + return + + # Extract folder name for prefix + prefix = get_folder_name(folder_path) + print(f"\nScanning folder: {folder_path}") + print(f"File prefix: {prefix}") + + # Find media files + images, videos = find_media_files(folder_path) + + if not images and not videos: + print("\n✗ No media files found in this folder.") + print(" Supported formats: PNG, JPG, JPEG, GIF, BMP (images)") + print(" MP4, AVI, MOV, MKV, WEBM, FLV (videos)") + return + + print(f"\nFound {len(images)} image(s) and {len(videos)} video(s)") + + # Generate rename plan + rename_plan = generate_rename_plan(folder_path, images, videos, prefix) + + # Display preview + display_preview(rename_plan) + + if not rename_plan: + return + + # Ask for confirmation + print("\nProceed with renaming? (yes/no):") + + try: + response = input("Your choice: ").strip().lower() + except KeyboardInterrupt: + print("\n\nExited by user.") + return + + if response not in ['yes', 'y']: + print("\nRenaming cancelled.") + return + + # Execute renaming + print("\nRenaming files...") + success_count = execute_renaming(folder_path, rename_plan) + + print(f"\n✓ Successfully renamed {success_count} file(s)!") + + # Move files to images folder + print("\nMoving files to 'images' folder...") + moved_count = move_to_images_folder(folder_path) + + print(f"\n✓ Successfully moved {moved_count} file(s) to images folder!") + + # Create README file + create_readme_file(folder_path, prefix) + + +if __name__ == "__main__": + main()