DEV Community

Isaac Obuor
Isaac Obuor

Posted on • Edited on

Secure E-commerce Platform on AWS-Infrastructure

This comprehensive project will give you hands-on experience with all the AWS services mentioned in the job posting. Here's why this project is perfect for demonstrating your qualifications:

Key Benefits:

. Real-world scenario: E-commerce platform mirrors actual business requirements
. Production-ready: Includes security, monitoring, and compliance features
. Scalable architecture: Demonstrates understanding of enterprise-level design
. Full CI/CD pipeline: Shows DevOps integration skills
. Security-first approach: Meets compliance requirements that employers value

What Makes This Stand Out:

  1. Multi-tier architecture showing deep VPC understanding
  2. Container orchestration with ECS Fargate (modern approach)
  3. Blue/green deployments demonstrating zero-downtime strategies
  4. Comprehensive security including WAF, encryption, and monitoring
  5. Cost optimization through proper resource sizing and lifecycle policies

AWS Portfolio Project - Step-by-Step Implementation Guide
Pre-requisites Setup (Day 0)

  1. AWS Account Setup bash# Create AWS Account (if you don't have one) # Sign up at: https://aws.amazon.com/ # Enable billing alerts in CloudWatch # Set up MFA for root account

Install AWS CLI

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

Configure AWS CLI

aws configure

Enter your Access Key ID, Secret Access Key, Region (us-east-1), and output format (json)

  1. Development Environment bash# Install required tools # Node.js (for sample application) curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - sudo apt-get install -y nodejs

Docker (for containerization)

sudo apt-get update
sudo apt-get install docker.io
sudo usermod -aG docker $USER

Git (for version control)

sudo apt-get install git

VS Code or your preferred editor

wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > packages.microsoft.gpg
sudo install -o root -g root -m 644 packages.microsoft.gpg /etc/apt/trusted.gpg.d/
sudo sh -c 'echo "deb [arch=amd64,arm64,armhf signed-by=/etc/apt/trusted.gpg.d/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" > /etc/apt/sources.list.d/vscode.list'
sudo apt update
sudo apt install code

  1. Project Structure Setup bash# Create project directory mkdir aws-ecommerce-infrastructure cd aws-ecommerce-infrastructure

Create directory structure

mkdir -p {
cloudformation/{network,security,compute,storage,cicd},
application/{frontend,backend},
scripts,
documentation,
monitoring
}

Initialize Git repository

git init
echo "# AWS E-commerce Infrastructure Project" > README.md
git add README.md
git commit -m "Initial commit"

WEEK 1: Network Foundation & Security
Day 1: VPC and Networking Setup
Step 1: Create Base VPC CloudFormation Template
bash# Create the main network template
touch cloudformation/network/vpc-base.yaml
File: cloudformation/network/vpc-base.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: 'E-commerce VPC with public and private subnets'

Parameters:
  ProjectName:
    Type: String
    Default: ecommerce
    Description: Name of the project

  Environment:
    Type: String
    Default: production
    AllowedValues: [development, staging, production]

Resources:
  # VPC
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-vpc'
        - Key: Environment
          Value: !Ref Environment

  # Internet Gateway
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-igw'

  InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

  # Public Subnets
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [0, !GetAZs '']
      CidrBlock: 10.0.1.0/24
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-public-subnet-1'

  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [1, !GetAZs '']
      CidrBlock: 10.0.2.0/24
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-public-subnet-2'

  # Private Subnets
  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [0, !GetAZs '']
      CidrBlock: 10.0.10.0/24
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-private-subnet-1'

  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [1, !GetAZs '']
      CidrBlock: 10.0.20.0/24
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-private-subnet-2'

  # Database Subnets
  DBSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [0, !GetAZs '']
      CidrBlock: 10.0.30.0/24
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-db-subnet-1'

  DBSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [1, !GetAZs '']
      CidrBlock: 10.0.40.0/24
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-db-subnet-2'

  # NAT Gateways
  NatGateway1EIP:
    Type: AWS::EC2::EIP
    DependsOn: InternetGatewayAttachment
    Properties:
      Domain: vpc

  NatGateway2EIP:
    Type: AWS::EC2::EIP
    DependsOn: InternetGatewayAttachment
    Properties:
      Domain: vpc

  NatGateway1:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt NatGateway1EIP.AllocationId
      SubnetId: !Ref PublicSubnet1

  NatGateway2:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt NatGateway2EIP.AllocationId
      SubnetId: !Ref PublicSubnet2

  # Route Tables
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-public-routes'

  DefaultPublicRoute:
    Type: AWS::EC2::Route
    DependsOn: InternetGatewayAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet1

  PublicSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet2

  PrivateRouteTable1:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-private-routes-1'

  DefaultPrivateRoute1:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable1
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGateway1

  PrivateSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable1
      SubnetId: !Ref PrivateSubnet1

  PrivateRouteTable2:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-private-routes-2'

  DefaultPrivateRoute2:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable2
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGateway2

  PrivateSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable2
      SubnetId: !Ref PrivateSubnet2

Outputs:
  VPC:
    Description: A reference to the created VPC
    Value: !Ref VPC
    Export:
      Name: !Sub '${ProjectName}-${Environment}-VPC'

  PublicSubnets:
    Description: A list of the public subnets
    Value: !Join [",", [!Ref PublicSubnet1, !Ref PublicSubnet2]]
    Export:
      Name: !Sub '${ProjectName}-${Environment}-PublicSubnets'

  PrivateSubnets:
    Description: A list of the private subnets
    Value: !Join [",", [!Ref PrivateSubnet1, !Ref PrivateSubnet2]]
    Export:
      Name: !Sub '${ProjectName}-${Environment}-PrivateSubnets'

  DBSubnets:
    Description: A list of the database subnets
    Value: !Join [",", [!Ref DBSubnet1, !Ref DBSubnet2]]
    Export:
      Name: !Sub '${ProjectName}-${Environment}-DBSubnets'
Enter fullscreen mode Exit fullscreen mode

Step 2: Deploy the VPC

`# Deploy the VPC stack
aws cloudformation create-stack \
--stack-name ecommerce-vpc \
--template-body file://cloudformation/network/vpc-base.yaml \
--parameters ParameterKey=ProjectName,ParameterValue=ecommerce \
ParameterKey=Environment,ParameterValue=production

Wait for stack completion

aws cloudformation wait stack-create-complete --stack-name ecommerce-vpc

Verify the stack

aws cloudformation describe-stacks --stack-name ecommerce-vpc`

Security Groups Configuration
Step 1: Create Security Groups Template

bashtouch cloudformation/security/security-groups.yaml

File: cloudformation/security/security-groups.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: 'Security Groups for E-commerce Application'

Parameters:
  ProjectName:
    Type: String
    Default: ecommerce
  Environment:
    Type: String
    Default: production

Resources:
  # ALB Security Group
  ALBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub '${ProjectName}-${Environment}-alb-sg'
      GroupDescription: Security group for Application Load Balancer
      VpcId: 
        Fn::ImportValue: !Sub '${ProjectName}-${Environment}-VPC'
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
          Description: 'HTTP from anywhere'
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
          Description: 'HTTPS from anywhere'
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-alb-sg'

  # ECS Security Group
  ECSSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub '${ProjectName}-${Environment}-ecs-sg'
      GroupDescription: Security group for ECS tasks
      VpcId: 
        Fn::ImportValue: !Sub '${ProjectName}-${Environment}-VPC'
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3000
          ToPort: 3000
          SourceSecurityGroupId: !Ref ALBSecurityGroup
          Description: 'HTTP from ALB'
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-ecs-sg'

  # RDS Security Group
  RDSSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub '${ProjectName}-${Environment}-rds-sg'
      GroupDescription: Security group for RDS database
      VpcId: 
        Fn::ImportValue: !Sub '${ProjectName}-${Environment}-VPC'
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 5432
          ToPort: 5432
          SourceSecurityGroupId: !Ref ECSSecurityGroup
          Description: 'PostgreSQL from ECS'
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-rds-sg'

  # Bastion Host Security Group (for debugging)
  BastionSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub '${ProjectName}-${Environment}-bastion-sg'
      GroupDescription: Security group for bastion host
      VpcId: 
        Fn::ImportValue: !Sub '${ProjectName}-${Environment}-VPC'
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0  # Restrict this to your IP in production
          Description: 'SSH access'
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-bastion-sg'

Outputs:
  ALBSecurityGroup:
    Description: Security group for ALB
    Value: !Ref ALBSecurityGroup
    Export:
      Name: !Sub '${ProjectName}-${Environment}-ALB-SG'

  ECSSecurityGroup:
    Description: Security group for ECS
    Value: !Ref ECSSecurityGroup
    Export:
      Name: !Sub '${ProjectName}-${Environment}-ECS-SG'

  RDSSecurityGroup:
    Description: Security group for RDS
    Value: !Ref RDSSecurityGroup
    Export:
      Name: !Sub '${ProjectName}-${Environment}-RDS-SG'
Enter fullscreen mode Exit fullscreen mode

Step 2: Deploy Security Groups

`# Deploy security groups
aws cloudformation create-stack \
--stack-name ecommerce-security-groups \
--template-body file://cloudformation/security/security-groups.yaml \
--parameters ParameterKey=ProjectName,ParameterValue=ecommerce \
ParameterKey=Environment,ParameterValue=production

Wait for completion

aws cloudformation wait stack-create-complete --stack-name ecommerce-security-groups`

IAM Roles and Policies
Step 1: Create IAM Template

touch cloudformation/security/iam-roles.yaml

File: cloudformation/security/iam-roles.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: 'IAM Roles and Policies for E-commerce Application'

Resources:
  # ECS Task Execution Role
  ECSTaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: ecommerce-ecs-task-execution-role
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
      Policies:
        - PolicyName: SecretsManagerAccess
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - secretsmanager:GetSecretValue
                Resource: !Ref DBPasswordSecret

  # ECS Task Role
  ECSTaskRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: ecommerce-ecs-task-role
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: S3Access
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - s3:GetObject
                  - s3:PutObject
                  - s3:DeleteObject
                Resource:
                  - arn:aws:s3:::ecommerce-product-images/*
                  - arn:aws:s3:::ecommerce-user-uploads/*
        - PolicyName: CloudWatchLogs
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: '*'

  # CodePipeline Service Role
  CodePipelineServiceRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: ecommerce-codepipeline-role
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: codepipeline.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: CodePipelinePolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - s3:GetBucketVersioning
                  - s3:GetObject
                  - s3:GetObjectVersion
                  - s3:PutObject
                Resource: '*'
              - Effect: Allow
                Action:
                  - codebuild:BatchGetBuilds
                  - codebuild:StartBuild
                Resource: '*'
              - Effect: Allow
                Action:
                  - codedeploy:CreateDeployment
                  - codedeploy:GetApplication
                  - codedeploy:GetApplicationRevision
                  - codedeploy:GetDeployment
                  - codedeploy:GetDeploymentConfig
                  - codedeploy:RegisterApplicationRevision
                Resource: '*'

  # Database Password Secret
  DBPasswordSecret:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name: ecommerce-db-password
      Description: Password for RDS PostgreSQL database
      GenerateSecretString:
        SecretStringTemplate: '{"username": "postgres"}'
        GenerateStringKey: 'password'
        PasswordLength: 32
        ExcludeCharacters: '"@/\'

Outputs:
  ECSTaskExecutionRoleArn:
    Description: ARN of the ECS Task Execution Role
    Value: !GetAtt ECSTaskExecutionRole.Arn
    Export:
      Name: ecommerce-production-ECS-TaskExecutionRole-Arn

  ECSTaskRoleArn:
    Description: ARN of the ECS Task Role
    Value: !GetAtt ECSTaskRole.Arn
    Export:
      Name: ecommerce-production-ECS-TaskRole-Arn

  DBPasswordSecretArn:
    Description: ARN of the database password secret
    Value: !Ref DBPasswordSecret
    Export:
      Name: ecommerce-production-DB-PasswordSecret-Arn
Enter fullscreen mode Exit fullscreen mode

Step 2: Deploy IAM Stack

`# Deploy IAM roles
aws cloudformation create-stack \
--stack-name ecommerce-iam-roles \
--template-body file://cloudformation/security/iam-roles.yaml \
--capabilities CAPABILITY_NAMED_IAM

Wait for completion

aws cloudformation wait stack-create-complete --stack-name ecommerce-iam-roles`

Create Sample Application
Step 1: Create Backend Application

`mkdir -p application/backend
cd application/backend

Initialize Node.js project

npm init -y

Install dependencies

npm install express cors helmet morgan dotenv pg
npm install -D nodemon

Create package.json scripts`

File: application/backend/package.json

{
  "name": "ecommerce-api",
  "version": "1.0.0",
  "description": "E-commerce API backend",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "express": "^4.18.2",
    "cors": "^2.8.5",
    "helmet": "^7.0.0",
    "morgan": "^1.10.0",
    "dotenv": "^16.3.1",
    "pg": "^8.11.3"
  },
  "devDependencies": {
    "nodemon": "^3.0.1"
  }
}
Enter fullscreen mode Exit fullscreen mode

File: application/backend/server.js

const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
require('dotenv').config();

const app = express();
const PORT = process.env.PORT || 3000;

// Middleware
app.use(helmet());
app.use(cors());
app.use(morgan('combined'));
app.use(express.json());

// Health check endpoint
app.get('/health', (req, res) => {
  res.status(200).json({
    status: 'healthy',
    timestamp: new Date().toISOString(),
    uptime: process.uptime()
  });
});

// API routes
app.get('/api/products', (req, res) => {
  res.json({
    products: [
      { id: 1, name: 'Laptop', price: 999.99, category: 'Electronics' },
      { id: 2, name: 'Smartphone', price: 699.99, category: 'Electronics' },
      { id: 3, name: 'Headphones', price: 199.99, category: 'Audio' }
    ]
  });
});

app.get('/api/products/:id', (req, res) => {
  const productId = parseInt(req.params.id);
  const product = {
    id: productId,
    name: 'Sample Product',
    price: 99.99,
    description: 'This is a sample product',
    category: 'Sample Category'
  };
  res.json(product);
});

// Error handling middleware
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Something went wrong!' });
});

// 404 handler
app.use('*', (req, res) => {
  res.status(404).json({ error: 'Route not found' });
});

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
  console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
});
Enter fullscreen mode Exit fullscreen mode

Step 2: Create Dockerfile
File: application/backend/Dockerfile

FROM node:18-alpine

# Create app directory
WORKDIR /usr/src/app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci --only=production

# Copy source code
COPY . .

# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001

# Change ownership to nodejs user
RUN chown -R nodejs:nodejs /usr/src/app
USER nodejs

# Expose port
EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node healthcheck.js

CMD ["npm", "start"]
Enter fullscreen mode Exit fullscreen mode

File: application/backend/healthcheck.js

const http = require('http');

const options = {
  hostname: 'localhost',
  port: 3000,
  path: '/health',
  method: 'GET'
};

const req = http.request(options, (res) => {
  if (res.statusCode === 200) {
    process.exit(0);
  } else {
    process.exit(1);
  }
});

req.on('error', () => {
  process.exit(1);
});

req.end();
Enter fullscreen mode Exit fullscreen mode

Step 3: Test Locally


# Test the application locally
cd application/backend
npm install
npm run dev

# In another terminal, test the API
curl http://localhost:3000/health
curl http://localhost:3000/api/products

# Build and test Docker image
docker build -t ecommerce-api .
docker run -p 3000:3000 ecommerce-api
Enter fullscreen mode Exit fullscreen mode

Here is the out come of the build and test docker image.

ECR Setup and Container Push
Step 1: Create ECR Repository

`# Create ECR repository
aws ecr create-repository \
--repository-name ecommerce-api \
--region us-east-1

Get login token. We will edit the command by copy and past our AWS account ID

aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin .dkr.ecr.us-east-1.amazonaws.com`

Step 2: Build and Push Image

`cd application/backend

Build image

docker build -t ecommerce-api .

We have to edit the command by copy and past our AWS account ID

docker tag ecommerce-api:latest .dkr.ecr.us-east-1.amazonaws.com/ecommerce-api:latest

Push image

docker push .dkr.ecr.us-east-1.amazonaws.com/ecommerce-api:latest`

Step 3: Create Build Script
File: scripts/build-and-push.sh

`#!/bin/bash

Build and push script

set -e

Variables

AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
AWS_REGION=us-east-1
IMAGE_NAME=ecommerce-api
IMAGE_TAG=${1:-latest}

Login to ECR

aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com

Build image

echo "Building Docker image..."
docker build -t $IMAGE_NAME:$IMAGE_TAG application/backend/

Tag image

docker tag $IMAGE_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$IMAGE_TAG

Push image

echo "Pushing to ECR..."
docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$IMAGE_TAG

echo "Build and push completed successfully!"`

`# Make script executable
chmod +x scripts/build-and-push.sh

Run the script

./scripts/build-and-push.sh`

What You've Accomplished:
โœ… VPC Infrastructure: Multi-tier network with proper subnet isolation
โœ… Security Groups: Layered security with least privilege access
โœ… IAM Roles: Secure role-based access control
โœ… Sample Application: Containerized Node.js API
โœ… ECR Repository: Container registry with pushed image
Verification Commands:

`# Check all your stacks
aws cloudformation list-stacks --stack-status-filter CREATE_COMPLETE

Verify VPC resources

aws ec2 describe-vpcs --filters "Name=tag:Name,Values=ecommerce-production-vpc"
aws ec2 describe-subnets --filters "Name=vpc-id,Values="

Check ECR image

aws ecr describe-images --repository-name ecommerce-api`

File: documentation/week1-progress.md

`#Check our Progress Report

Infrastructure Completed

  • [x] VPC with 6 subnets across 2 AZs
  • [x] NAT Gateways for high availability
  • [x] Security groups with least privilege access
  • [x] IAM roles for ECS and CI/CD
  • [x] Containerized Node.js application
  • [x] ECR repository with pushed image

Architecture Decisions

  1. Multi-AZ Design: Ensures high availability
  2. Private Subnets: Applications run in private subnets for security
  3. Defense in Depth: Multiple security layers (NACLs, Security Groups, IAM)
  4. Least Privilege: IAM policies grant minimal required permissions ` Application Infrastructure & Database Day 6: RDS Database Setup Step 1: Create RDS Template

touch cloudformation/storage/rds-database.yaml

File: cloudformation/storage/rds-database.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: 'RDS PostgreSQL Database for E-commerce Application'

Parameters:
  ProjectName:
    Type: String
    Default: ecommerce
  Environment:
    Type: String
    Default: production
  DBInstanceClass:
    Type: String
    Default: db.t3.micro
    Description: RDS instance class
    AllowedValues:
      - db.t3.micro
      - db.t3.small
      - db.t3.medium
      - db.t3.large
  DBPassword:
    Type: String
    NoEcho: true
    Description: Master password for RDS instance (8-128 characters)
    MinLength: 8
    MaxLength: 128
    AllowedPattern: ^[a-zA-Z0-9!@#$%^&*()_+=-]*$
    ConstraintDescription: Must contain 8-128 alphanumeric characters

Resources:
  # DB Subnet Group
  DBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupName: !Sub '${ProjectName}-${Environment}-db-subnet-group'
      DBSubnetGroupDescription: Subnet group for RDS database
      SubnetIds: !Split 
        - ','
        - Fn::ImportValue: !Sub '${ProjectName}-${Environment}-DBSubnets'
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-db-subnet-group'

  # RDS Instance
  RDSInstance:
    Type: AWS::RDS::DBInstance
    DeletionPolicy: Snapshot
    Properties:
      DBInstanceIdentifier: !Sub '${ProjectName}-${Environment}-postgres'
      DBInstanceClass: !Ref DBInstanceClass
      Engine: postgres
      # EngineVersion: '15.4'  # Comment out to use default version
      AllocatedStorage: 20
      StorageType: gp2
      StorageEncrypted: true

      DBName: ecommerce
      MasterUsername: postgres
      MasterUserPassword: !Ref DBPassword  # Using parameter instead of Secrets Manager

      VPCSecurityGroups:
        - Fn::ImportValue: !Sub '${ProjectName}-${Environment}-RDS-SG'
      DBSubnetGroupName: !Ref DBSubnetGroup

      BackupRetentionPeriod: 7
      PreferredBackupWindow: "03:00-04:00"
      PreferredMaintenanceWindow: "sun:04:00-sun:05:00"

      MultiAZ: false  # Set to true for production
      PubliclyAccessible: false

      EnablePerformanceInsights: true
      PerformanceInsightsRetentionPeriod: 7

      DeletionProtection: false  # Set to true for production

      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-postgres'
        - Key: Environment
          Value: !Ref Environment

Outputs:
  RDSEndpoint:
    Description: RDS instance endpoint
    Value: !GetAtt RDSInstance.Endpoint.Address
    Export:
      Name: !Sub '${ProjectName}-${Environment}-RDS-Endpoint'

  RDSPort:
    Description: RDS instance port
    Value: !GetAtt RDSInstance.Endpoint.Port
    Export:
      Name: !Sub '${ProjectName}-${Environment}-RDS-Port'

  RDSInstanceId:
    Description: RDS instance identifier
    Value: !Ref RDSInstance
    Export:
      Name: !Sub '${ProjectName}-${Environment}-RDS-InstanceId'
Enter fullscreen mode Exit fullscreen mode

Step 2: Deploy RDS Stack

`# Deploy RDS database
aws cloudformation create-stack \
--stack-name ecommerce-rds \
--template-body file://cloudformation/storage/rds-database.yaml \
--parameters ParameterKey=DBPassword,ParameterValue=YourSecurePassword123!

This will take 10-15 minutes

aws cloudformation wait stack-create-complete --stack-name ecommerce-rds`

ECS Cluster Setup
Step 1: Create ECS Template

touch cloudformation/compute/ecs-cluster.yaml

File: cloudformation/compute/ecs-cluster.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: 'ECS Cluster and Service for E-commerce API'

Parameters:
  ProjectName:
    Type: String
    Default: ecommerce
  Environment:
    Type: String
    Default: production
  ImageURI:
    Type: String
    Description: ECR image URI
  DBEndpoint:
    Type: String
    Default: ''
    Description: RDS endpoint (leave empty to auto-import)

Conditions:
  HasDBEndpoint: !Not [!Equals [!Ref DBEndpoint, '']]

Resources:
  # ECS Cluster
  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: !Sub '${ProjectName}-${Environment}-cluster'
      ClusterSettings:
        - Name: containerInsights
          Value: enabled
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-cluster'

  # CloudWatch Log Group
  LogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub '/aws/ecs/${ProjectName}-${Environment}-api'
      RetentionInDays: 7

  # ECS Task Definition
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: !Sub '${ProjectName}-${Environment}-api'
      NetworkMode: awsvpc
      RequiresCompatibilities:
        - FARGATE
      Cpu: '256'
      Memory: '512'
      ExecutionRoleArn: 
        Fn::ImportValue: !Sub '${ProjectName}-${Environment}-ECS-TaskExecutionRole-Arn'
      TaskRoleArn:
        Fn::ImportValue: !Sub '${ProjectName}-${Environment}-ECS-TaskRole-Arn'
      ContainerDefinitions:
        - Name: !Sub '${ProjectName}-api'
          Image: !Ref ImageURI
          PortMappings:
            - ContainerPort: 3000
              Protocol: tcp
          Environment:
            - Name: NODE_ENV
              Value: production
            - Name: PORT
              Value: '3000'
            - Name: DB_HOST
              Value: !If 
                - HasDBEndpoint
                - !Ref DBEndpoint
                - Fn::ImportValue: !Sub '${ProjectName}-${Environment}-RDS-Endpoint'
            - Name: DB_PORT
              Value: '5432'
            - Name: DB_NAME
              Value: ecommerce
            - Name: DB_USER
              Value: postgres
          Secrets:
            - Name: DB_PASSWORD
              ValueFrom: 
                Fn::ImportValue: !Sub '${ProjectName}-${Environment}-DB-PasswordSecret-Arn'
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Ref LogGroup
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: ecs
          # Simplified health check - using TCP instead of HTTP
          # Remove if your container doesn't support health checks
          Essential: true

  # Application Load Balancer
  ApplicationLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Sub '${ProjectName}-${Environment}-alb'
      Scheme: internet-facing
      Type: application
      SecurityGroups:
        - Fn::ImportValue: !Sub '${ProjectName}-${Environment}-ALB-SG'
      Subnets: !Split
        - ','
        - Fn::ImportValue: !Sub '${ProjectName}-${Environment}-PublicSubnets'
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-alb'

  # Target Group
  TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub '${ProjectName}-${Environment}-tg'
      Port: 3000
      Protocol: HTTP
      VpcId:
        Fn::ImportValue: !Sub '${ProjectName}-${Environment}-VPC'
      TargetType: ip
      HealthCheckIntervalSeconds: 30
      HealthCheckPath: /health
      HealthCheckProtocol: HTTP
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 3
      TargetGroupAttributes:
        - Key: deregistration_delay.timeout_seconds
          Value: '30'

  # ALB Listener - Fixed syntax
  ALBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          ForwardConfig:
            TargetGroups:
              - TargetGroupArn: !Ref TargetGroup
                Weight: 100
      LoadBalancerArn: !Ref ApplicationLoadBalancer
      Port: 80
      Protocol: HTTP

  # ECS Service
  ECSService:
    Type: AWS::ECS::Service
    DependsOn: ALBListener
    Properties:
      ServiceName: !Sub '${ProjectName}-${Environment}-api-service'
      Cluster: !Ref ECSCluster
      TaskDefinition: !Ref TaskDefinition
      LaunchType: FARGATE
      DesiredCount: 2
      NetworkConfiguration:
        AwsvpcConfiguration:
          SecurityGroups:
            - Fn::ImportValue: !Sub '${ProjectName}-${Environment}-ECS-SG'
          Subnets: !Split
            - ','
            - Fn::ImportValue: !Sub '${ProjectName}-${Environment}-PrivateSubnets'
          AssignPublicIp: DISABLED
      LoadBalancers:
        - ContainerName: !Sub '${ProjectName}-api'
          ContainerPort: 3000
          TargetGroupArn: !Ref TargetGroup
      HealthCheckGracePeriodSeconds: 120
      DeploymentConfiguration:
        MaximumPercent: 200
        MinimumHealthyPercent: 50
        DeploymentCircuitBreaker:
          Enable: true
          Rollback: true

Outputs:
  ClusterName:
    Description: ECS Cluster Name
    Value: !Ref ECSCluster
    Export:
      Name: !Sub '${ProjectName}-${Environment}-ECS-Cluster'

  ServiceName:
    Description: ECS Service Name
    Value: !Ref ECSService
    Export:
      Name: !Sub '${ProjectName}-${Environment}-ECS-Service'

  LoadBalancerDNS:
    Description: Load Balancer DNS Name
    Value: !GetAtt ApplicationLoadBalancer.DNSName
    Export:
      Name: !Sub '${ProjectName}-${Environment}-ALB-DNS'

  LoadBalancerArn:
    Description: Load Balancer ARN
    Value: !Ref ApplicationLoadBalancer
    Export:
      Name: !Sub '${ProjectName}-${Environment}-ALB-Arn'

  LoadBalancerURL:
    Description: Load Balancer URL
    Value: !Sub 'http://${ApplicationLoadBalancer.DNSName}'
    Export:
      Name: !Sub '${ProjectName}-${Environment}-ALB-URL'
Enter fullscreen mode Exit fullscreen mode

Before Deploying, Validate:

aws cloudformation validate-template --template-body file://cloudformation/compute/ecs-cluster.yaml

# Check if these exports exist from your other stacks:
aws cloudformation list-exports --query 'Exports[?contains(Name,
ecommerce-production)].Name'

`# Get your AWS account ID
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

Set image URI

IMAGE_URI="$AWS_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/ecommerce-api:latest"

Deploy ECS stack

aws cloudformation create-stack \
--stack-name ecommerce-ecs \
--template-body file://cloudformation/compute/ecs-cluster.yaml \
--parameters ParameterKey=ProjectName,ParameterValue=ecommerce \
ParameterKey=Environment,ParameterValue=production \
ParameterKey=ImageURI,ParameterValue=$IMAGE_URI

This will take 10-15 minutes

aws cloudformation wait stack-create-complete --stack-name ecommerce-ecs`

Day 8: S3 Buckets Setup
Step 1: Create S3 Template

touch cloudformation/storage/s3-buckets.yaml

File: cloudformation/storage/s3-buckets.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: 'S3 Buckets for E-commerce Application'

Parameters:
  ProjectName:
    Type: String
    Default: ecommerce
  Environment:
    Type: String
    Default: production
  AllowedOrigins:
    Type: String
    Default: 'https://example.com,https://www.example.com'
    Description: Allowed origins for CORS (comma-separated)

Resources:
  # KMS Key for S3 Encryption
  S3KMSKey:
    Type: AWS::KMS::Key
    Properties:
      Description: KMS Key for S3 bucket encryption
      KeyPolicy:
        Version: '2012-10-17'
        Statement:
          - Sid: Enable IAM User Permissions
            Effect: Allow
            Principal:
              AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
            Action: 'kms:*'
            Resource: '*'
          - Sid: Allow S3 Service
            Effect: Allow
            Principal:
              Service: s3.amazonaws.com
            Action:
              - kms:Decrypt
              - kms:GenerateDataKey
            Resource: '*'

  S3KMSKeyAlias:
    Type: AWS::KMS::Alias
    Properties:
      AliasName: !Sub 'alias/${ProjectName}-${Environment}-s3-key'
      TargetKeyId: !Ref S3KMSKey

  # CloudWatch Log Group for S3 (moved up for dependency order)
  S3LogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub '/aws/s3/${ProjectName}-${Environment}'
      RetentionInDays: 14

  # Frontend Assets Bucket
  FrontendBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub '${ProjectName}-${Environment}-frontend-${AWS::AccountId}'
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      LifecycleConfiguration:
        Rules:
          - Id: DeleteOldVersions
            Status: Enabled
            NoncurrentVersionExpirationInDays: 30
          - Id: DeleteIncompleteMultipartUploads
            Status: Enabled
            AbortIncompleteMultipartUpload:
              DaysAfterInitiation: 7
      # Removed invalid NotificationConfiguration
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-frontend'
        - Key: Purpose
          Value: 'Frontend static assets'

  # Product Images Bucket
  ProductImagesBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub '${ProjectName}-${Environment}-product-images-${AWS::AccountId}'
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: aws:kms
              KMSMasterKeyID: !Ref S3KMSKey
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      CorsConfiguration:
        CorsRules:
          - AllowedHeaders: ['*']
            AllowedMethods: [GET, PUT, POST, DELETE]
            AllowedOrigins: !Ref AllowedOrigins
            MaxAge: 3600
      LifecycleConfiguration:
        Rules:
          - Id: TransitionToIA
            Status: Enabled
            Transitions:
              - Days: 30
                StorageClass: STANDARD_IA
          - Id: TransitionToGlacier
            Status: Enabled
            Transitions:
              - Days: 90
                StorageClass: GLACIER
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-product-images'

  # User Uploads Bucket
  UserUploadsBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub '${ProjectName}-${Environment}-user-uploads-${AWS::AccountId}'
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: aws:kms
              KMSMasterKeyID: !Ref S3KMSKey
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      CorsConfiguration:
        CorsRules:
          - AllowedHeaders: ['*']
            AllowedMethods: [PUT, POST]
            AllowedOrigins: !Ref AllowedOrigins
            MaxAge: 3600
      LifecycleConfiguration:
        Rules:
          - Id: DeleteOldVersions
            Status: Enabled
            NoncurrentVersionExpirationInDays: 7
          - Id: DeleteOldUploads
            Status: Enabled
            ExpirationInDays: 365
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-user-uploads'

  # CloudFront Origin Access Control (replaces deprecated OAI)
  CloudFrontOAC:
    Type: AWS::CloudFront::OriginAccessControl
    Properties:
      OriginAccessControlConfig:
        Name: !Sub '${ProjectName}-${Environment}-oac'
        Description: !Sub 'OAC for ${ProjectName}-${Environment}'
        OriginAccessControlOriginType: s3
        SigningBehavior: always
        SigningProtocol: sigv4

  # Bucket Policy for CloudFront OAC
  FrontendBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref FrontendBucket
      PolicyDocument:
        Statement:
          - Sid: AllowCloudFrontServicePrincipal
            Effect: Allow
            Principal:
              Service: cloudfront.amazonaws.com
            Action: 's3:GetObject'
            Resource: !Sub '${FrontendBucket}/*'
            Condition:
              StringEquals:
                'AWS:SourceArn': !Sub 'arn:aws:cloudfront::${AWS::AccountId}:distribution/*'

Outputs:
  FrontendBucketName:
    Description: Name of the frontend assets bucket
    Value: !Ref FrontendBucket
    Export:
      Name: !Sub '${ProjectName}-${Environment}-Frontend-Bucket'

  ProductImagesBucketName:
    Description: Name of the product images bucket
    Value: !Ref ProductImagesBucket
    Export:
      Name: !Sub '${ProjectName}-${Environment}-ProductImages-Bucket'

  UserUploadsBucketName:
    Description: Name of the user uploads bucket
    Value: !Ref UserUploadsBucket
    Export:
      Name: !Sub '${ProjectName}-${Environment}-UserUploads-Bucket'

  CloudFrontOAC:
    Description: CloudFront Origin Access Control
    Value: !Ref CloudFrontOAC
    Export:
      Name: !Sub '${ProjectName}-${Environment}-CloudFront-OAC'

  FrontendBucketDomainName:
    Description: Frontend bucket domain name
    Value: !GetAtt FrontendBucket.DomainName
    Export:
      Name: !Sub '${ProjectName}-${Environment}-Frontend-Bucket-Domain'

  S3KMSKeyId:
    Description: KMS Key ID for S3 encryption
    Value: !Ref S3KMSKey
    Export:
      Name: !Sub '${ProjectName}-${Environment}-S3-KMS-Key'

  S3KMSKeyArn:
    Description: KMS Key ARN for S3 encryption
    Value: !GetAtt S3KMSKey.Arn
    Export:
      Name: !Sub '${ProjectName}-${Environment}-S3-KMS-Key-Arn'
Enter fullscreen mode Exit fullscreen mode

Validation Commands:

`# Validate the template
aws cloudformation validate-template --template-body file://cloudformation/storage/s3-buckets.yaml

Deploy with custom origins

aws cloudformation create-stack \
--stack-name ecommerce-s3 \
--template-body file://cloudformation/storage/s3-buckets.yaml \
--parameters '[{"ParameterKey":"AllowedOrigins","ParameterValue":"https://yourdomain.com,https://www.yourdomain.com"}]'`{% endraw %}

Next steps to monitor the stack:
Check stack status:
{% raw %}aws cloudformation describe-stacks --stack-name ecommerce-s3

Monitor stack events (see what's happening):
aws cloudformation describe-stack-events --stack-name ecommerce-s3

Get stack resources (once complete):
aws cloudformation describe-stack-resources --stack-name ecommerce-s3

Get stack outputs (if any are defined):
aws cloudformation describe-stacks --stack-name ecommerce-s3 --query 'Stacks[0].Outputs'

loudFront Distribution
Step 1: Create CloudFront Template

touch cloudformation/storage/cloudfront-distribution.yaml

File: cloudformation/storage/cloudfront-distribution.yaml

# Or see all events in chronological order to understand the full timeline
aws cloudformation describe-stack-events --stack-name ecommerce-cloudfront --query 'reverse(sort_by(StackEvents, &Timestamp))[0:30].[Timestamp,LogicalResourceId,ResourceStatus,ResourceStatusReason]' --output table


AWSTemplateFormatVersion: '2010-09-09'
Description: 'CloudFront Distribution for E-commerce Application - Fully Fixed'

Parameters:
  ProjectName:
    Type: String
    Default: ecommerce
    Description: Project name prefix for resources
  Environment:
    Type: String
    Default: production
    AllowedValues: [production, staging, development]
    Description: Deployment environment

Resources:
  # S3 Bucket for Frontend (with proper ownership controls)
  FrontendBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub '${ProjectName}-${Environment}-frontend-${AWS::AccountId}'
      WebsiteConfiguration:
        IndexDocument: index.html
        ErrorDocument: error.html
      PublicAccessBlockConfiguration:
        BlockPublicAcls: false
        BlockPublicPolicy: false
        IgnorePublicAcls: false
        RestrictPublicBuckets: false
      OwnershipControls:
        Rules:
          - ObjectOwnership: BucketOwnerPreferred
      CorsConfiguration:
        CorsRules:
          - AllowedHeaders: ['*']
            AllowedMethods: [GET, HEAD]
            AllowedOrigins: ['*']
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-frontend'
        - Key: Environment
          Value: !Ref Environment

  # CloudFront Origin Access Control (OAC) with enhanced description
  CloudFrontOAC:
    Type: AWS::CloudFront::OriginAccessControl
    Properties:
      OriginAccessControlConfig:
        Name: !Sub '${ProjectName}-${Environment}-oac'
        OriginAccessControlOriginType: s3
        SigningBehavior: always
        SigningProtocol: sigv4
        Description: !Sub 'OAC for ${ProjectName}-${Environment} S3 bucket (Managed by CloudFormation)'

  # Logging bucket with all required configurations
  LoggingBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub '${ProjectName}-${Environment}-cf-logs-${AWS::AccountId}'
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: false    # Required for CloudFront logging
        IgnorePublicAcls: false  # Required for CloudFront logging
        BlockPublicPolicy: true
        RestrictPublicBuckets: true
      OwnershipControls:
        Rules:
          - ObjectOwnership: BucketOwnerPreferred
      LifecycleConfiguration:
        Rules:
          - Id: DeleteOldLogs
            Status: Enabled
            ExpirationInDays: 90
            NoncurrentVersionExpirationInDays: 90
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-cf-logs'
        - Key: Environment
          Value: !Ref Environment

  # Security Headers Policy with XSS protection
  SecurityHeadersPolicy:
    Type: AWS::CloudFront::ResponseHeadersPolicy
    Properties:
      ResponseHeadersPolicyConfig:
        Name: !Sub '${ProjectName}-${Environment}-security-headers'
        Comment: Security headers for e-commerce application
        SecurityHeadersConfig:
          StrictTransportSecurity:
            AccessControlMaxAgeSec: 31536000
            IncludeSubdomains: true
            Override: true
          ContentTypeOptions:
            Override: true
          FrameOptions:
            FrameOption: DENY
            Override: true
          ReferrerPolicy:
            ReferrerPolicy: strict-origin-when-cross-origin
            Override: true

        CustomHeadersConfig:
          Items:
            - Header: X-Custom-Header
              Value: !Sub '${ProjectName}-${Environment}'
              Override: false

  # CloudFront Distribution with all fixes
  CloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    DependsOn: 
      - SecurityHeadersPolicy
      - CloudFrontOAC
      - LoggingBucket
      - FrontendBucket
    Properties:
      DistributionConfig:
        Origins:
          # S3 Origin
          - DomainName: !GetAtt FrontendBucket.DomainName
            Id: S3Origin
            OriginAccessControlId: !Ref CloudFrontOAC
            S3OriginConfig: {}

          # ALB Origin with validation
          - DomainName: 
              Fn::ImportValue: !Sub '${ProjectName}-${Environment}-ALB-DNS'
            Id: ALBOrigin
            CustomOriginConfig:
              HTTPPort: 80
              HTTPSPort: 443
              OriginProtocolPolicy: http-only
              OriginSSLProtocols: [TLSv1.2]
              OriginReadTimeout: 30
              OriginKeepaliveTimeout: 5

        DefaultCacheBehavior:
          TargetOriginId: S3Origin
          ViewerProtocolPolicy: redirect-to-https
          CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad  # CachingOptimized
          OriginRequestPolicyId: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf  # CORS-S3Origin
          ResponseHeadersPolicyId: !Ref SecurityHeadersPolicy
          Compress: true
          SmoothStreaming: false
          AllowedMethods: [GET, HEAD, OPTIONS]
          CachedMethods: [GET, HEAD, OPTIONS]

        CacheBehaviors:
          # API Path
          - PathPattern: '/api/*'
            TargetOriginId: ALBOrigin
            ViewerProtocolPolicy: redirect-to-https
            CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
            OriginRequestPolicyId: 216adef6-5c7f-47e4-b989-5492eafa07d3  # AllViewer
            ResponseHeadersPolicyId: !Ref SecurityHeadersPolicy
            AllowedMethods: [GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE]
            CachedMethods: [GET, HEAD, OPTIONS]

          # Static Assets Path
          - PathPattern: '/static/*'
            TargetOriginId: S3Origin
            ViewerProtocolPolicy: redirect-to-https
            CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6  # CachingOptimized
            OriginRequestPolicyId: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf
            ResponseHeadersPolicyId: !Ref SecurityHeadersPolicy
            Compress: true
            AllowedMethods: [GET, HEAD, OPTIONS]
            CachedMethods: [GET, HEAD, OPTIONS]

        Enabled: true
        DefaultRootObject: index.html
        HttpVersion: http2
        IPV6Enabled: true

        CustomErrorResponses:
          - ErrorCode: 403
            ResponseCode: 200
            ResponsePagePath: /index.html
            ErrorCachingMinTTL: 300
          - ErrorCode: 404
            ResponseCode: 200
            ResponsePagePath: /index.html
            ErrorCachingMinTTL: 300

        PriceClass: PriceClass_100

        ViewerCertificate:
          CloudFrontDefaultCertificate: true

        Logging:
          Bucket: !GetAtt LoggingBucket.DomainName
          IncludeCookies: false
          Prefix: cloudfront-logs/

        Comment: !Sub 'CloudFront distribution for ${ProjectName}-${Environment}'

      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-cloudfront'
        - Key: Environment
          Value: !Ref Environment

  # S3 Bucket Policy with proper dependencies
  FrontendBucketPolicy:
    Type: AWS::S3::BucketPolicy
    DependsOn: 
      - CloudFrontDistribution
      - FrontendBucket
    Properties:
      Bucket: !Ref FrontendBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: cloudfront.amazonaws.com
            Action: s3:GetObject
            Resource: !Sub '${FrontendBucket.Arn}/*'
            Condition:
              StringEquals:
                'AWS:SourceArn': !Sub 'arn:aws:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}'

  # CloudWatch Log Group with retention policy
  CloudFrontLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub '/aws/cloudfront/${ProjectName}-${Environment}'
      RetentionInDays: 30
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-cf-logs'
        - Key: Environment
          Value: !Ref Environment

  # SNS Topic with proper naming
  SNSAlarmTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: !Sub '${ProjectName}-${Environment}-cloudfront-alarms'
      DisplayName: !Sub '${ProjectName}-${Environment} CloudFront Alerts'
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-${Environment}-cf-alerts'
        - Key: Environment
          Value: !Ref Environment

  # Enhanced CloudWatch Alarms
  HighErrorRateAlarm:
    Type: AWS::CloudWatch::Alarm
    DependsOn: 
      - CloudFrontDistribution
      - SNSAlarmTopic
    Properties:
      AlarmName: !Sub '${ProjectName}-${Environment}-cloudfront-high-error-rate'
      AlarmDescription: CloudFront high 4xx/5xx error rate (>5% for 15 minutes)
      Namespace: AWS/CloudFront
      MetricName: 4xxErrorRate
      Dimensions:
        - Name: DistributionId
          Value: !Ref CloudFrontDistribution
        - Name: Region
          Value: Global
      Statistic: Average
      Period: 300
      EvaluationPeriods: 3
      Threshold: 5
      ComparisonOperator: GreaterThanThreshold
      TreatMissingData: notBreaching
      AlarmActions:
        - !Ref SNSAlarmTopic
      OKActions:
        - !Ref SNSAlarmTopic

  HighOriginLatencyAlarm:
    Type: AWS::CloudWatch::Alarm
    DependsOn: 
      - CloudFrontDistribution
      - SNSAlarmTopic
    Properties:
      AlarmName: !Sub '${ProjectName}-${Environment}-cloudfront-high-origin-latency'
      AlarmDescription: CloudFront origin latency >3s for 15 minutes
      Namespace: AWS/CloudFront
      MetricName: OriginLatency
      Dimensions:
        - Name: DistributionId
          Value: !Ref CloudFrontDistribution
        - Name: Region
          Value: Global
      Statistic: Average
      Period: 300
      EvaluationPeriods: 3
      Threshold: 3000
      ComparisonOperator: GreaterThanThreshold
      TreatMissingData: notBreaching
      AlarmActions:
        - !Ref SNSAlarmTopic
      OKActions:
        - !Ref SNSAlarmTopic

Outputs:
  CloudFrontDistributionId:
    Description: CloudFront Distribution ID
    Value: !Ref CloudFrontDistribution
    Export:
      Name: !Sub '${ProjectName}-${Environment}-CloudFront-DistributionId'

  CloudFrontDomainName:
    Description: CloudFront Distribution Domain Name
    Value: !GetAtt CloudFrontDistribution.DomainName
    Export:
      Name: !Sub '${ProjectName}-${Environment}-CloudFront-DomainName'

  CloudFrontDistributionURL:
    Description: CloudFront Distribution URL
    Value: !Sub 'https://${CloudFrontDistribution.DomainName}'
    Export:
      Name: !Sub '${ProjectName}-${Environment}-CloudFront-URL'

  FrontendBucketName:
    Description: Frontend S3 Bucket Name
    Value: !Ref FrontendBucket
    Export:
      Name: !Sub '${ProjectName}-${Environment}-Frontend-Bucket-Name'

  FrontendWebsiteURL:
    Description: Frontend Website URL
    Value: !GetAtt FrontendBucket.WebsiteURL
    Export:
      Name: !Sub '${ProjectName}-${Environment}-Frontend-Website-URL'

  LoggingBucketName:
    Description: CloudFront Logging Bucket Name
    Value: !Ref LoggingBucket
    Export:
      Name: !Sub '${ProjectName}-${Environment}-Logging-Bucket-Name'

  SecurityHeadersPolicyId:
    Description: Security Headers Policy ID
    Value: !Ref SecurityHeadersPolicy
    Export:
      Name: !Sub '${ProjectName}-${Environment}-Security-Headers-Policy'

  SNSAlarmTopicARN:
    Description: SNS Alarm Topic ARN
    Value: !Ref SNSAlarmTopic
    Export:
      Name: !Sub '${ProjectName}-${Environment}-SNS-Alarm-Topic'

Enter fullscreen mode Exit fullscreen mode

Step 2: Deploy CloudFront

`# Deploy CloudFront distribution
aws cloudformation create-stack \
--stack-name ecommerce-cloudfront \
--template-body file://cloudformation/storage/cloudfront-distribution.yaml \
--parameters ParameterKey=ProjectName,ParameterValue=ecommerce \
ParameterKey=Environment,ParameterValue=production

This takes 15-20 minutes

aws cloudformation wait stack-create-complete --stack-name ecommerce-cloudfront`

Testing and Verification
Step 1: Create Test Scripts

mkdir -p scripts/testing
touch scripts/testing/test-infrastructure.sh

File: scripts/testing/test-infrastructure.sh

#!/bin/bash

set -e

echo "๐Ÿ” Testing AWS E-commerce Infrastructure..."

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# Test functions
test_vpc() {
    echo -e "${YELLOW}Testing VPC...${NC}"
    VPC_ID=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=ecommerce-production-vpc" --query 'Vpcs[0].VpcId' --output text)
    if [ "$VPC_ID" != "None" ] && [ "$VPC_ID" != "" ]; then
        echo -e "${GREEN}โœ“ VPC exists: $VPC_ID${NC}"
    else
        echo -e "${RED}โœ— VPC not found${NC}"
        exit 1
    fi
}

test_subnets() {
    echo -e "${YELLOW}Testing Subnets...${NC}"
    SUBNET_COUNT=$(aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPC_ID" --query 'length(Subnets)')
    if [ "$SUBNET_COUNT" -ge 6 ]; then
        echo -e "${GREEN}โœ“ All subnets created: $SUBNET_COUNT${NC}"
    else
        echo -e "${RED}โœ— Missing subnets. Expected 6, found $SUBNET_COUNT${NC}"
        exit 1
    fi
}

test_rds() {
    echo -e "${YELLOW}Testing RDS...${NC}"
    RDS_STATUS=$(aws rds describe-db-instances --db-instance-identifier ecommerce-production-postgres --query 'DBInstances[0].DBInstanceStatus' --output text 2>/dev/null || echo "NOT_FOUND")
    if [ "$RDS_STATUS" = "available" ]; then
        echo -e "${GREEN}โœ“ RDS instance is available${NC}"
    else
        echo -e "${RED}โœ— RDS instance not available. Status: $RDS_STATUS${NC}"
        exit 1
    fi
}

test_ecs() {
    echo -e "${YELLOW}Testing ECS...${NC}"
    CLUSTER_STATUS=$(aws ecs describe-clusters --clusters ecommerce-production-cluster --query 'clusters[0].status' --output text 2>/dev/null || echo "NOT_FOUND")
    if [ "$CLUSTER_STATUS" = "ACTIVE" ]; then
        echo -e "${GREEN}โœ“ ECS cluster is active${NC}"

        # Test ECS service
        SERVICE_STATUS=$(aws ecs describe-services --cluster ecommerce-production-cluster --services ecommerce-production-api-service --query 'services[0].status' --output text 2>/dev/null || echo "NOT_FOUND")
        if [ "$SERVICE_STATUS" = "ACTIVE" ]; then
            echo -e "${GREEN}โœ“ ECS service is active${NC}"
        else
            echo -e "${RED}โœ— ECS service not active. Status: $SERVICE_STATUS${NC}"
        fi
    else
        echo -e "${RED}โœ— ECS cluster not active. Status: $CLUSTER_STATUS${NC}"
        exit 1
    fi
}

test_alb() {
    echo -e "${YELLOW}Testing Application Load Balancer...${NC}"
    ALB_DNS=$(aws cloudformation describe-stacks --stack-name ecommerce-ecs --query 'Stacks[0].Outputs[?OutputKey==`LoadBalancerDNS`].OutputValue' --output text 2>/dev/null || echo "NOT_FOUND")
    if [ "$ALB_DNS" != "NOT_FOUND" ] && [ "$ALB_DNS" != "" ]; then
        echo -e "${GREEN}โœ“ ALB exists: $ALB_DNS${NC}"

        # Test health endpoint
        echo -e "${YELLOW}Testing API health endpoint...${NC}"
        HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "http://$ALB_DNS/health" || echo "000")
        if [ "$HTTP_STATUS" = "200" ]; then
            echo -e "${GREEN}โœ“ API health check passed${NC}"
        else
            echo -e "${RED}โœ— API health check failed. HTTP Status: $HTTP_STATUS${NC}"
        fi
    else
        echo -e "${RED}โœ— ALB not found${NC}"
        exit 1
    fi
}

test_s3() {
    echo -e "${YELLOW}Testing S3 Buckets...${NC}"
    FRONTEND_BUCKET=$(aws cloudformation describe-stacks --stack-name ecommerce-s3 --query 'Stacks[0].Outputs[?OutputKey==`FrontendBucketName`].OutputValue' --output text 2>/dev/null || echo "NOT_FOUND")
    if [ "$FRONTEND_BUCKET" != "NOT_FOUND" ]; then
        echo -e "${GREEN}โœ“ Frontend bucket exists: $FRONTEND_BUCKET${NC}"
    else
        echo -e "${RED}โœ— Frontend bucket not found${NC}"
        exit 1
    fi
}

test_cloudfront() {
    echo -e "${YELLOW}Testing CloudFront...${NC}"
    CF_DOMAIN=$(aws cloudformation describe-stacks --stack-name ecommerce-cloudfront --query 'Stacks[0].Outputs[?OutputKey==`CloudFrontDomainName`].OutputValue' --output text 2>/dev/null || echo "NOT_FOUND")
    if [ "$CF_DOMAIN" != "NOT_FOUND" ]; then
        echo -e "${GREEN}โœ“ CloudFront distribution exists: $CF_DOMAIN${NC}"

        # Test CloudFront endpoint
        echo -e "${YELLOW}Testing CloudFront endpoint...${NC}"
        CF_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://$CF_DOMAIN" || echo "000")
        if [ "$CF_STATUS" = "200" ] || [ "$CF_STATUS" = "403" ]; then
            echo -e "${GREEN}โœ“ CloudFront endpoint accessible${NC}"
        else
            echo -e "${RED}โœ— CloudFront endpoint not accessible. HTTP Status: $CF_STATUS${NC}"
        fi
    else
        echo -e "${RED}โœ— CloudFront distribution not found${NC}"
        exit 1
    fi
}

# Run all tests
echo "๐Ÿš€ Starting infrastructure tests..."
test_vpc
test_subnets
test_rds
test_ecs
test_alb
test_s3
test_cloudfront

echo -e "${GREEN}๐ŸŽ‰ All infrastructure tests passed!${NC}"

# Display useful information
echo ""
echo "๐Ÿ“‹ Infrastructure Summary:"
echo "VPC ID: $VPC_ID"
echo "ALB DNS: $ALB_DNS"
echo "CloudFront Domain: $CF_DOMAIN"
echo "API Health Check: http://$ALB_DNS/health"
echo "API Products Endpoint: http://$ALB_DNS/api/products"
echo ""
echo "๐Ÿ”— Access your application:"
echo "Frontend (CloudFront): https://$CF_DOMAIN"
echo "API (Direct): http://$ALB_DNS/api/products"
Enter fullscreen mode Exit fullscreen mode

Step 2: Run Infrastructure Tests

`# Make script executable
chmod +x scripts/testing/test-infrastructure.sh

Run tests

./scripts/testing/test-infrastructure.sh`

Step 3: Upload Sample Frontend Content

# Create simple frontend for testing
mkdir -p application/frontend/dist

# Save the enhanced HTML to your file
cat > application/frontend/dist/index.html << 'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TechStore - Modern Electronics</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            line-height: 1.6;
            color: #333;
            background: #f8f9fa;
        }

        .container {
            max-width: 1400px;
            margin: 0 auto;
            padding: 0 20px;
        }

        /* Header */
        header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 1rem 0;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }

        .header-content {
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
        }

        .logo {
            font-size: 2rem;
            font-weight: bold;
            text-decoration: none;
            color: white;
        }

        nav ul {
            display: flex;
            list-style: none;
            gap: 2rem;
        }

        nav a {
            color: white;
            text-decoration: none;
            font-weight: 500;
            transition: opacity 0.3s;
        }

        nav a:hover {
            opacity: 0.8;
        }

        .cart-info {
            display: flex;
            align-items: center;
            gap: 1rem;
        }

        .cart-count {
            background: #ff6b6b;
            border-radius: 50%;
            padding: 0.2rem 0.6rem;
            font-size: 0.8rem;
            font-weight: bold;
        }

        /* Hero Section */
        .hero {
            background: linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0.4)), url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 600"><rect fill="%23f0f0f0" width="1200" height="600"/><text x="600" y="300" text-anchor="middle" font-size="48" fill="%23999">Hero Background</text></svg>');
            background-size: cover;
            background-position: center;
            color: white;
            text-align: center;
            padding: 6rem 0;
        }

        .hero h1 {
            font-size: 3.5rem;
            margin-bottom: 1rem;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
        }

        .hero p {
            font-size: 1.3rem;
            margin-bottom: 2rem;
            opacity: 0.9;
        }

        .btn {
            display: inline-block;
            background: #ff6b6b;
            color: white;
            padding: 1rem 2rem;
            text-decoration: none;
            border-radius: 50px;
            font-weight: bold;
            transition: all 0.3s;
            border: none;
            cursor: pointer;
            font-size: 1rem;
        }

        .btn:hover {
            background: #ff5252;
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(255, 107, 107, 0.4);
        }

        .btn-secondary {
            background: transparent;
            border: 2px solid #ff6b6b;
            color: #ff6b6b;
        }

        .btn-secondary:hover {
            background: #ff6b6b;
            color: white;
        }

        /* API Status */
        .api-status {
            margin: 2rem 0;
            padding: 1rem;
            border-radius: 8px;
            text-align: center;
            font-weight: bold;
        }

        .status-loading {
            background: #fff3cd;
            color: #856404;
            border: 1px solid #ffeaa7;
        }

        .status-ok {
            background: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
        }

        .status-error {
            background: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }

        /* Categories */
        .categories {
            padding: 4rem 0;
            background: white;
        }

        .section-title {
            text-align: center;
            font-size: 2.5rem;
            margin-bottom: 3rem;
            color: #333;
        }

        .categories-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 2rem;
            margin-bottom: 3rem;
        }

        .category-card {
            background: linear-gradient(45deg, #667eea, #764ba2);
            color: white;
            padding: 2rem;
            border-radius: 15px;
            text-align: center;
            cursor: pointer;
            transition: transform 0.3s, box-shadow 0.3s;
        }

        .category-card:hover {
            transform: translateY(-5px);
            box-shadow: 0 10px 25px rgba(0,0,0,0.15);
        }

        .category-icon {
            font-size: 3rem;
            margin-bottom: 1rem;
        }

        /* Products Section */
        .products-section {
            padding: 4rem 0;
            background: #f8f9fa;
        }

        .filters {
            display: flex;
            justify-content: center;
            gap: 1rem;
            margin-bottom: 3rem;
            flex-wrap: wrap;
        }

        .filter-btn {
            background: white;
            border: 2px solid #ddd;
            padding: 0.5rem 1.5rem;
            border-radius: 25px;
            cursor: pointer;
            transition: all 0.3s;
        }

        .filter-btn.active,
        .filter-btn:hover {
            background: #667eea;
            color: white;
            border-color: #667eea;
        }

        .products-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
            gap: 2rem;
        }

        .product-card {
            background: white;
            border-radius: 15px;
            overflow: hidden;
            box-shadow: 0 5px 15px rgba(0,0,0,0.08);
            transition: transform 0.3s, box-shadow 0.3s;
            cursor: pointer;
        }

        .product-card:hover {
            transform: translateY(-5px);
            box-shadow: 0 15px 35px rgba(0,0,0,0.15);
        }

        .product-image {
            width: 100%;
            height: 250px;
            background: linear-gradient(45deg, #f0f0f0, #e0e0e0);
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 4rem;
            color: #999;
        }

        .product-info {
            padding: 1.5rem;
        }

        .product-title {
            font-size: 1.25rem;
            font-weight: bold;
            margin-bottom: 0.5rem;
            color: #333;
        }

        .product-description {
            color: #666;
            margin-bottom: 1rem;
            font-size: 0.9rem;
        }

        .product-price {
            display: flex;
            align-items: center;
            gap: 1rem;
            margin-bottom: 1rem;
        }

        .current-price {
            font-size: 1.5rem;
            font-weight: bold;
            color: #ff6b6b;
        }

        .original-price {
            text-decoration: line-through;
            color: #999;
        }

        .discount {
            background: #ff6b6b;
            color: white;
            padding: 0.2rem 0.5rem;
            border-radius: 4px;
            font-size: 0.8rem;
        }

        .product-rating {
            display: flex;
            align-items: center;
            gap: 0.5rem;
            margin-bottom: 1rem;
        }

        .stars {
            color: #ffd700;
        }

        .rating-count {
            color: #666;
            font-size: 0.9rem;
        }

        .add-to-cart {
            width: 100%;
            background: #667eea;
            color: white;
            border: none;
            padding: 0.75rem;
            border-radius: 8px;
            font-weight: bold;
            cursor: pointer;
            transition: background 0.3s;
        }

        .add-to-cart:hover {
            background: #5a6fd8;
        }

        /* Footer */
        footer {
            background: #333;
            color: white;
            padding: 3rem 0 1rem;
        }

        .footer-content {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
            gap: 2rem;
            margin-bottom: 2rem;
        }

        .footer-section h3 {
            margin-bottom: 1rem;
            color: #ff6b6b;
        }

        .footer-section ul {
            list-style: none;
        }

        .footer-section a {
            color: #ccc;
            text-decoration: none;
            transition: color 0.3s;
        }

        .footer-section a:hover {
            color: #ff6b6b;
        }

        .footer-bottom {
            text-align: center;
            padding-top: 2rem;
            border-top: 1px solid #555;
            color: #ccc;
        }

        /* Cart Modal */
        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.5);
            z-index: 1000;
        }

        .modal-content {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: white;
            padding: 2rem;
            border-radius: 15px;
            max-width: 500px;
            width: 90%;
            max-height: 80vh;
            overflow-y: auto;
        }

        .close {
            position: absolute;
            top: 1rem;
            right: 1rem;
            font-size: 2rem;
            cursor: pointer;
            color: #999;
        }

        .cart-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 1rem 0;
            border-bottom: 1px solid #eee;
        }

        .cart-total {
            font-size: 1.5rem;
            font-weight: bold;
            text-align: right;
            margin-top: 1rem;
            color: #ff6b6b;
        }

        /* Responsive */
        @media (max-width: 768px) {
            .header-content {
                flex-direction: column;
                gap: 1rem;
            }

            nav ul {
                flex-wrap: wrap;
                justify-content: center;
            }

            .hero h1 {
                font-size: 2.5rem;
            }

            .products-grid {
                grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
            }
        }

        .loading {
            text-align: center;
            padding: 2rem;
            color: #666;
        }

        .spinner {
            border: 4px solid #f3f3f3;
            border-top: 4px solid #667eea;
            border-radius: 50%;
            width: 50px;
            height: 50px;
            animation: spin 1s linear infinite;
            margin: 0 auto 1rem;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
    </style>
</head>
<body>
    <!-- Header -->
    <header>
        <div class="container">
            <div class="header-content">
                <a href="#" class="logo">๐Ÿ›’ TechStore</a>
                <nav>
                    <ul>
                        <li><a href="#home">Home</a></li>
                        <li><a href="#products">Products</a></li>
                        <li><a href="#categories">Categories</a></li>
                        <li><a href="#about">About</a></li>
                        <li><a href="#contact">Contact</a></li>
                    </ul>
                </nav>
                <div class="cart-info">
                    <span>Cart: <span class="cart-count" id="cartCount">0</span></span>
                    <button class="btn btn-secondary" onclick="openCart()">View Cart</button>
                </div>
            </div>
        </div>
    </header>

    <!-- Hero Section -->
    <section class="hero" id="home">
        <div class="container">
            <h1>Welcome to TechStore</h1>
            <p>Discover the latest technology and electronics at unbeatable prices</p>
            <a href="#products" class="btn">Shop Now</a>
        </div>
    </section>

    <!-- API Status -->
    <div class="container">
        <div class="api-status status-loading" id="apiStatus">
            <div class="spinner"></div>
            Testing API connection...
        </div>
    </div>

    <!-- Categories -->
    <section class="categories" id="categories">
        <div class="container">
            <h2 class="section-title">Shop by Category</h2>
            <div class="categories-grid">
                <div class="category-card" onclick="filterProducts('smartphones')">
                    <div class="category-icon">๐Ÿ“ฑ</div>
                    <h3>Smartphones</h3>
                    <p>Latest mobile devices</p>
                </div>
                <div class="category-card" onclick="filterProducts('laptops')">
                    <div class="category-icon">๐Ÿ’ป</div>
                    <h3>Laptops</h3>
                    <p>Powerful computing</p>
                </div>
                <div class="category-card" onclick="filterProducts('headphones')">
                    <div class="category-icon">๐ŸŽง</div>
                    <h3>Audio</h3>
                    <p>Premium sound quality</p>
                </div>
                <div class="category-card" onclick="filterProducts('cameras')">
                    <div class="category-icon">๐Ÿ“ท</div>
                    <h3>Cameras</h3>
                    <p>Capture memories</p>
                </div>
                <div class="category-card" onclick="filterProducts('gaming')">
                    <div class="category-icon">๐ŸŽฎ</div>
                    <h3>Gaming</h3>
                    <p>Gaming gear</p>
                </div>
                <div class="category-card" onclick="filterProducts('accessories')">
                    <div class="category-icon">โšก</div>
                    <h3>Accessories</h3>
                    <p>Tech accessories</p>
                </div>
            </div>
        </div>
    </section>

    <!-- Products Section -->
    <section class="products-section" id="products">
        <div class="container">
            <h2 class="section-title">Featured Products</h2>

            <div class="filters">
                <button class="filter-btn active" onclick="filterProducts('all')">All Products</button>
                <button class="filter-btn" onclick="filterProducts('smartphones')">Smartphones</button>
                <button class="filter-btn" onclick="filterProducts('laptops')">Laptops</button>
                <button class="filter-btn" onclick="filterProducts('headphones')">Audio</button>
                <button class="filter-btn" onclick="filterProducts('cameras')">Cameras</button>
                <button class="filter-btn" onclick="filterProducts('gaming')">Gaming</button>
            </div>

            <div class="products-grid" id="productsGrid">
                <div class="loading">
                    <div class="spinner"></div>
                    Loading products...
                </div>
            </div>
        </div>
    </section>

    <!-- Footer -->
    <footer>
        <div class="container">
            <div class="footer-content">
                <div class="footer-section">
                    <h3>About TechStore</h3>
                    <p>Your trusted partner for the latest technology and electronics. We offer high-quality products at competitive prices with excellent customer service.</p>
                </div>
                <div class="footer-section">
                    <h3>Quick Links</h3>
                    <ul>
                        <li><a href="#home">Home</a></li>
                        <li><a href="#products">Products</a></li>
                        <li><a href="#categories">Categories</a></li>
                        <li><a href="#about">About Us</a></li>
                    </ul>
                </div>
                <div class="footer-section">
                    <h3>Customer Service</h3>
                    <ul>
                        <li><a href="#">Contact Us</a></li>
                        <li><a href="#">Shipping Info</a></li>
                        <li><a href="#">Returns</a></li>
                        <li><a href="#">FAQ</a></li>
                    </ul>
                </div>
                <div class="footer-section">
                    <h3>Connect</h3>
                    <ul>
                        <li><a href="#">Facebook</a></li>
                        <li><a href="#">Twitter</a></li>
                        <li><a href="#">Instagram</a></li>
                        <li><a href="#">LinkedIn</a></li>
                    </ul>
                </div>
            </div>
            <div class="footer-bottom">
                <p>&copy; 2025 TechStore. All rights reserved. | Built with AWS</p>
            </div>
        </div>
    </footer>

    <!-- Cart Modal -->
    <div class="modal" id="cartModal">
        <div class="modal-content">
            <span class="close" onclick="closeCart()">&times;</span>
            <h2>Shopping Cart</h2>
            <div id="cartItems"></div>
            <div class="cart-total" id="cartTotal">Total: $0.00</div>
            <button class="btn" style="width: 100%; margin-top: 1rem;" onclick="checkout()">Proceed to Checkout</button>
        </div>
    </div>

    <script>
        // Sample product data
        const products = [
            {
                id: 1,
                name: "iPhone 15 Pro",
                category: "smartphones",
                price: 999,
                originalPrice: 1099,
                description: "Latest iPhone with titanium design and A17 Pro chip",
                rating: 4.8,
                reviews: 1234,
                image: "๐Ÿ“ฑ"
            },
            {
                id: 2,
                name: "MacBook Pro M3",
                category: "laptops",
                price: 1999,
                originalPrice: 2199,
                description: "Powerful laptop with M3 chip for professionals",
                rating: 4.9,
                reviews: 856,
                image: "๐Ÿ’ป"
            },
            {
                id: 3,
                name: "Sony WH-1000XM5",
                category: "headphones",
                price: 399,
                originalPrice: 449,
                description: "Industry-leading noise canceling headphones",
                rating: 4.7,
                reviews: 2103,
                image: "๐ŸŽง"
            },
            {
                id: 4,
                name: "Canon EOS R5",
                category: "cameras",
                price: 3899,
                originalPrice: 4299,
                description: "Professional mirrorless camera with 45MP sensor",
                rating: 4.6,
                reviews: 445,
                image: "๐Ÿ“ท"
            },
            {
                id: 5,
                name: "PlayStation 5",
                category: "gaming",
                price: 499,
                originalPrice: 559,
                description: "Next-gen gaming console with ray tracing",
                rating: 4.8,
                reviews: 3421,
                image: "๐ŸŽฎ"
            },
            {
                id: 6,
                name: "iPad Pro 12.9\"",
                category: "smartphones",
                price: 1099,
                originalPrice: 1199,
                description: "Professional tablet with M2 chip",
                rating: 4.7,
                reviews: 1876,
                image: "๐Ÿ“ฑ"
            },
            {
                id: 7,
                name: "Dell XPS 13",
                category: "laptops",
                price: 1299,
                originalPrice: 1399,
                description: "Ultrabook with InfinityEdge display",
                rating: 4.5,
                reviews: 934,
                image: "๐Ÿ’ป"
            },
            {
                id: 8,
                name: "AirPods Pro",
                category: "headphones",
                price: 249,
                originalPrice: 279,
                description: "Wireless earbuds with active noise cancellation",
                rating: 4.6,
                reviews: 5672,
                image: "๐ŸŽง"
            }
        ];

        let cart = [];
        let currentFilter = 'all';

        // Initialize the page
        document.addEventListener('DOMContentLoaded', function() {
            testAPIConnection();
            displayProducts();
        });

        // Test API connection
        function testAPIConnection() {
            const apiStatus = document.getElementById('apiStatus');

            // Simulate API test
            setTimeout(() => {
                const isConnected = Math.random() > 0.3; // 70% success rate for demo

                if (isConnected) {
                    apiStatus.className = 'api-status status-ok';
                    apiStatus.innerHTML = 'โœ… API Connection Successful - Backend services are running';
                } else {
                    apiStatus.className = 'api-status status-error';
                    apiStatus.innerHTML = 'โŒ API Connection Failed - Using demo data';
                }
            }, 2000);
        }

        // Display products
        function displayProducts() {
            const grid = document.getElementById('productsGrid');
            const filteredProducts = currentFilter === 'all' ? products : products.filter(p => p.category === currentFilter);

            setTimeout(() => {
                grid.innerHTML = filteredProducts.map(product => `
                    <div class="product-card">
                        <div class="product-image">${product.image}</div>
                        <div class="product-info">
                            <h3 class="product-title">${product.name}</h3>
                            <p class="product-description">${product.description}</p>
                            <div class="product-rating">
                                <span class="stars">${'โ˜…'.repeat(Math.floor(product.rating))}${product.rating % 1 ? 'โ˜†' : ''}</span>
                                <span class="rating-count">${product.rating} (${product.reviews} reviews)</span>
                            </div>
                            <div class="product-price">
                                <span class="current-price">$${product.price}</span>
                                ${product.originalPrice > product.price ? `
                                    <span class="original-price">$${product.originalPrice}</span>
                                    <span class="discount">${Math.round((1 - product.price/product.originalPrice) * 100)}% OFF</span>
                                ` : ''}
                            </div>
                            <button class="add-to-cart" onclick="addToCart(${product.id})">Add to Cart</button>
                        </div>
                    </div>
                `).join('');
            }, 500);
        }

        // Filter products
        function filterProducts(category) {
            currentFilter = category;

            // Update filter buttons
            document.querySelectorAll('.filter-btn').forEach(btn => btn.classList.remove('active'));
            event.target.classList.add('active');

            // Show loading
            document.getElementById('productsGrid').innerHTML = `
                <div class="loading">
                    <div class="spinner"></div>
                    Loading ${category === 'all' ? 'all products' : category}...
                </div>
            `;

            displayProducts();
        }

        // Add to cart
        function addToCart(productId) {
            const product = products.find(p => p.id === productId);
            const existingItem = cart.find(item => item.id === productId);

            if (existingItem) {
                existingItem.quantity += 1;
            } else {
                cart.push({...product, quantity: 1});
            }

            updateCartCount();

            // Show feedback
            event.target.style.background = '#28a745';
            event.target.textContent = 'Added!';
            setTimeout(() => {
                event.target.style.background = '#667eea';
                event.target.textContent = 'Add to Cart';
            }, 1000);
        }

        // Update cart count
        function updateCartCount() {
            const count = cart.reduce((total, item) => total + item.quantity, 0);
            document.getElementById('cartCount').textContent = count;
        }

        // Open cart modal
        function openCart() {
            const modal = document.getElementById('cartModal');
            const cartItems = document.getElementById('cartItems');
            const cartTotal = document.getElementById('cartTotal');

            if (cart.length === 0) {
                cartItems.innerHTML = '<p style="text-align: center; color: #666; padding: 2rem;">Your cart is empty</p>';
                cartTotal.textContent = 'Total: $0.00';
            } else {
                cartItems.innerHTML = cart.map(item => `
                    <div class="cart-item">
                        <div>
                            <strong>${item.name}</strong><br>
                            <small>${item.description}</small><br>
                            <small>Quantity: ${item.quantity}</small>
                        </div>
                        <div>
                            <strong>$${(item.price * item.quantity).toFixed(2)}</strong>
                            <button onclick="removeFromCart(${item.id})" style="margin-left: 1rem; background: #ff6b6b; color: white; border: none; padding: 0.2rem 0.5rem; border-radius: 4px; cursor: pointer;">Remove</button>
                        </div>
                    </div>
                `).join('');

                const total = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
                cartTotal.textContent = `Total: $${total.toFixed(2)}`;
            }

            modal.style.display = 'block';
        }

        // Close cart modal
        function closeCart() {
            document.getElementById('cartModal').style.display = 'none';
        }

        // Remove from cart
        function removeFromCart(productId) {
            cart = cart.filter(item => item.id !== productId);
            updateCartCount();
            openCart(); // Refresh cart display
        }

        // Checkout
        function checkout() {
            if (cart.length === 0) {
                alert('Your cart is empty!');
                return;
            }

            const total = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
            alert(`Thank you for your purchase! Total: $${total.toFixed(2)}\n\nThis is a demo - no actual payment processed.`);

            cart = [];
            updateCartCount();
            closeCart();
        }

        // Close modal when clicking outside
        window.onclick = function(event) {
            const modal = document.getElementById('cartModal');
            if (event.target === modal) {
                closeCart();
            }
        }

        // Smooth scrolling for navigation links
        document.querySelectorAll('a[href^="#"]').forEach(anchor => {
            anchor.addEventListener('click', function (e) {
                e.preventDefault();
                const target = document.querySelector(this.getAttribute('href'));
                if (target) {
                    target.scrollIntoView({
                        behavior: 'smooth',
                        block: 'start'
                    });
                }
            });
        });
    </script>
</body>
</html>
EOF

# Upload to S3
FRONTEND_BUCKET=$(aws cloudformation describe-stacks --stack-name ecommerce-cloudfront --query 'Stacks[0].Outputs[?OutputKey==`FrontendBucketName`].OutputValue' --output text)
aws s3 cp application/frontend/dist/index.html s3://$FRONTEND_BUCKET/index.html --content-type text/html
Enter fullscreen mode Exit fullscreen mode
# Upload to S3
FRONTEND_BUCKET=$(aws cloudformation describe-stacks --stack-name ecommerce-s3 --query 'Stacks[0].Outputs[?OutputKey==`FrontendBucketName`].OutputValue' --output text)

If you are in dist directory use this command
aws s3 cp index.html s3://$FRONTEND_BUCKET/index.html --content-type text/html

Enter fullscreen mode Exit fullscreen mode

CI/CD Pipeline and Advanced Features
CodePipeline Setup
Step 1: Create CodeBuild Template

touch cloudformation/cicd/codebuild-project.yaml

File: cloudformation/cicd/codebuild-project.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: 'CodePipeline for E-commerce API deployment'

Parameters:
  ProjectName:
    Type: String
    Default: ecommerce
  Environment:
    Type: String
    Default: production
  GitHubOwner:
    Type: String
    Description: GitHub repository owner
  GitHubRepo:
    Type: String
    Description: GitHub repository name
    Default: ecommerce-infrastructure
  GitHubBranch:
    Type: String
    Description: GitHub branch
    Default: main
  GitHubToken:
    Type: String
    Description: GitHub personal access token
    NoEcho: true

Resources:
  # CodePipeline Service Role
  CodePipelineServiceRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub '${ProjectName}-${Environment}-codepipeline-role'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: codepipeline.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: CodePipelinePolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - s3:GetBucketVersioning
                  - s3:GetObject
                  - s3:GetObjectVersion
                  - s3:PutObject
                  - s3:GetBucketLocation
                  - s3:ListBucket
                Resource:
                  - Fn::ImportValue: !Sub '${ProjectName}-${Environment}-Artifacts-Bucket'
                  - !Sub 
                    - '${BucketArn}/*'
                    - BucketArn:
                        Fn::ImportValue: !Sub '${ProjectName}-${Environment}-Artifacts-Bucket'
              - Effect: Allow
                Action:
                  - codebuild:BatchGetBuilds
                  - codebuild:StartBuild
                Resource:
                  - Fn::ImportValue: !Sub '${ProjectName}-${Environment}-CodeBuild-Project'
              - Effect: Allow
                Action:
                  - ecs:DescribeServices
                  - ecs:DescribeTaskDefinition
                  - ecs:DescribeTasks
                  - ecs:ListTasks
                  - ecs:RegisterTaskDefinition
                  - ecs:UpdateService
                Resource: '*'
              - Effect: Allow
                Action:
                  - iam:PassRole
                Resource: '*'

  # CodeDeploy Application
  CodeDeployApplication:
    Type: AWS::CodeDeploy::Application
    Properties:
      ApplicationName: !Sub '${ProjectName}-${Environment}-ecs-app'
      ComputePlatform: ECS

  # CodeDeploy Service Role
  CodeDeployServiceRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub '${ProjectName}-${Environment}-codedeploy-role'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: codedeploy.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS

  # CodeDeploy Deployment Group
  CodeDeployDeploymentGroup:
    Type: AWS::CodeDeploy::DeploymentGroup
    Properties:
      ApplicationName: !Ref CodeDeployApplication
      DeploymentGroupName: !Sub '${ProjectName}-${Environment}-deployment-group'
      ServiceRoleArn: !GetAtt CodeDeployServiceRole.Arn
      DeploymentConfigName: CodeDeployDefault.ECSAllAtOnce
      ECSServices:
        - ServiceName: 
            Fn::ImportValue: !Sub '${ProjectName}-${Environment}-ECS-Service'
          ClusterName:
            Fn::ImportValue: !Sub '${ProjectName}-${Environment}-ECS-Cluster'
      LoadBalancerInfo:
        TargetGroupInfoList:
          - Name: !Sub '${ProjectName}-${Environment}-tg'
      BlueGreenDeploymentConfiguration:
        TerminateBlueInstancesOnDeploymentSuccess:
          Action: TERMINATE
          TerminationWaitTimeInMinutes: 5
        DeploymentReadyOption:
          ActionOnTimeout: CONTINUE_DEPLOYMENT
        GreenFleetProvisioningOption:
          Action: COPY_AUTO_SCALING_GROUP

  # CodePipeline
  CodePipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Name: !Sub '${ProjectName}-${Environment}-pipeline'
      RoleArn: !GetAtt CodePipelineServiceRole.Arn
      ArtifactStore:
        Type: S3
        Location:
          Fn::ImportValue: !Sub '${ProjectName}-${Environment}-Artifacts-Bucket'
      Stages:
        - Name: Source
          Actions:
            - Name: SourceAction
              ActionTypeId:
                Category: Source
                Owner: ThirdParty
                Provider: GitHub
                Version: 1
              Configuration:
                Owner: !Ref GitHubOwner
                Repo: !Ref GitHubRepo
                Branch: !Ref GitHubBranch
                OAuthToken: !Ref GitHubToken
                PollForSourceChanges: false
              OutputArtifacts:
                - Name: SourceOutput

        - Name: Build
          Actions:
            - Name: BuildAction
              ActionTypeId:
                Category: Build
                Owner: AWS
                Provider: CodeBuild
                Version: 1
              Configuration:
                ProjectName:
                  Fn::ImportValue: !Sub '${ProjectName}-${Environment}-CodeBuild-Project'
              InputArtifacts:
                - Name: SourceOutput
              OutputArtifacts:
                - Name: BuildOutput

        - Name: Deploy
          Actions:
            - Name: DeployAction
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Provider: ECS
                Version: 1
              Configuration:
                ClusterName:
                  Fn::ImportValue: !Sub '${ProjectName}-${Environment}-ECS-Cluster'
                ServiceName:
                  Fn::ImportValue: !Sub '${ProjectName}-${Environment}-ECS-Service'
                FileName: imagedefinitions.json
              InputArtifacts:
                - Name: BuildOutput

  # GitHub Webhook
  GitHubWebhook:
    Type: AWS::CodePipeline::Webhook
    Properties:
      Name: !Sub '${ProjectName}-${Environment}-github-webhook'
      TargetPipeline: !Ref CodePipeline
      TargetAction: SourceAction
      TargetPipelineVersion: !GetAtt CodePipeline.Version
      RegisterWithThirdParty: true
      Authentication: GITHUB_HMAC
      AuthenticationConfiguration:
        SecretToken: !Ref GitHubToken
      Filters:
        - JsonPath: "$.ref"
          MatchEquals: !Sub "refs/heads/${GitHubBranch}"

Outputs:
  CodePipelineName:
    Description: CodePipeline name
    Value: !Ref CodePipeline
    Export:
      Name: !Sub '${ProjectName}-${Environment}-Pipeline'

  CodeDeployApplication:
    Description: CodeDeploy application name
    Value: !Ref CodeDeployApplication
    Export:
      Name: !Sub '${ProjectName}-${Environment}-CodeDeploy-App'
Enter fullscreen mode Exit fullscreen mode

GitHub Repository Setup
Step 1: Create GitHub Repository


bash# 
Create a new directory for your GitHub repo
mkdir ../ecommerce-infrastructure-repo
cd ../ecommerce-infrastructure-repo

# Initialize Git repository
git init
git branch -M main

# Copy your project files
cp -r ../aws-ecommerce-infrastructure/* .

# Create README
cat > README.md << 'EOF'
# E-commerce Infrastructure on AWS

This repository contains the Infrastructure as Code (IaC) for a production-ready e-commerce platform built on AWS.

## Architecture Overview

- **Frontend**: React SPA served via CloudFront + S3
- **Backend**: Node.js API running on ECS Fargate
- **Database**: RDS PostgreSQL with encryption
- **Storage**: S3 buckets with lifecycle policies
- **CDN**: CloudFront for global content delivery
- **CI/CD**: CodePipeline + CodeDeploy for automated deployments

## Deployment Instructions

1. Deploy VPC: `aws cloudformation create-stack --stack-name ecommerce-vpc --template-body file://cloudformation/network/vpc-base.yaml`
2. Deploy Security Groups: `aws cloudformation create-stack --stack-name ecommerce-security-groups --template-body file://cloudformation/security/security-groups.yaml`
3. Deploy IAM Roles: `aws cloudformation create-stack --stack-name ecommerce-iam-roles --template-body file://cloudformation/security/iam-roles.yaml --capabilities CAPABILITY_NAMED_IAM`

## Security Features

- VPC with private subnets
- Security groups with least privilege
- IAM roles with minimal permissions
- Encrypted storage (S3, RDS)
- WAF protection
- CloudWatch monitoring

## Cost Optimization

- Fargate for serverless containers
- S3 lifecycle policies
- CloudFront edge caching
- RDS right-sizing

EOF

# Create .gitignore
cat > .gitignore << 'EOF'
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# IDE files
.vscode/
.idea/
*.swp
*.swo

# Node.js
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# AWS
.aws/

# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Build outputs
dist/
build/
EOF

# Add all files and commit
git add .
git commit -m "Initial commit: AWS E-commerce Infrastructure"

# Create GitHub repository (you'll need GitHub CLI or do this manually)
# gh repo create ecommerce-infrastructure --public --source=. --
Enter fullscreen mode Exit fullscreen mode

Step 2: Create GitHub Personal Access Token

Go to GitHub โ†’ Settings โ†’ Developer settings โ†’ Personal access tokens
Click "Generate new token (classic)"
Select scopes: repo, admin:repo_hook
Copy the token for later use

Top comments (0)