This post details my journey of designing and deploying a secure, highly available, and scalable **3-Tier Web Application Infrastructure **entirely on AWS using the Cloud Development Kit (CDK) and the AWS CLI. I used a code-only approach to ensure repeatability and automate all operational tasks.
Architecture Diagram
3 Tier Architecture Diagram
VPC Architecture Diagram
Prerequisites: Security and Control
Before writing a single line of infrastructure code, I focused on securing the deployment environment and controlling costs.
💰 Cost Control: Billing Alarms as Guardrails
As someone who have just over a month experience in AWS, overspending is a common fear. I implemented two essential CloudWatch Billing Alarms to monitor estimated AWS charges:
- Safety Net Buffer ($5): A high-level alert created easily via the AWS Console for moderate costs.
- Tighter Threshold ($1): A critical, early-warning alert deployed using the AWS CLI.
This dual approach provides both an easy to use alarm with scripts for automation offering an immediate warning for any unexpected spending spikes.
👤 Principle of Least Privilege: Dedicated IAM User
To ensure secure, auditable deployments, I created a dedicated IAM User with the minimum permissions required.
- Custom IAM Policy: Based on a mental model of required resources (VPC, EC2, RDS, etc.), I defined a custom 3-tier-deployment-policy.
- CLI Access: Generated Access Keys using aws iam create-access-key and securely stored them in a local CLI profile instead of exposing them in scripts.
While running the script, I encountered a frustrating error when attaching the policy to the user: An error occurred (ValidationError) when calling the AttachUserPolicy operation: Invalid ARN: Could not be parsed!.
The root cause was a shell scripting issue: when capturing the policy ARN using command substitution ($()), the tee command in my logging function was writing log messages to Standard Output (stdout), which were then accidentally perfixed to the ARN.
To fix this, I modified the logging function to redirect the tee command's output to Standard Error (stderr) (>&2). This ensured only the valid ARN was captured on stdout for the AWS CLI command.
Infra-as-Code: Writing the Entire Stack in AWS CDK
The AWS Cloud Development Kit (CDK) allowed me to define the entire cloud infrastructure using Python.
Before deploying, I executed the one-time setup command:
cdk bootstrap aws://<account-id>/<region>
This command creates the CDKToolkit CloudFormation Stack, which provisions essential resources (like an S3 bucket and IAM roles) necessary for the CDK CLI to deploy assets and templates. This process is essentially known as bootstrapping.
- Modular Design: Constructs, Stacks, and App
I structured the project for clarity and reusability:
Component | Role |
---|---|
Constructs | The basic building blocks; defines how an individual resource or group of resources is created. Example: VpcConstruct (defines subnets, NATs, etc.) |
Stacks | High-level groupings; instantiates Constructs with environment-specific configurations and parameters. Example: VpcConstruct with specific CIDRs) |
App | The entry point; connects all stacks, defines their deployment order, and specifies the target AWS account/region. Example: cdk deploy executes the App |
The 3-Tier Architecture Breakdown
The infrastructure is divided into three distinct tiers, each with a defined role and strict security groups.
- Network Tier: The Private Cloud (VPC)
The is the foundational tier and is a single VPC spanning two Availability Zones (AZs) for high availability and fault tolerance.
Subnet Type | Role | Purpose/Hosts |
---|---|---|
Public Subnets (3) | For internet-facing resources that require an Internet Gateway (IGW). | Hosts the Application Load Balancer (ALB) and NAT Gateway(s). |
Private Subnets (3) | For backend application resources that are isolated from the internet. | Hosts the EC2 Auto Scaling Group (ASG) and RDS database. |
One thing that I loved about CDK is the way it abstracts away manual networking setup: It automatically Defining the VPC subnet with proper routes so that I dont have to manually update route table unless I have a specific rqeuirement to do so. And by specifying nat_gateways=1, it automatically provisions a NAT Gateway in a public subnet and updates the private subnet route tables to direct all outbound traffic through it.
Network Stack CloudForamtion Dashboard
- Web Tier: The Public Entry Point
The Application Load Balancer (ALB) serves as the internet-facing entry point and is designed as such:
- Security Group: Only permits inbound traffic on HTTP (port 80).
- Target Group: Directs traffic to the application instances on port 8080.
- Health Check: Configured to check the root path (/) to ensure only healthy instances receive traffic.
Web Stack CloudForamtion Dashboard
- App Tier: The Application Logic
The application runs on Amazon Linux 2 EC2 instances managed by an Auto Scaling Group (ASG), deployed entirely within the private subnets (with egress).
- Security: The ASG instances are private with egress. They can only be reached by the ALB's security group on port 8080.
- Outbound Egress: Traffic for updates or external API calls is routed securely through the NAT Gateway.
- Identity & Credentials: An attached IAM role grants permissions for SSM, CloudWatch Logs/Metrics. It has read access to the database credentials stored in Secrets Manager, ensuring no plaintext credentials are ever stored on the instance.
- Observability Config: The CloudWatch Agent configuration is stored in an SSM Parameter Store. The instance user data script fetches the config at startup, making monitoring adjustments easy without ASG redeployment.
App Stack CloudForamtion Dashboard
- Database Tier: The Isolated Backend
A PostgreSQL instance on Amazon RDS is deployed in the PRIVATE ISOLATED subnets.
- Isolation: The database has no internet access, inbound or outbound.
- Strict Access Control: Access is only permitted from the application’s ASG Security Group to the RDS Security Group on TCP port 5432.
- Secret Management: RDS credentials are automatically provisioned and stored in AWS Secrets Manager, and only the App Tier's IAM role is authorized to retrieve them.
Operational Excellence: CLI and Observability
For observability, I built a set of Bash scripts utilizing the AWS CLI.
I created scripts for:
- Instance/Target Health Checks: Quick validation of the environment state.
- Resource Information: Fetching details on RDS and EC2 instances.
- SSH via SSM: Secure, tunnel-less access to private instances.
- Application Log Retrieval: Centralized access to application logs.
Script for getting infra info
Leveraging EC2 Metadata
One thing that I learned when doing this project was for checking the local health and configuration of an instance from within the instance itself, the application uses EC2 Metadata. This data is always available locally via the non-routable IP address: 169.254.169.254.
Complete Observability with CloudWatch
The deployed infrastructure is fully observable using the CloudWatch Agent and Alarms.
- Agent Configuration: The agent collects system metrics (like CPU) and combined application logs (including NGINX logs and a custom log for RDS connectivity checks).
- CloudWatch Alarms:
1. Performance: Notifies if EC2 CPU utilization exceeds 80%.
2. Capacity: Warns when RDS storage capacity is nearing its limit.
CloudWatch Alarm Dashboard
This setup ensures that the entire environment, from deployment to scaling and health checks is fully automated, secure, and easily monitored from the terminal.
Conclusion
This project demonstrated the deployment of a multi tier web app on AWS using purely code based approach. This project helped me clear my doubts on availability zones, ASG and target groups. It was also a great learning for making secuirty groups to build a complete, isolated subnets. I had a lot of fun doing this project and especially writing the modular code in Python. Up next, I will explore Terraform, which is a declarative configuration language (HCL) and is differnet from the CDK (general purpose programming model). Onward to the next challenge, the cloud awaits! ☁️
Feel free to check my github repo on this project:
https://github.com/asim-makes/3-tier-infra
Top comments (0)