Overview

This guide walks you through deploying CaseBender on AWS using pre-built Docker images with Amazon ECS (Elastic Container Service) and Fargate.

Prerequisites

  1. AWS Account
  2. AWS CLI installed and configured
  3. Docker installed

Step 1: Initial Setup

Install and Configure AWS CLI

# Using Homebrew
brew install awscli

# Configure AWS CLI

aws configure

# Configure Docker for ECR

aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin $(aws sts get-caller-identity --query Account --output text).dkr.ecr.us-east-1.amazonaws.com

Step 2: Set Up AWS Infrastructure

Create S3 Bucket for Storage

# Create S3 bucket
aws s3 create-bucket \
  --bucket casebender-storage \
  --region us-east-1

# Enable versioning (optional)
aws s3api put-bucket-versioning \
  --bucket casebender-storage \
  --versioning-configuration Status=Enabled

# Create IAM user for S3 access
aws iam create-user --user-name casebender-storage-user

# Create and attach policy
aws iam create-policy \
  --policy-name casebender-storage-policy \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::casebender-storage",
                "arn:aws:s3:::casebender-storage/*"
            ]
        }
    ]
}'

# Attach policy to user
aws iam attach-user-policy \
  --user-name casebender-storage-user \
  --policy-arn arn:aws:iam::YOUR_ACCOUNT_ID:policy/casebender-storage-policy

# Create access keys
aws iam create-access-key --user-name casebender-storage-user

Create a VPC

# Create VPC
aws ec2 create-vpc \
  --cidr-block 10.0.0.0/16 \
  --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=casebender-vpc}]'

# Enable DNS hostnames
aws ec2 modify-vpc-attribute \
  --vpc-id <vpc-id> \
  --enable-dns-hostnames

Create Subnets

# Create public subnets
aws ec2 create-subnet \
  --vpc-id <vpc-id> \
  --cidr-block 10.0.1.0/24 \
  --availability-zone us-east-1a \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=casebender-public-1a}]'

aws ec2 create-subnet \
  --vpc-id <vpc-id> \
  --cidr-block 10.0.2.0/24 \
  --availability-zone us-east-1b \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=casebender-public-1b}]'

Set Up RDS (PostgreSQL)

# Create DB subnet group
aws rds create-db-subnet-group \
  --db-subnet-group-name casebender-db-subnet \
  --db-subnet-group-description "Subnet group for CaseBender RDS" \
  --subnet-ids "<subnet-1-id>" "<subnet-2-id>"

# Create RDS instance
aws rds create-db-instance \
  --db-instance-identifier casebender-db \
  --db-instance-class db.t3.medium \
  --engine postgres \
  --master-username superadmin \
  --master-user-password <your-secure-password> \
  --allocated-storage 20 \
  --db-subnet-group-name casebender-db-subnet

Set Up ElastiCache (Redis)

# Create cache subnet group
aws elasticache create-cache-subnet-group \
  --cache-subnet-group-name casebender-cache-subnet \
  --cache-subnet-group-description "Subnet group for CaseBender Redis" \
  --subnet-ids "<subnet-1-id>" "<subnet-2-id>"

# Create Redis cluster
aws elasticache create-cache-cluster \
  --cache-cluster-id casebender-redis \
  --engine redis \
  --cache-node-type cache.t3.micro \
  --num-cache-nodes 1 \
  --cache-subnet-group-name casebender-cache-subnet

Step 3: Create ECR Repositories

# Create repositories for each service
aws ecr create-repository --repository-name casebender/app
aws ecr create-repository --repository-name casebender/workflow-processor
aws ecr create-repository --repository-name casebender/misp-processor

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

# Pull CaseBender images
docker pull casebender/casebender:latest
docker pull casebender/workflow-processor:latest
docker pull casebender/misp-processor:latest

# Tag images for ECR
docker tag casebender/casebender:latest ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/casebender/app:latest
docker tag casebender/workflow-processor:latest ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/casebender/workflow-processor:latest
docker tag casebender/misp-processor:latest ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/casebender/misp-processor:latest

# Push images to ECR
docker push ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/casebender/app:latest
docker push ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/casebender/workflow-processor:latest
docker push ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/casebender/misp-processor:latest

Step 4: Create ECS Cluster

# Create ECS cluster
aws ecs create-cluster --cluster-name casebender-cluster

# Create task execution role
aws iam create-role \
  --role-name ecsTaskExecutionRole \
  --assume-role-policy-document file://task-execution-assume-role.json

# Attach policy
aws iam attach-role-policy \
  --role-name ecsTaskExecutionRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

Step 5: Create Task Definitions

Create task definition JSON files for each service:

{
  "family": "casebender-app",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "1024",
  "memory": "2048",
  "executionRoleArn": "arn:aws:iam::<aws-account-id>:role/ecsTaskExecutionRole",
  "containerDefinitions": [
    {
      "name": "app",
      "image": "<aws-account-id>.dkr.ecr.us-east-1.amazonaws.com/casebender/app:latest",
      "portMappings": [
        {
          "containerPort": 3000,
          "protocol": "tcp"
        }
      ],
      "environment": [
        {
          "name": "POSTGRES_PRISMA_URL",
          "value": "postgresql://superadmin:password@casebender-db.xxxxx.region.rds.amazonaws.com:5432/casebender"
        },
        {
          "name": "REDIS_URL",
          "value": "redis://casebender-redis.xxxxx.region.cache.amazonaws.com:6379"
        },
        {
          "name": "AWS_S3_BUCKET",
          "value": "casebender-storage"
        },
        {
          "name": "AWS_S3_REGION",
          "value": "us-east-1"
        }
      ],
      "secrets": [
        {
          "name": "AUTH_SECRET",
          "valueFrom": "arn:aws:secretsmanager:region:account:secret:auth-secret"
        },
        {
          "name": "AUTH_SALT",
          "valueFrom": "arn:aws:secretsmanager:region:account:secret:auth-salt"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/casebender",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "app"
        }
      }
    }
  ]
}

Register the task definitions:

# Register task definitions
aws ecs register-task-definition --cli-input-json file://app-task-definition.json
aws ecs register-task-definition --cli-input-json file://workflow-processor-task-definition.json
aws ecs register-task-definition --cli-input-json file://misp-processor-task-definition.json

Step 6: Create Application Load Balancer

# Create ALB
aws elbv2 create-load-balancer \
  --name casebender-alb \
  --subnets <subnet-1-id> <subnet-2-id> \
  --security-groups <security-group-id>

# Create target group
aws elbv2 create-target-group \
  --name casebender-tg \
  --protocol HTTP \
  --port 3000 \
  --vpc-id <vpc-id> \
  --target-type ip

# Create listener
aws elbv2 create-listener \
  --load-balancer-arn <alb-arn> \
  --protocol HTTPS \
  --port 443 \
  --certificates CertificateArn=<certificate-arn> \
  --default-actions Type=forward,TargetGroupArn=<target-group-arn>

Step 7: Create ECS Services

# Create service for main app
aws ecs create-service \
  --cluster casebender-cluster \
  --service-name casebender-app \
  --task-definition casebender-app \
  --desired-count 2 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[<subnet-1-id>,<subnet-2-id>],securityGroups=[<security-group-id>],assignPublicIp=ENABLED}" \
  --load-balancers "targetGroupArn=<target-group-arn>,containerName=app,containerPort=3000"

# Create services for processors
aws ecs create-service \
  --cluster casebender-cluster \
  --service-name workflow-processor \
  --task-definition casebender-workflow-processor \
  --desired-count 1 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[<subnet-1-id>,<subnet-2-id>],securityGroups=[<security-group-id>],assignPublicIp=ENABLED}"

aws ecs create-service \
  --cluster casebender-cluster \
  --service-name misp-processor \
  --task-definition casebender-misp-processor \
  --desired-count 1 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[<subnet-1-id>,<subnet-2-id>],securityGroups=[<security-group-id>],assignPublicIp=ENABLED}"

Step 8: Set Up Route 53 (Optional)

If you’re using a custom domain:

# Create hosted zone (if not exists)
aws route53 create-hosted-zone \
  --name yourdomain.com \
  --caller-reference $(date +%s)

# Create A record
aws route53 change-resource-record-sets \
  --hosted-zone-id <zone-id> \
  --change-batch '{
    "Changes": [{
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "yourdomain.com",
        "Type": "A",
        "AliasTarget": {
          "HostedZoneId": "<alb-hosted-zone-id>",
          "DNSName": "<alb-dns-name>",
          "EvaluateTargetHealth": true
        }
      }
    }]
  }'

Monitoring and Maintenance

Set Up CloudWatch Alarms

# Create CPU utilization alarm
aws cloudwatch put-metric-alarm \
  --alarm-name casebender-cpu-alarm \
  --alarm-description "CPU utilization exceeded 80%" \
  --metric-name CPUUtilization \
  --namespace AWS/ECS \
  --statistic Average \
  --period 300 \
  --threshold 80 \
  --comparison-operator GreaterThanThreshold \
  --dimensions Name=ClusterName,Value=casebender-cluster \
  --evaluation-periods 2 \
  --alarm-actions <sns-topic-arn>

View Logs

# View service logs
aws logs get-log-events \
  --log-group-name /ecs/casebender \
  --log-stream-name app/<container-id>

Update Services

# Update service with new task definition
aws ecs update-service \
  --cluster casebender-cluster \
  --service casebender-app \
  --task-definition casebender-app:NEW_REVISION

Cost Optimization

  1. Use Fargate Spot for non-critical workloads
  2. Implement auto-scaling based on metrics
  3. Choose appropriate instance sizes
  4. Use Reserved Instances for predictable workloads

Security Best Practices

  1. Use AWS Secrets Manager for sensitive data
  2. Implement WAF rules
  3. Enable VPC Flow Logs
  4. Regular security group audits
  5. Enable AWS GuardDuty

Next Steps

  • Set up CI/CD pipeline with AWS CodePipeline
  • Configure backup strategies
  • Implement monitoring and alerting
  • Review security best practices