Skip to content

ShenLoong99/aws-terraform-2-tier-webapp

Repository files navigation

Contributors Forks Stargazers Issues Unlicense License LinkedIn

๐Ÿš€ High Availability Two-Tier WebApp

cover-image
A Resilient Node.js Application with Automated Scaling and Real-Time Observability
โš ๏ธ Cost Advisory: This project is not fully compatible with the AWS Free Tier. While designed to be low-cost, charges will be incurred for the Application Load Balancer (ALB), NAT Gateways (if applicable), and Multi-AZ storage/compute. Expect a small daily charge while the infrastructure is active.

AWS Terraform Node.js


GitHub Actions
Infrastructure CI Production Deployment Update Documentation


Last Commit Repo Size pre-commit Checkov Security

Explore the docs ยป

Table of Contents
  1. About The Project
  2. Built With
  3. Use Cases
  4. Architecture
  5. File Structure
  6. Technical Reference
  7. Getting Started
  8. GitOps & CI/CD Workflow
  9. Usage
  10. Roadmap
  11. Challenges
  12. Well Architected Framework
  13. Acknowledgements

About The Project

This project demonstrates a production-ready Two-Tier Architecture deployed on AWS using Terraform. It features a Node.js web application running on a fleet of EC2 instances managed by an Auto Scaling Group (ASG) for high availability and a Multi-AZ RDS MySQL database for data persistence.

The core focus of this implementation is Operational Excellence. By integrating the CloudWatch Agent via SSM Parameter Store, the infrastructure automatically captures system logs and application health metrics, providing deep visibility into the environment without manual configuration of individual servers.

Built With

  1. Infrastructure as Code (IaC) & Backend

    terraform

    • Terraform & Terraform Cloud: Orchestrates the entire lifecycle of the AWS resources. State is managed remotely in Terraform Cloud to enable team collaboration and safe state-locking.
    • HCL (HashiCorp Configuration Language): Used to define modular resources (VPC, EC2, RDS) for high reusability.
  2. Compute & Application Layer

    nodejs asg alb

    • Node.js & Express: The core application engine handling RESTful CRUD operations.
    • Auto Scaling Group (ASG): Ensures high availability by maintaining a minimum of 2 instances across multiple Availability Zones (AZs).
    • Launch Templates: Standardizes the "Golden Image" configuration, including instance tags, IAM profiles, and UserData.
    • Application Load Balancer (ALB): Acts as the single entry point, distributing traffic and performing health checks to ensure users never hit a failing server.
  3. Data & Networking Tier

    rds vpc igw cloudwatch

    • Amazon RDS (MySQL): A managed database instance residing in a Private Subnet Group, isolated from the public internet.
    • Custom VPC Architecture: Ensures high availability by maintaining a minimum of 2 instances across multiple Availability Zones (AZs).
      • Public Subnets: Hosting the ALB and (temporarily) the EC2 instances.
      • Private Subnets: Dedicated to data persistence.
      • Internet Gateway (IGW): Enables external connectivity for the web tier.
      • Route Tables: Manages the traffic flow between the internet and internal subnets.
    • CloudWatch Logs: Dedicated log group for flow analysis, providing a complete audit trail for security and compliance.
  4. Observability & Management

    ssm cloudwatch s3 iam vpc-flow-logs

    • AWS Systems Manager (SSM) Parameter Store: Centrally stores the CloudWatch Agent JSON configuration, allowing for "Configuration-as-Code" updates.
    • CloudWatch Logs: Centralized log repository for application journals and cloud-init output.
    • Amazon S3: Used for ALB Access Logging. Every request processed by the load balancer is logged as a gzipped file, capturing client IPs, request paths, and server response times for deep traffic analysis.
    • IAM Roles & Instance Profiles: Implements Least Privilege Access, granting EC2 only the permissions needed to write to CloudWatch and read from SSM.
    • VPC Flow Logs: Captures IP traffic metadata (source/destination IPs, ports, and protocols) for all network interfaces within the VPC.

Use Cases

  • Fault-Tolerant Web Hosting: Proving that the application remains live even if an entire Availability Zone (AZ) fails.
  • Automated Monitoring Setup: A reusable pattern for DevOps engineers to inject monitoring agents into a fleet of servers automatically.
  • Observability Template: A reference for setting up unified logging across a dynamic fleet of servers.
  • Infrastructure Hardening: Demonstration of IAM least-privilege roles and secure VPC networking.

Architecture

architecture-diagram


The system is deployed into a custom VPC spanning two Availability Zones to ensure high availability. The Web Tier is managed by an Auto Scaling Group (ASG) and distributed by an Application Load Balancer (ALB), while the Data Tier is strictly isolated in private subnets with restricted ingress.

  1. Client Ingress & Routing: Traffic enters via the Internet Gateway (IGW) and is intercepted by the Application Load Balancer. The ALB acts as the single entry point, offloading SSL (if configured) and performing health checks to ensure traffic only reaches healthy EC2 nodes.
  2. Elastic Compute & Scaling: The ASG maintains a minimum of 2 instances. It utilizes a Target Group to seamlessly register/deregister instances during scaling events or failovers, ensuring zero-downtime deployments.
  3. Multi-Layered Observability:
    • Host Level: The CloudWatch Agent retrieves its ssm:AmazonCloudWatch-linux-webapp configuration to stream /var/log/cloud-init-output.log and application logs.
    • Network Level: VPC Flow Logs capture all IP traffic metadata to monitor for rejected connection attempts.
    • Access Level: ALB Access Logs are archived in Amazon S3 for long-term auditability and traffic pattern analysis.
  4. Secure Data Persistence: The Node.js application communicates with the RDS MySQL instance located in the Private Subnets. Security Groups are configured using Security Group Referencing (allowing 3306 ONLY from the Web Security Group), ensuring the database is never exposed to the public internet.
  5. The Logging & Auditing Ecosystem:
    • S3 Bucket (ALB Access Logs): Every request hitting the Load Balancer is logged into a dedicated Amazon S3 bucket. This provides a durable audit trail of client IPs, request paths, and response latencies, crucial for compliance and traffic analysis.
    • CloudWatch (System & Network):
      • Host Level: EC2 instances stream /var/log/cloud-init-output.log and application logs to CloudWatch Logs.
      • Network Level: VPC Flow Logs capture all IP traffic metadata to monitor for rejected connection attempts.
  6. IAM Roles & Security Governance:

    Logging functionality is enabled through an IAM Instance Profile attached to the EC2 instances. This role follows the Principle of Least Privilege, granting specific permissions to:

    • Retrieve configurations from SSM Parameter Store.
    • Write log streams to CloudWatch via the CloudWatchAgentServerPolicy.
    • Allow the ALB service principal to write access logs to the S3 Bucket via a bucket policy.

File Structure

AWS-TERRAFORM-2-TIER-WEBAPP/
โ”œโ”€โ”€ ๐Ÿ“ .github/               # GitHub Actions or workflows
โ”‚   โ””โ”€โ”€ workflows/             # CI/CD Pipeline Definitions
โ”‚       โ”œโ”€โ”€ cd.yml             # Production Deployment
โ”‚       โ”œโ”€โ”€ ci.yml             # Terraform PR Insights (Checkov, TFLint, Plan)
โ”‚       โ””โ”€โ”€ documentation.yml  # Automated Documentation Sync via terraform-docs
โ”œโ”€โ”€ ๐Ÿ“ .terraform/            # Terraform working directory
โ”œโ”€โ”€ ๐Ÿ“ assets/                # Project documentation and images
โ”œโ”€โ”€ ๐Ÿ“ modules/               # Reusable infrastructure modules
โ”‚   โ”œโ”€โ”€ ๐Ÿ“ alb/               # Application Load Balancer configuration
โ”‚   โ”œโ”€โ”€ ๐Ÿ“ ec2/               # Compute tier configuration
โ”‚   โ”‚   โ”œโ”€โ”€ ๐Ÿ“ scripts/       # User data and initialization scripts (e.g., user_data.tftpl)
โ”‚   โ”‚   โ”œโ”€โ”€ main.tf           # EC2 Launch Template and ASG resources
โ”‚   โ”‚   โ”œโ”€โ”€ outputs.tf        # EC2-specific output values
โ”‚   โ”‚   โ”œโ”€โ”€ providers.tf      # Version constraints (No cloud block!)
โ”‚   โ”‚   โ””โ”€โ”€ variables.tf      # EC2-specific input variables
โ”‚   โ”œโ”€โ”€ ๐Ÿ“ rds/              # Managed database configuration
โ”‚   โ”œโ”€โ”€ ๐Ÿ“ security_groups/   # Networking security rules
โ”‚   โ””โ”€โ”€ ๐Ÿ“ vpc/               # Virtual Private Cloud network setup
โ”œโ”€โ”€ ๐Ÿ“ scripts/               # Automation & Validation Scripts
โ”‚   โ”œโ”€โ”€ verify-deployment.sh   # Post-deployment test script
โ”‚   โ””โ”€โ”€ test-functions.sh      # Functional API curl test script
โ”œโ”€โ”€ .gitignore                 # Files excluded from version control
โ”œโ”€โ”€ .terraform.lock.hcl        # Provider dependency lock file
โ”œโ”€โ”€ .pre-commit-config.yaml    # Local git-hook orchestration
โ”œโ”€โ”€ .tflint.hcl                # TFLint AWS ruleset configuration
โ”œโ”€โ”€ .checkov.yml               # Checkov scan ignore list
โ”œโ”€โ”€ .terraform-docs.yml        # Config for terraform documentation during workflow
โ”œโ”€โ”€ main.tf                    # Root module orchestrating all tiers
โ”œโ”€โ”€ outputs.tf                 # Global output values (e.g., ALB DNS)
โ”œโ”€โ”€ project-key.pem            # Private SSH key for EC2 access
โ”œโ”€โ”€ providers.tf               # AWS provider and Terraform version config
โ””โ”€โ”€ README.md                  # Project documentation (Auto-injected by terraform-docs)
โ”œโ”€โ”€ README.template.md         # Documentation template
โ”œโ”€โ”€ terraform.tfstate          # Current state of deployed infrastructure
โ”œโ”€โ”€ terraform.tfstate.backup   # Previous state backup
โ””โ”€โ”€ variables.tf               # Global input variables

Technical Reference

This section is automatically updated with the latest infrastructure details.
Detailed Infrastructure Specifications

Requirements

Name Version
terraform >= 1.5.0
aws ~> 5.0
local ~> 2.0
tls ~> 4.0

Modules

Name Source Version
alb ./modules/alb n/a
ec2 ./modules/ec2 n/a
rds ./modules/rds n/a
security_groups ./modules/security_groups n/a
storage ./modules/storage n/a
vpc ./modules/vpc n/a

Resources

Name Type
aws_key_pair.generated_key resource
local_file.private_key resource
tls_private_key.main resource

Inputs

Name Description Type Default Required
admin_ip CIDR block for admin IPs allowed to access resources string n/a yes
availability_zones Availability zones to use list(string)
[
"us-east-1a",
"us-east-1b"
]
no
aws_region AWS region to deploy resources in string "us-east-1" no
db_password RDS root password string n/a yes
private_subnets Private subnet CIDR blocks list(string)
[
"10.0.3.0/24",
"10.0.4.0/24"
]
no
public_subnets Public subnet CIDR blocks list(string)
[
"10.0.1.0/24",
"10.0.2.0/24"
]
no
vpc_cidr VPC CIDR block string "10.0.0.0/16" no

Outputs

Name Description
alb_dns_name The DNS name of the load balancer
alb_sg_id Security Group ID of the ALB
alb_target_group_arn n/a
app_url The clickable URL for the application
aws_region The AWS region in use
ec2_instance_ids IDs from the EC2 module
ec2_public_ips Public IP addresses of the EC2 instances
ec2_sg_id Security Group ID of the EC2 instances
private_subnet_ids Private subnet IDs
public_subnet_ids Public subnet IDs
rds_endpoint The connection endpoint for the RDS instance
rds_sg_id Security Group ID of the RDS instance
vpc_id VPC ID

Getting Started

Prerequisites

  • AWS Account with Bedrock Claude 3.5 model access enabled.
  • Terraform CLI (v1.5.0+) installed locally.
  • Terraform Cloud account for remote state management.

Terraform Cloud State Management

  1. Create a new Workspace with github version control workflow in Terraform Cloud.
  2. In the Variables tab, add the following Terraform Variables:
  3. Add the following Environment Variables (AWS Credentials):
    • AWS_ACCESS_KEY_ID
    • AWS_SECRET_ACCESS_KEY
  4. Run the command ni Terraform CLI:
    terraform login
  5. Create a token and follow the steps in browser to complete the Terraform Cloud Connection.
  6. Add the backend block in terraform code block:
    backend "remote" {
      hostname     = "app.terraform.io"
      organization = <your-organization-name>
      workspaces {
        name = <your-workspace-name>
      }
    }
  7. Run the command in Terraform CLI to migrate the state into Terraform Cloud:
    terraform init -migrate-state

Installation & Deployment

  1. Clone the Repository:
    git clone https://github.com/ShenLoong99/aws-terraform-2-tier-webapp.git
  2. Provision Infrastructure:
    Terraform Cloud โ†’ Initialize & Apply: Push your code to GitHub. Terraform Cloud will automatically detect the change, run a plan, and wait for your approval.
  3. Observe workflow:
    GitHub (GitOps) โ†’ Github actions: Observe the process/workflow of CI/CD in the actions tab in GitHub.

GitOps & CI/CD Workflow

This project uses a fully automated GitOps pipeline to ensure code quality and deployment reliability. The Pre-commit framework implements a "Shift-Left" strategy, ensuring that code is formatted, documented, and secure before it ever leaves your machine.

Workflow

  1. Branch Protection Rulesets
    To ensure high code quality and prevent unauthorized changes to the production environment, the main branch is governed by a GitHub Branch Ruleset.
    • Pull Request Mandatory: No code can be pushed directly to main. All changes must originate from a feature branch and be merged via a Pull Request.
    • Required Status Checks: The Infrastructure CI (Terraform Plan & Static Analysis) must pass successfully before a merge is permitted.
    • Bypass Authority: The dedicated GitHub App is added to the Bypass List with "Always allow" permissions. This allows the bot to push documentation updates directly to main without being blocked by PR requirements.
  2. Pre-commit
    • Tool: Executes terraform fmt, terraform validate, TFLint, terraform_docs and checkov to ensure the code is clean.
    • Trigger: Runs on every git commit.
    • Outcome: If any check fails, the commit is blocked. You fix the error, re-add the file, and commit again.
  3. Continuous Integration (PR)
    • Tool: Executes terraform fmt -check, terraform validate and checkov, then do plan and cost estimation and print it on PR.
    • Trigger: Runs on every Pull Request.
    • Outcome: This acts as the "Gatekeeper" before code is merged to main.
  4. Continuous Delivery (Deployment)
    • Tool: Terraform Cloud + GitHub Actions OIDC.
    • Trigger: Merges to the main branch.
    • Outcome: The pipeline verifies the infrastructure state and runs a post-deployment health check with(health-check.sh & smoke-test-website.sh).
  5. Dynamically update readme documentation
    • Tool: terraform_docs + GitHub Actions.
    • Trigger: Merges to the main branch.
    • Outcome: The pipeline verifies the infrastructure state from Terraform Cloud, retrieve outputs from Terraform Cloud and update the readme documentation file dynamically.

Prerequisites for GitOps

  • Repository Secret TF_API_TOKEN: Required for GitHub to communicate with Terraform Cloud.
  • Trigger: A GitHub Actions OIDC role (GitHubActionRole) allows the runner to verify AWS resources without long-lived keys.
  • Automated Documentation via GitHub App: Instead of using a Personal Access Token (PAT) or the default GITHUB_TOKEN, this project uses a custom GitHub App for automated tasks.
    Secret Description Source
    BOT_APP_ID The unique numerical ID assigned to your GitHub App. App Settings > General
    BOT_PRIVATE_KEY The full content of the generated .pem private key file. App Settings > Private keys

Usage & Testing

  1. Web Tier Verification

    Once the Auto Scaling Group shows InService instances:

    1. Access the App: Copy the Public IP of any running instance (or the Load Balancer DNS if applicable) and paste it into your browser at port 3000.
      to-do-list-served-server-0
    2. Add an Item: Type a task (e.g., "Setup CloudWatch Alarms") in the input box and click Add. Verify the item appears in the list.
      to-do-list-crud
    3. Mark Complete: Click the Complete button. The item should move or change status, confirming a PUT request to the RDS MySQL backend.
    4. Delete an Item: Click Delete. Verify the item is removed from the UI and the database via a DELETE request.
  2. Verification of Load Balancing (Traffic Distribution)

    The application is designed to demonstrate horizontal scaling and stateless execution. You can observe the Load Balancer's "Round Robin" or "Least Outstanding Requests" algorithm in action by following these step:

    1. Observe the Footer: The webpage UI displays the Metadata Instance ID of the specific EC2 server that processed the request.
    2. Refresh the Browser: By performing refresh repeatedly, you will notice the Server ID toggle between the two instances in the Auto Scaling Group.
      to-do-list-served-server-0
    3. The Significance: This confirms that the ALB is successfully distributing ingress traffic across multiple Availability Zones. It also demonstrates that the application state is correctly decoupled; despite switching between different backend servers, the To-Do List data remains consistent because both instances are connected to the same central RDS MySQL database.
  3. Validating Multi-AZ Resilience

    Manually terminate one instance in the console. Watch the ASG Activity Tab; you will see the ASG detect the "Unhealthy" status and automatically spin up a replacement in a different subnet to maintain the desired capacity of 2.


    test-stop-1-ec2-instance target-groups-health-status
  4. ALB Access Log Audit

    1. Generate Traffic: Click through your application for 5-10 minutes (add/delete some tasks).
    2. Verify S3 Storage: Navigate to the Amazon S3 Console and open your logging bucket (e.g., webapp-alb-logs-...).
    3. Inspect Logs: Follow the folder path: AWSLogs/ <ACCOUNT_ID> /elasticloadbalancing/ <REGION> /.
      alb-s3-access-logs
    4. Download the file: Extract it and view it in notepad
      alb-s3-access-logs-output
  5. CloudWatch Log Verification

    Analyzing Security Group Effectiveness

    1. Generate a "Reject" Event: Attempt to SSH into your RDS database directly from your home computer. Since the RDS is in a private subnet, this connection will time out.
    2. Verify in CloudWatch: Navigate to CloudWatch > Log Groups > /aws/vpc/flow-logs-debug.
    3. Search for Rejections: Look for logs where the action is REJECT. This confirms that your Security Group "Least Privilege" rules are actively blocking unauthorized external traffic.
      vpc-flow-logs-cloudwatch

    EC2 user data bootstrap log

    • Search for the log group /aws/ec2/webapp-logs. Note that logs are organized by Instance ID, allowing you to track logs even after the instance that created them has been terminated.
      ec2-cloudwatch-logs

๐Ÿ› ๏ธ Advanced Debugging Tips

If the application is not responding or you notice issues with the initial setup, SSH into an instance and use these commands to diagnose the root cause.

  1. Debugging & Troubleshooting
    • Check the Network Tab: Open Developer Tools (F12) and ensure requests to /health are returning 200 OK.
    • Incognito Mode: If "Mark Complete" or "Delete" buttons fail with a `removeChild` error in the console, try the app in an Incognito Window. This prevents browser extensions from interfering with the dynamic DOM updates.
    • Hard Reload: Use Ctrl + F5 to ensure your browser isn't running a cached, older version of the JavaScript bundle.
  2. Connecting to the Instance

    There are two primary ways to access your application instance:

    Option 1: Standard SSH
    ssh -i project-key.pem ec2-user@<INSTANCE_PUBLIC_IP>

    Access the virtual machine securely using your generated RSA private key.

    โš ๏ธ IMPORTANT: Ensure you are operating in your Local Terminal linked to the Terraform Cloud Workspace before proceeding with these commands.

    Option 2: AWS Systems Manager (SSM) Session Manager (Easier)

    If you do not have SSH access or want to connect directly via the browser:

    • Navigate to the EC2 Console and select your instance.
    • Click the Connect button at the top.
    • Select the Session Manager tab and click Connect.

    Note: This is the recommended secure method as it doesn't require opening port 22 to the public internet.

  3. Inspecting Initialization (Cloud-Init)

    Cloud-init logs record the UserData execution during the first boot. Use these if Node.js or the CloudWatch Agent fails to install.

    Command Purpose
    sudo tail -f /var/log/cloud-init-output.log Live Monitor: Watch the setup script execute in real-time.
    cloud-init-output
    sudo cat /var/log/cloud-init-output.log Full Audit: View the entire history of the installation phase.
    sudo cat /var/log/cloud-init-output.log | grep -i "error" Error Hunt: Quickly filter for specific installation or network failures.
  4. Application & Service Logs

    The application runs as a background service managed by systemd. Use these to verify if the Node.js API is active.

    Command Purpose
    sudo systemctl status nodeserver Heartbeat Check: Verify if the nodeserver service is active (running).
    sudo-systemctl-status-nodeserver
    sudo journalctl -u nodeserver.service -n 50 --no-pager Code Logs: View the last 50 lines of application output (console logs).
    curl -I http://localhost:3000 Local Health Check: Confirm the web server is responding inside the instance.
    curl-localhost
  5. Database Connectivity Tier

    Verify that your EC2 instance can talk to the Private RDS instance across subnets. Note: You must install the MySQL client on the EC2 instance first to run these queries.

    Step Command
    1. Install Client sudo dnf install mariadb105 -y
    2. Test Connection mysql -h <RDS_ENDPOINT> -u admin -p<PASSWORD> webapp_2_tier_db -e "SELECT * FROM items;"

    Troubleshooting: If the connection times out after installing the client, ensure the RDS Security Group has an Inbound Rule allowing Port 3306 from the EC2 Security Group ID.

Roadmap

    Roadmap

    • Multi-AZ Deployment: Instances distributed across different subnets and managed by an Auto Scaling Group (ASG).
    • Config-as-Code: SSM Parameter Store used for unified agent settings.
    • Cost Controls: Automated log retention and Infrequent Access tiers.
    • CI/CD Integration: Automate Terraform Apply via GitHub Actions.
    • ALB Integration: Application Load Balancer implemented as a single entry point.
    • Traffic Auditing: S3-based ALB access logging for long-term compliance.
    • Network Visibility: VPC Flow Logs integrated with CloudWatch for deep packet metadata analysis.
    • Dynamic UI Rendering: Fixed Terraform interpolation and Node.js DOM conflicts for a resilient frontend.
    • Reliability & Failure Design: Implemented ASG with health_check_type = "ELB" and instance_refresh to ensure automated recovery and zero-downtime updates.
    • HTTPS: Implement SSL termination using AWS Certificate Manager (ACM).
    • Dynamic Scaling Policy: Implement aws_autoscaling_policy to scale out based on CPU utilization (currently configured as a fixed-size reliable fleet).
    • Bastion Host: Move the database and web servers to private subnets for enhanced security.
    • Edge Security (WAF): Deploy AWS WAF to protect the Application Load Balancer from common web exploits like SQL Injection and Cross-Site Scripting (XSS).

Challenges

Challenge Solution
Logging Visibility Amazon Linux 2023 removed /var/log/messages. Switched agent tracking to cloud-init-output.log and application-specific paths.
The "3 Instances" Mystery Observation of 3 instances running simultaneously during updates. Learned that the min_healthy_percentage (50%) forces ASG to launch a new instance before killing the old one.
ASG Grace Periods Default cooldowns and grace periods (300s) caused delays. Adjusted health_check_grace_period for faster development cycles.
Terraform Cloud State Migration Moving to a remote backend caused local_file resources to "flap" (recreate on every apply). I learned that local file resources are incompatible with ephemeral cloud runners and shifted to managing keys via sensitive outputs.
Log Class UI Errors Encountered "Operation not supported" errors when viewing INFREQUENT_ACCESS logs. Learned that while IA saves costs, it has a UI propagation delay and limited "Live Tail" support compared to STANDARD logs.
IAM Propagation Encountered AccessDenied during initial agent boot. Ensured the AmazonSSMManagedInstanceCore policy was attached to the Instance Profile.
Free Tier Constraints To avoid unexpected costs, I limited the max_size of the ASG and deferred automatic scaling policies. This highlights the balance between high-availability architecture and budget management in a dev/test environment.
Load Balancer Integration Configuring the aws_lb_target_group to correctly perform health checks on Port 3000. Learned that the ASG must be explicitly attached to the Target Group to receive traffic.
Public vs. Private Security Successfully isolated the RDS in a private subnet. Navigating the challenge of allowing EC2-to-RDS communication while keeping the database shielded from the public internet.
Statelessness To achieve this, the Node.js application was built to be stateless. No session data is stored on the local disk of the EC2 instance; all persistent data is offloaded to RDS. This allows the ALB to swap servers mid-session without the user losing their progress.
ALB Algorithms By default, the ALB uses a round-robin approach at the request level. If you don't see the ID change immediately, it may be due to HTTP Keep-Alive (the browser reusing a connection) or Stickiness settings (which are disabled in this project to better demonstrate load distribution).
Terraform Variable Interpolation in UserData One of the most complex challenges was passing JavaScript code through Terraform's templatefile() function. Terraform interprets ${...} as variables to be replaced. I implemented Double-Dollar Escaping ($${id}) for browser-side template literals and transitioned to standard String Concatenation for complex HTML generation to ensure Terraform and the Bash cat command didn't corrupt the JavaScript logic.
Stateful Database Initialization Ensuring the database schema was ready before the application started required a robust Node.js initialization script that handles "Table Already Exists" errors and performs conditional migrations (like adding the completed column) without crashing the service.
Browser Extension DOM Interference I identified that the browser extension MetaMask can cause NotFoundError when scripts rapidly update the DOM via innerHTML. Documented the requirement for testing in Incognito Mode to isolate application logic from third-party browser scripts.

๐Ÿ›๏ธ AWS Well-Architected Framework Alignment

This project demonstrates a Cost-Optimized Development Environment, prioritizing security and observability over high-availability redundancy to minimize AWS expenditure while maintaining production-grade standards.

Pillar Technical Implementation & Alignment
operation-excellence
Operational Excellence
  • Infrastructure as Code: 100% of the stack is provisioned via Terraform, allowing for consistent, repeatable deployments.
  • Automated Observability: Integrated CloudWatch Agent via SSM Parameter Store for automated log collection from EC2 instances without manual intervention.
  • GitOps CI/CD: Implemented a fully automated pipeline for testing, linting, and deploying infrastructure changes via GitHub Actions and Terraform Cloud.
  • Standardized Deployment: Uses a modularized file structure to separate networking, compute, and database logic for better maintainability.
security
Security
  • Network Isolation: RDS database is strictly isolated in private subnets with no public internet ingress.
  • Traffic Control: Security Groups utilize referencing (e.g., RDS only allows 3306 from the Web SG) rather than open IP ranges.
  • Auditability: VPC Flow Logs and ALB Access Logs provide a complete audit trail of all network traffic and client requests.
reliability
Reliability & Failure Design
  • Data Persistence: RDS MySQL managed database provides automated backups and multi-AZ failover capabilities.
  • Health Monitoring: The Application Load Balancer performs active health checks to notify of instance downtime via CloudWatch Alarms.
performance
Performance Efficiency
  • Elastic Load Balancing: Application Load Balancer (ALB) ensures efficient traffic distribution across the healthy compute fleet.
  • Optimized Compute: Leveraged t3.micro instances for the web tier and managed RDS for optimized database performance.
  • Configuration as Code: Using SSM to push agent configurations avoids manual "SSH-to-all" performance bottlenecks during updates.
cost-optimization
Cost Optimization
  • Free-Tier Alignment: Utilizes t3.micro instances and a single RDS instance to stay within the AWS Free Tier.
  • Log Retention: Set to 1 day to prevent unnecessary storage costs in CloudWatch.
  • Database Tiering: Leveraged db.t3.micro to stay within the AWS Free Tier while still testing Multi-AZ logic.
Sustainability
  • Managed Services: Utilizing RDS reduces the energy overhead of running manual database servers.

Acknowledgements

Special thanks to Tech with Lucy for the architectural inspiration and excellent AWS tutorials that helped shape this pipeline.

About

This project is a 2 tier web application, using EC2 (app layer), RDS (database layer) and ALB (routing). This project comes with a secure VPC environment with proper subnetting and security groups.

Topics

Resources

Stars

Watchers

Forks

Contributors