Henry Hoang

Final Exam

Final exam for Cloud Essentials for Developer module

Final Exam: Deploy FastAPI Application to AWS Cloud

Assignment Metadata

FieldDescription
Assignment NameDeploy FastAPI Application to AWS Cloud
Course05 - Basic Cloud Essentials for Developer
Project Namecloud-fastapi-deployment
Estimated Time480 minutes (8 hours)
FrameworkFastAPI, Docker, AWS (ECR, EC2, S3, RDS/DynamoDB, IAM), GitHub Actions

Overview

In this project exam, you will take the FastAPI application you built in Module 03 - Basic Building Monolith API with FastAPI and deploy it to AWS Cloud. You will replace local services with AWS managed services and set up a CI/CD pipeline for automated deployments.

Architecture Diagram

┌─────────────────────────────────────────────────────────────────────────────┐
│                              AWS Cloud                                       │
│  ┌─────────────┐     ┌─────────────┐     ┌─────────────────────────────┐   │
│  │   GitHub    │────▶│    ECR      │     │          VPC                │   │
│  │   Actions   │     │  (Images)   │     │  ┌─────────────────────┐   │   │
│  └─────────────┘     └──────┬──────┘     │  │    EC2 Instance     │   │   │
│                             │            │  │  ┌───────────────┐  │   │   │
│                             │            │  │  │   FastAPI     │  │   │   │
│                             ▼            │  │  │   (Docker)    │  │   │   │
│                      Pull Image          │  │  └───────┬───────┘  │   │   │
│                             │            │  └──────────┼──────────┘   │   │
│                             ▼            │             │              │   │
│                      ┌──────────────┐    │             ▼              │   │
│                      │   Deploy     │────┼───▶  ┌─────────────┐      │   │
│                      └──────────────┘    │      │    RDS      │      │   │
│                                          │      │ (PostgreSQL)│      │   │
│  ┌─────────────┐                         │      └─────────────┘      │   │
│  │     S3      │◀────────────────────────┼──────────────────────────┘    │
│  │  (Storage)  │                         │                               │
│  └─────────────┘                         └───────────────────────────────┘
│                                                                           │
│  ┌─────────────┐                                                          │
│  │    IAM      │  (Access Control for all services)                       │
│  └─────────────┘                                                          │
└───────────────────────────────────────────────────────────────────────────┘

Learning Objectives

By completing this exam, you will be able to:

  • Create IAM users and configure appropriate permissions for AWS services
  • Build Docker images and push them to Amazon ECR
  • Deploy containerized FastAPI applications on Amazon EC2
  • Configure Amazon RDS (PostgreSQL) as the database backend
  • Implement file storage using Amazon S3
  • Set up CI/CD pipeline using GitHub Actions
  • Apply security best practices with VPC and security groups
  • Utilize environment variables and AWS credentials securely

Prerequisites

Before starting this exam, ensure you have:

  • Completed Module 03: Basic Building Monolith API with FastAPI
  • AWS Account with appropriate permissions
  • GitHub account
  • Docker installed locally
  • AWS CLI installed and configured
  • Basic understanding of cloud concepts from Module 05 lectures

Part 1: IAM Configuration (15%)

Task 1.1: Create IAM User

Create a new IAM user for programmatic access with the following specifications:

SpecificationValue
User Namefastapi-deployer
Access TypeProgrammatic access
Console AccessOptional

Task 1.2: Create and Attach Policies

Create the following permissions for your IAM user:

Required Permissions

ServiceActions Required
ECRecr:GetAuthorizationToken, ecr:BatchCheckLayerAvailability, ecr:GetDownloadUrlForLayer, ecr:BatchGetImage, ecr:PutImage, ecr:InitiateLayerUpload, ecr:UploadLayerPart, ecr:CompleteLayerUpload
EC2ec2:DescribeInstances, ec2:StartInstances, ec2:StopInstances
S3s3:GetObject, s3:PutObject, s3:DeleteObject, s3:ListBucket
RDSrds:DescribeDBInstances, rds:Connect

Task 1.3: Configure AWS CLI

Configure your local AWS CLI with the created credentials:

aws configure
# Enter your Access Key ID, Secret Access Key, Region (ap-southeast-1), Output format (json)

Deliverables for Part 1

  • Screenshot of IAM user creation
  • Screenshot of attached policies
  • Verification of aws sts get-caller-identity output

Part 2: Database Setup with Amazon RDS (20%)

Task 2.1: Create RDS PostgreSQL Instance

Create an RDS instance with the following specifications:

SpecificationValue
EnginePostgreSQL 15.x
Instance Classdb.t3.micro (Free Tier)
Storage20 GB gp2
DB Instance IDfastapi-db
Master Usernamepostgres
Database Namefastapi_prod
VPCDefault VPC
Public AccessibilityYes (for development only)
Security GroupAllow PostgreSQL (5432)

Task 2.2: Update FastAPI Database Configuration

Modify your FastAPI application's db.py to use environment variables:

import os
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker

# Use environment variables for database connection
DATABASE_URL = os.getenv(
    "DATABASE_URL",
    "postgresql+asyncpg://postgres:password@localhost:5432/fastapi_prod"
)

engine = create_async_engine(DATABASE_URL, echo=True)

AsyncSessionLocal = sessionmaker(
    engine, expire_on_commit=False, class_=AsyncSession
)

async def get_db_connection():
    db = AsyncSessionLocal()
    try:
        yield db
    finally:
        await db.close()

Task 2.3: Test Database Connection

Verify connectivity to RDS from your local machine:

# Using psql
psql -h <rds-endpoint> -U postgres -d fastapi_prod

# Or using Python
python -c "import asyncio; from db import engine; asyncio.run(engine.connect())"

Deliverables for Part 2

  • Screenshot of RDS instance details
  • Updated db.py with environment variable support
  • Screenshot of successful database connection

Part 3: File Storage with Amazon S3 (15%)

Task 3.1: Create S3 Bucket

Create an S3 bucket for storing application files:

SpecificationValue
Bucket Namefastapi-app-files-<your-id>
Regionap-southeast-1
Block Public AccessEnabled
VersioningEnabled (optional)

Task 3.2: Implement S3 File Upload in FastAPI

Add a new endpoint for file uploads using boto3:

# s3_service.py
import boto3
from botocore.exceptions import ClientError
import os

class S3Service:
    def __init__(self):
        self.s3_client = boto3.client(
            "s3",
            region_name=os.getenv("AWS_REGION", "ap-southeast-1"),
            aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"),
            aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"),
        )
        self.bucket_name = os.getenv("S3_BUCKET_NAME")

    async def upload_file(self, file_path: str, object_name: str) -> bool:
        try:
            self.s3_client.upload_file(file_path, self.bucket_name, object_name)
            return True
        except ClientError as e:
            print(f"Upload failed: {e}")
            return False

    async def download_file(self, object_name: str, file_path: str) -> bool:
        try:
            self.s3_client.download_file(self.bucket_name, object_name, file_path)
            return True
        except ClientError as e:
            print(f"Download failed: {e}")
            return False

    def get_presigned_url(self, object_name: str, expiration: int = 3600) -> str:
        try:
            url = self.s3_client.generate_presigned_url(
                'get_object',
                Params={'Bucket': self.bucket_name, 'Key': object_name},
                ExpiresIn=expiration
            )
            return url
        except ClientError:
            return None

Task 3.3: Create File Upload Endpoint

# In main.py
from fastapi import UploadFile, File
from s3_service import S3Service
import tempfile

s3_service = S3Service()

@app.post("/files/upload")
async def upload_file(file: UploadFile = File(...)):
    # Save temporarily
    with tempfile.NamedTemporaryFile(delete=False) as tmp:
        content = await file.read()
        tmp.write(content)
        tmp_path = tmp.name

    # Upload to S3
    object_name = f"uploads/{file.filename}"
    success = await s3_service.upload_file(tmp_path, object_name)

    if success:
        url = s3_service.get_presigned_url(object_name)
        return {"message": "File uploaded", "url": url}
    return {"error": "Upload failed"}

Deliverables for Part 3

  • Screenshot of S3 bucket configuration
  • Implementation of s3_service.py
  • File upload endpoint in FastAPI
  • Screenshot of successful file upload to S3

Part 4: Containerization with Amazon ECR (20%)

Task 4.1: Create ECR Repository

Create an ECR repository for your FastAPI application:

aws ecr create-repository \
    --repository-name fastapi-app \
    --region ap-southeast-1

Task 4.2: Create Dockerfile

Create a production-ready Dockerfile:

# Dockerfile
FROM python:3.11-slim

WORKDIR /app

# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application code
COPY . .

# Expose port
EXPOSE 8000

# Run with uvicorn
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Task 4.3: Create docker-compose.yml

# docker-compose.yml
version: '3.8'

services:
  fastapi:
    build: .
    ports:
      - '8000:8000'
    environment:
      - DATABASE_URL=${DATABASE_URL}
      - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
      - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
      - AWS_REGION=${AWS_REGION}
      - S3_BUCKET_NAME=${S3_BUCKET_NAME}
    restart: unless-stopped

Task 4.4: Build and Push to ECR

# Authenticate Docker with ECR
aws ecr get-login-password --region ap-southeast-1 | \
    docker login --username AWS --password-stdin <account-id>.dkr.ecr.ap-southeast-1.amazonaws.com

# Build image
docker build -t fastapi-app .

# Tag image
docker tag fastapi-app:latest <account-id>.dkr.ecr.ap-southeast-1.amazonaws.com/fastapi-app:latest

# Push image
docker push <account-id>.dkr.ecr.ap-southeast-1.amazonaws.com/fastapi-app:latest

Deliverables for Part 4

  • Screenshot of ECR repository
  • Dockerfile
  • docker-compose.yml
  • Screenshot of pushed image in ECR

Part 5: Deploy to Amazon EC2 (20%)

Task 5.1: Launch EC2 Instance

Launch an EC2 instance with the following specifications:

SpecificationValue
AMIAmazon Linux 2023 or Ubuntu 22.04
Instance Typet2.micro (Free Tier)
Key PairCreate or use existing
Security GroupAllow SSH (22), HTTP (80), Custom TCP (8000)
Storage8 GB gp3

Task 5.2: Configure EC2 Instance

SSH into your EC2 instance and install required software:

# Connect to EC2
ssh -i your-key.pem ec2-user@<ec2-public-ip>

# Install Docker (Amazon Linux 2023)
sudo yum update -y
sudo yum install -y docker
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker ec2-user

# Install AWS CLI (if not pre-installed)
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

# Configure AWS credentials
aws configure

Task 5.3: Deploy Application on EC2

# Authenticate with ECR
aws ecr get-login-password --region ap-southeast-1 | \
    docker login --username AWS --password-stdin <account-id>.dkr.ecr.ap-southeast-1.amazonaws.com

# Pull image
docker pull <account-id>.dkr.ecr.ap-southeast-1.amazonaws.com/fastapi-app:latest

# Run container
docker run -d \
    --name fastapi-app \
    -p 8000:8000 \
    -e DATABASE_URL="postgresql+asyncpg://postgres:password@<rds-endpoint>:5432/fastapi_prod" \
    -e AWS_ACCESS_KEY_ID="<your-access-key>" \
    -e AWS_SECRET_ACCESS_KEY="<your-secret-key>" \
    -e AWS_REGION="ap-southeast-1" \
    -e S3_BUCKET_NAME="fastapi-app-files-<your-id>" \
    <account-id>.dkr.ecr.ap-southeast-1.amazonaws.com/fastapi-app:latest

Task 5.4: Verify Deployment

Test your deployed application:

# Test health endpoint
curl http://<ec2-public-ip>:8000/

# Test API endpoints
curl http://<ec2-public-ip>:8000/users/
curl -X POST http://<ec2-public-ip>:8000/users/ \
    -H "Content-Type: application/json" \
    -d '{"name": "Test User", "email": "test@example.com"}'

Deliverables for Part 5

  • Screenshot of running EC2 instance
  • Screenshot of security group configuration
  • Screenshot of running Docker container (docker ps)
  • Screenshot of successful API response from EC2

Part 6: CI/CD Pipeline with GitHub Actions (10%)

Task 6.1: Create GitHub Actions Workflow

Create .github/workflows/deploy.yml:

name: Deploy to AWS

on:
  push:
    branches:
      - main

env:
  AWS_REGION: ap-southeast-1
  ECR_REPOSITORY: fastapi-app

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Login to Amazon ECR
        run: |
          aws ecr get-login-password --region ${{ env.AWS_REGION }} | \
            docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ env.AWS_REGION }}.amazonaws.com

      - name: Build and push Docker image
        run: |
          docker build -t ${{ env.ECR_REPOSITORY }} .
          docker tag ${{ env.ECR_REPOSITORY }}:latest ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ env.AWS_REGION }}.amazonaws.com/${{ env.ECR_REPOSITORY }}:latest
          docker tag ${{ env.ECR_REPOSITORY }}:latest ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ env.AWS_REGION }}.amazonaws.com/${{ env.ECR_REPOSITORY }}:${{ github.sha }}
          docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ env.AWS_REGION }}.amazonaws.com/${{ env.ECR_REPOSITORY }}:latest
          docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ env.AWS_REGION }}.amazonaws.com/${{ env.ECR_REPOSITORY }}:${{ github.sha }}

      - name: Deploy to EC2
        uses: appleboy/ssh-action@v1.0.0
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ec2-user
          key: ${{ secrets.EC2_SSH_KEY }}
          script: |
            aws ecr get-login-password --region ${{ env.AWS_REGION }} | \
              docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ env.AWS_REGION }}.amazonaws.com

            docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ env.AWS_REGION }}.amazonaws.com/${{ env.ECR_REPOSITORY }}:latest

            docker stop fastapi-app || true
            docker rm fastapi-app || true

            docker run -d \
              --name fastapi-app \
              -p 8000:8000 \
              -e DATABASE_URL="${{ secrets.DATABASE_URL }}" \
              -e AWS_ACCESS_KEY_ID="${{ secrets.AWS_ACCESS_KEY_ID }}" \
              -e AWS_SECRET_ACCESS_KEY="${{ secrets.AWS_SECRET_ACCESS_KEY }}" \
              -e AWS_REGION="${{ env.AWS_REGION }}" \
              -e S3_BUCKET_NAME="${{ secrets.S3_BUCKET_NAME }}" \
              ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ env.AWS_REGION }}.amazonaws.com/${{ env.ECR_REPOSITORY }}:latest

            docker image prune -f

Task 6.2: Configure GitHub Secrets

Add the following secrets to your GitHub repository:

Secret NameDescription
AWS_ACCESS_KEY_IDIAM user access key
AWS_SECRET_ACCESS_KEYIAM user secret key
AWS_ACCOUNT_IDYour AWS account ID
EC2_HOSTEC2 instance public IP
EC2_SSH_KEYPrivate key for EC2 SSH access
DATABASE_URLFull RDS connection string
S3_BUCKET_NAMES3 bucket name

Deliverables for Part 6

  • GitHub Actions workflow file
  • Screenshot of GitHub secrets configuration
  • Screenshot of successful workflow run
  • Screenshot of automated deployment to EC2

Submission Requirements

Required Deliverables

DeliverableDescription
Source CodeComplete FastAPI application with AWS integration
DockerfileProduction-ready Dockerfile
docker-compose.ymlDocker Compose configuration
GitHub Actions WorkflowCI/CD pipeline configuration
README.mdSetup and deployment instructions
ScreenshotsAll required screenshots from each part

README.md Requirements

Your README.md must include:

  1. Project Overview - Brief description of the application
  2. Architecture Diagram - Visual representation of AWS services used
  3. Prerequisites - Required tools and accounts
  4. Local Development Setup - How to run locally
  5. AWS Resources - List of AWS resources created
  6. Environment Variables - All required environment variables
  7. Deployment Instructions - Step-by-step deployment guide
  8. API Documentation - Available endpoints and usage

Submission Checklist

  • All source code committed to GitHub repository
  • Application successfully deployed to EC2
  • All API endpoints working correctly
  • CI/CD pipeline triggered and completed successfully
  • README.md complete with all sections
  • All screenshots included

Grading Rubric

PartWeightCriteria
Part 1: IAM Configuration15%Correct IAM setup, appropriate permissions
Part 2: Database (RDS)20%RDS created, connected, environment variables used
Part 3: Storage (S3)15%S3 bucket, file upload/download working
Part 4: Containerization20%Dockerfile, ECR push, image versioning
Part 5: Deployment (EC2)20%EC2 running, application accessible, security groups
Part 6: CI/CD Pipeline10%GitHub Actions working, automated deployment

Grading Scale

GradeScore RangeDescription
A90-100%All requirements met, excellent implementation
B80-89%Most requirements met, minor issues
C70-79%Core requirements met, some features missing
D60-69%Partial implementation, significant issues
FBelow 60%Major requirements not met

Bonus Challenges (Optional)

Bonus 1: HTTPS with Load Balancer (+5%)

  • Set up Application Load Balancer with SSL certificate
  • Configure Route 53 for custom domain

Bonus 2: Environment-based Configuration (+5%)

  • Implement separate staging and production environments
  • Use AWS Systems Manager Parameter Store for secrets

Bonus 3: Monitoring and Logging (+5%)

  • Set up CloudWatch for application logs
  • Create CloudWatch alarms for critical metrics

Bonus 4: Database Migrations in CI/CD (+5%)

  • Integrate Alembic migrations into GitHub Actions workflow
  • Implement rollback strategy

Resources


Notes

Security Best Practices

  • Never commit AWS credentials to version control
  • Use IAM roles with least-privilege permissions
  • Keep RDS in private subnet for production
  • Enable VPC flow logs for network monitoring
  • Regularly rotate access keys

Cost Management

  • Use Free Tier eligible resources when possible
  • Set up billing alerts to monitor costs
  • Stop EC2 instances when not in use
  • Delete unused resources after the exam

On this page