AWS DevOps Pipeline
Simple guide to deploy FastAPI + Vanilla JS + AWS Infrastructure with CI/CD
Prerequisites
Estimated Cost: ~$8-12/month for running infrastructure
We need to install AWS CLI, Terraform, and Git. Choose your operating system:
Windows (PowerShell as Administrator)
Install Chocolatey first (if not already installed):
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
Then install the required tools:
choco install awscli terraform git -y
Mac (Terminal)
Install Homebrew first (if not already installed):
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Then install the required tools:
brew install awscli terraform git
Linux (Ubuntu/Debian)
Install AWS CLI v2:
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
Install Terraform:
wget https://releases.hashicorp.com/terraform/1.7.0/terraform_1.7.0_linux_amd64.zip
unzip terraform_1.7.0_linux_amd64.zip
sudo mv terraform /usr/local/bin/
Install Git:
sudo apt update
sudo apt install git -y
Verify Installation
Run these commands to verify everything is installed correctly:
aws --version
terraform --version
git --version
What You'll See:

Installing AWS CLI

Installation Confirmation

Version Verification
Need an AWS account? Create one at aws.amazon.com
Step 1: Create IAM User
- Log into the AWS Console
- Navigate to IAM service
- Click Users in the left sidebar
- Click Create User
- Enter username:
devops-demo-user
- Check Provide user access to the AWS Management Console (optional)
- Click Next
Step 2: Attach Permissions
- Select Attach policies directly
- Search for and select
AdministratorAccess
- Click Next
- Review and click Create User
Note: AdministratorAccess is used for simplicity in this demo. In production, use more restrictive policies.
Step 3: Create Access Keys
- Click on your newly created user
- Go to the Security credentials tab
- Scroll down to Access keys
- Click Create access key
- Select Command Line Interface (CLI)
- Check the confirmation checkbox
- Click Next
- Add description (optional):
DevOps Demo CLI Access
- Click Create access key
- ⚠️ Important: Copy both the Access Key ID and Secret Access Key immediately!

Configure the AWS CLI with your access keys from the previous step.
Configure AWS CLI
Run the following command and enter your credentials when prompted:
aws configure
Enter These Values
Verify Configuration
Test your configuration by checking your identity:
aws sts get-caller-identity
This should return your AWS account ID and user ARN.
What You'll See:

AWS CLI Configuration

Identity Verification
✅ Success! If you see your account details, AWS CLI is properly configured and ready to use.
We'll create 3 separate repositories to organize our DevOps pipeline components.
Why 3 Separate Repositories?
- Infrastructure: Manages AWS resources with Terraform
- Backend: FastAPI application with its own CI/CD pipeline
- Frontend: Static website with separate deployment to S3
Create Each Repository
Follow these steps for each of the 3 repositories:
- Go to github.com/new
- Enter the repository name (see list below)
- Set visibility to Private (recommended for AWS keys)
- Check Add a README file
- Click Create repository
Infrastructure Repository
1stdevops-demo-infrastructure
Contains Terraform files for AWS resources
Backend Repository
2nddevops-demo-backend
FastAPI application, Dockerfile, and CI/CD
Frontend Repository
3rddevops-demo-frontend
HTML/JS files and S3 deployment workflow
What You'll See:

Infrastructure Repository

Backend Repository

Frontend Repository
Clone Repositories Locally
Replace [username]
with your GitHub username:
# Create a folder for all projects
mkdir devops-demo
cd devops-demo
# Clone all repositories
git clone https://github.com/[username]/devops-demo-infrastructure.git
git clone https://github.com/[username]/devops-demo-backend.git
git clone https://github.com/[username]/devops-demo-frontend.git
# Verify all folders exist
ls -la
Verify Success
You should now have 3 folders on your local machine:
- 📁
devops-demo-infrastructure/
- 📁
devops-demo-backend/
- 📁
devops-demo-frontend/
This Terraform configuration creates a complete AWS infrastructure for our DevOps demo application.
AWS Services Created
VPC & Networking
Purpose: Network foundation
Uses default VPC with security groups for app (port 8000) and database (port 5432)
RDS PostgreSQL
Purpose: Database
db.t3.micro instance with 20GB storage, accessible only from app security group
ECS Cluster
Purpose: Container orchestration
Fargate-based cluster running containerized FastAPI application
ECS Task Definition
Purpose: Container configuration
256 CPU, 512MB memory, with database URL from SSM Parameter Store
ECS Service
Purpose: Application deployment
Maintains 1 running task with public IP assignment
ECR Repository
Purpose: Container registry
Stores Docker images for the backend application
S3 Bucket
Purpose: Static website hosting
Hosts frontend files with public read access and website configuration
IAM Roles
Purpose: Security & permissions
ECS execution and task roles with minimal required permissions
SSM Parameter
Purpose: Secrets management
Securely stores database connection string
CloudWatch
Purpose: Logging
Centralized logging for ECS containers
Service | Purpose | Configuration |
---|---|---|
VPC & Networking
|
Network foundation | Uses default VPC with security groups for app (port 8000) and database (port 5432) |
RDS PostgreSQL
|
Database | db.t3.micro instance with 20GB storage, accessible only from app security group |
ECS Cluster
|
Container orchestration | Fargate-based cluster running containerized FastAPI application |
ECS Task Definition
|
Container configuration | 256 CPU, 512MB memory, with database URL from SSM Parameter Store |
ECS Service
|
Application deployment | Maintains 1 running task with public IP assignment |
ECR Repository
|
Container registry | Stores Docker images for the backend application |
S3 Bucket
|
Static website hosting | Hosts frontend files with public read access and website configuration |
IAM Roles
|
Security & permissions | ECS execution and task roles with minimal required permissions |
SSM Parameter
|
Secrets management | Securely stores database connection string |
CloudWatch
|
Logging | Centralized logging for ECS containers |
Total estimated cost: ~$8-12/month
Create devops-demo-infrastructure/main.tf
provider "aws" {
region = "us-east-1"
}
data "aws_vpc" "default" {
default = true
}
data "aws_subnets" "default" {
filter {
name = "vpc-id"
values = [data.aws_vpc.default.id]
}
}
resource "aws_security_group" "app" {
vpc_id = data.aws_vpc.default.id
ingress {
from_port = 8000
to_port = 8000
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# ... (truncated for brevity, full code available in guide)
output "ecr_url" { value = aws_ecr_repository.app.repository_url }
output "bucket_name" { value = aws_s3_bucket.frontend.bucket }
output "website_url" { value = "http://${aws_s3_bucket_website_configuration.frontend.website_endpoint}" }
cd devops-demo-infrastructure
terraform init
terraform apply
# Save the outputs


1. Create FastAPI app
File: devops-demo-backend/app/main.py
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
import os
import json
import psycopg2
from psycopg2.extras import RealDictCursor
app = FastAPI()
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"])
# ... (truncated for brevity)
2. Add dependencies
File: requirements.txt
fastapi==0.104.1
uvicorn==0.24.0
psycopg2-binary==2.9.9
3. Setup directories and secrets
cd devops-demo-backend
mkdir app .github .github/workflows
Add GitHub Secrets:
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
ECR_REGISTRY
(from terraform output)
GitHub Actions CI/CD Pipeline
The backend uses a fully automated CI/CD pipeline that builds, tests, and deploys your FastAPI application to AWS ECS.
What the GitHub Action Does:
Code Checkout & Setup
Checks out your code and configures Python environment
AWS Authentication
Configures AWS credentials using GitHub secrets
Docker Build & Push
Builds Docker image and pushes to AWS ECR registry
ECS Deployment
Updates ECS service to run the new container image
Automatic Triggers
- • Push to main branch: Automatically builds and deploys new version
- • Pull request: Runs build tests to verify code quality
- • Manual trigger: Can be run manually from GitHub Actions tab
4. Deploy
git add .
git commit -m "Add backend"
git push origin main


1. Get backend IP
# Get task ARN
aws ecs list-tasks --cluster demo-cluster --service demo-app --desired-status RUNNING --query 'taskArns[0]' --output text --region us-east-1
# Get network interface ID (use task ARN from above)
aws ecs describe-tasks --cluster demo-cluster --tasks [TASK_ARN] --region us-east-1 --query 'tasks[0].attachments[0].details[?name==`networkInterfaceId`].value[0]' --output text
# Get public IP (use network interface ID from above)
aws ec2 describe-network-interfaces --network-interface-ids [NETWORK_INTERFACE_ID] --region us-east-1 --query 'NetworkInterfaces[0].Association.PublicIp' --output text
2. Setup and deploy
Add GitHub Secrets:
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
S3_BUCKET
(from terraform output)API_URL
: http://[BACKEND_IP]:8000
cd devops-demo-frontend
mkdir .github .github/workflows
git add .
git commit -m "Add frontend"
git push origin main
GitHub Actions Static Website Deployment
The frontend uses a streamlined deployment pipeline that builds and uploads your static website directly to AWS S3.
What the GitHub Action Does:
Code Checkout
Retrieves your HTML, CSS, and JavaScript files from the repository
Environment Variable Substitution
Replaces API_URL placeholder with actual backend endpoint URL
AWS S3 Upload
Syncs all static files to your S3 bucket with correct permissions
Website Availability
Files are immediately available via S3 website endpoint
Automatic Triggers
- • Push to main: Instantly deploys changes
- • Pull request: Validates file structure
- • Manual run: Deploy on-demand
Benefits
- • Fast deployment: ~30 seconds
- • Cost effective: No server required
- • Highly available: AWS S3 reliability


Common Terraform Errors
Error: Invalid provider configuration
Usually means AWS credentials are not configured correctly.
Solution:
- Verify
aws configure
was run - Check
aws sts get-caller-identity
works - Ensure your AWS keys have proper permissions
Error: Resource already exists
Some AWS resources with the same name already exist.
Solution:
- Change the resource names in your Terraform file
- Or delete existing resources manually in AWS console
- Run
terraform destroy
if you have leftover resources
GitHub Actions Issues
AWS credentials not configured
Actions fail with authentication errors.
Solution:
- Go to your repo → Settings → Secrets and variables → Actions
- Add
AWS_ACCESS_KEY_ID
- Add
AWS_SECRET_ACCESS_KEY
- Add other required secrets (ECR_REGISTRY, S3_BUCKET, API_URL)
ECS IAM Permissions Debug
ECS tasks failing - runningCount stays at 0
Tasks keep stopping with no running containers.
Debug Commands:
# Check service status
aws ecs describe-services --cluster demo-cluster --services demo-app --region us-east-1
# Check stopped tasks for error details
aws ecs list-tasks --cluster demo-cluster --desired-status STOPPED --region us-east-1
aws ecs describe-tasks --cluster demo-cluster --tasks [TASK_ARN] --region us-east-1
Common Error Found:
"stoppedReason": "ResourceInitializationError: unable to pull secrets...
AccessDeniedException: User is not authorized to perform: ssm:GetParameters
because no identity-based policy allows the ssm:GetParameters action"
Root Cause:
IAM role had ssm:GetParameter
(singular) but ECS needs ssm:GetParameters
(plural).
Fix in Terraform:
resource "aws_iam_role_policy" "execution_ssm" {
name = "ssm-access"
role = aws_iam_role.execution.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = [
"ssm:GetParameter",
"ssm:GetParameters" # Added this line
]
Resource = [aws_ssm_parameter.db_url.arn]
}]
})
}
Key Learning: ECS requires both ssm:GetParameter
and ssm:GetParameters
permissions for secrets retrieval.
Connection Issues
Frontend can't connect to backend
CORS errors or connection refused.
Solution:
- Verify backend is running: check ECS service status
- Ensure security group allows port 8000 from anywhere
- Check that API_URL in frontend secrets is correct
- Verify backend has CORS enabled (should be in the code)
Error Examples:

Terraform Error Example

Error Resolution
You're Done!
Backend API
http://[BACKEND_IP]:8000
Frontend Website
From terraform website_url output
Cleanup: Run terraform destroy
when done
Terraform Destroy Example

Example output from terraform destroy command