AWS S3 Storage System/Phase 1 of 61-2 hours

AWS Setup & IAM Configuration

Phase 1: Setup AWS Infrastructure

Overview

Configure AWS S3 bucket, IAM policies, and CORS for the storage service. Foundation for all subsequent phases.

Requirements

Functional:

  • β€’S3 bucket with proper folder structure
  • β€’IAM user/role with minimal permissions
  • β€’CORS configured for frontend uploads

Non-functional:

  • β€’Region: ap-southeast-1 (Singapore) for Vietnam users
  • β€’Encryption: SSE-S3 (server-side)
  • β€’Versioning: Disabled initially (cost saving)

Architecture

pickleballdot-storage/
β”œβ”€β”€ profiles/           # User avatars
β”‚   └── {userId}/avatar.{ext}
β”œβ”€β”€ courts/             # Court images
β”‚   └── {courtId}/{imageId}.{ext}
β”œβ”€β”€ clubs/              # Club media
β”‚   └── {clubId}/
β”‚       β”œβ”€β”€ avatar.{ext}
β”‚       └── cover.{ext}
β”œβ”€β”€ posts/              # User/club posts
β”‚   └── {postId}/{imageId}.{ext}
β”œβ”€β”€ tournaments/        # Tournament banners
β”‚   └── {tournamentId}/banner.{ext}
β”œβ”€β”€ messages/           # Message attachments
β”‚   └── {conversationId}/{messageId}.{ext}
└── temp/               # Presigned upload staging
    └── {uploadId}.{ext}

Implementation Steps

1. Create S3 Bucket

aws s3api create-bucket \
  --bucket pickleballdot-storage \
  --region ap-southeast-1 \
  --create-bucket-configuration LocationConstraint=ap-southeast-1

2. Enable Server-Side Encryption

aws s3api put-bucket-encryption \
  --bucket pickleballdot-storage \
  --server-side-encryption-configuration '{
    "Rules": [{"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "AES256"}}]
  }'

3. Configure CORS

Create cors.json:

{
  "CORSRules": [
    {
      "AllowedOrigins": ["http://localhost:3000", "https://admin.pickleballdot.com"],
      "AllowedMethods": ["GET", "PUT", "POST", "DELETE"],
      "AllowedHeaders": ["*"],
      "ExposeHeaders": ["ETag"],
      "MaxAgeSeconds": 3600
    }
  ]
}
aws s3api put-bucket-cors --bucket pickleballdot-storage --cors-configuration file://cors.json

4. Create IAM Policy

Create s3-storage-policy.json:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::pickleballdot-storage",
        "arn:aws:s3:::pickleballdot-storage/*"
      ]
    }
  ]
}

5. Create IAM User for Backend

aws iam create-user --user-name pickleballdot-storage-user
aws iam create-policy --policy-name PickleballDotStoragePolicy --policy-document file://s3-storage-policy.json
aws iam attach-user-policy --user-name pickleballdot-storage-user --policy-arn arn:aws:iam::ACCOUNT_ID:policy/PickleballDotStoragePolicy
aws iam create-access-key --user-name pickleballdot-storage-user

6. Environment Variables

Add to .env:

# AWS S3
AWS_REGION=ap-southeast-1
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
AWS_S3_BUCKET=pickleballdot-storage
# CDN-ready: change this to CloudFront URL when added
STORAGE_PUBLIC_URL=https://pickleballdot-storage.s3.ap-southeast-1.amazonaws.com

7. Bucket Policy for Public Read (MVP)

Since no CDN yet, allow public read for uploaded images:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::pickleballdot-storage/*",
      "Condition": {
        "StringNotLike": {
          "s3:prefix": "temp/*"
        }
      }
    }
  ]
}
aws s3api put-bucket-policy --bucket pickleballdot-storage --policy file://bucket-policy.json

Note: When CDN added, revert to private bucket + CloudFront OAI.

8. Public Access Settings (MVP)

For MVP with public bucket policy, allow bucket policy (but block ACLs):

aws s3api put-public-access-block \
  --bucket pickleballdot-storage \
  --public-access-block-configuration '{
    "BlockPublicAcls": true,
    "IgnorePublicAcls": true,
    "BlockPublicPolicy": false,
    "RestrictPublicBuckets": false
  }'

Note: When CDN added, set all to true and use CloudFront for access.

Related Code Files

  • β€’Create: infrastructure/s3/cors.json
  • β€’Create: infrastructure/s3/s3-storage-policy.json
  • β€’Modify: .env.example

Success Criteria

  • β€’ S3 bucket created in ap-southeast-1
  • β€’ CORS configured for local + production origins
  • β€’ IAM user created with scoped permissions
  • β€’ Public access blocked
  • β€’ Environment variables documented

Risk Assessment

RiskMitigation
Credentials leakedUse IAM roles in production (ECS/Lambda), rotate keys
CORS misconfigurationTest from browser, verify headers
Cost overrunSet billing alerts, lifecycle policies later