Overview

This deployment includes:

  • ECS Cluster: Managed container orchestration using Fargate
  • EFS File System: Shared, persistent storage across containers
  • Application Load Balancer: For high availability and SSL termination
  • Built-in Authentication: Uses Oxy’s native authentication system
  • Auto Scaling: Automatic scaling based on demand

Prerequisites

Before starting, ensure you have:

  • AWS CLI configured with appropriate permissions
  • Docker installed locally for building and pushing images
  • Basic familiarity with AWS Console and CLI

Step 1: Network Infrastructure Setup

1.1 Create VPC and Subnets

# Create VPC
VPC_ID=$(aws ec2 create-vpc \
  --cidr-block 10.0.0.0/16 \
  --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=oxy-ecs-simple-vpc}]' \
  --query 'Vpc.VpcId' --output text)

# Enable DNS hostnames
aws ec2 modify-vpc-attribute --vpc-id $VPC_ID --enable-dns-hostnames

# Create Internet Gateway
IGW_ID=$(aws ec2 create-internet-gateway \
  --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=oxy-ecs-simple-igw}]' \
  --query 'InternetGateway.InternetGatewayId' --output text)

# Attach Internet Gateway to VPC
aws ec2 attach-internet-gateway --internet-gateway-id $IGW_ID --vpc-id $VPC_ID

# Create public subnets in multiple AZs
SUBNET_1_ID=$(aws ec2 create-subnet \
  --vpc-id $VPC_ID \
  --cidr-block 10.0.1.0/24 \
  --availability-zone us-west-2a \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=oxy-ecs-simple-subnet-1}]' \
  --query 'Subnet.SubnetId' --output text)

SUBNET_2_ID=$(aws ec2 create-subnet \
  --vpc-id $VPC_ID \
  --cidr-block 10.0.2.0/24 \
  --availability-zone us-west-2b \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=oxy-ecs-simple-subnet-2}]' \
  --query 'Subnet.SubnetId' --output text)

# Enable auto-assign public IP
aws ec2 modify-subnet-attribute --subnet-id $SUBNET_1_ID --map-public-ip-on-launch
aws ec2 modify-subnet-attribute --subnet-id $SUBNET_2_ID --map-public-ip-on-launch

# Create route table
ROUTE_TABLE_ID=$(aws ec2 create-route-table \
  --vpc-id $VPC_ID \
  --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=oxy-ecs-simple-rt}]' \
  --query 'RouteTable.RouteTableId' --output text)

# Add route to Internet Gateway
aws ec2 create-route --route-table-id $ROUTE_TABLE_ID --destination-cidr-block 0.0.0.0/0 --gateway-id $IGW_ID

# Associate route table with subnets
aws ec2 associate-route-table --subnet-id $SUBNET_1_ID --route-table-id $ROUTE_TABLE_ID
aws ec2 associate-route-table --subnet-id $SUBNET_2_ID --route-table-id $ROUTE_TABLE_ID

1.2 Create Security Groups

# Create security group for ECS tasks
ECS_SG_ID=$(aws ec2 create-security-group \
  --group-name oxy-ecs-simple-tasks-sg \
  --description "Security group for Oxy ECS tasks with built-in auth" \
  --vpc-id $VPC_ID \
  --tag-specifications 'ResourceType=security-group,Tags=[{Key=Name,Value=oxy-ecs-simple-tasks-sg}]' \
  --query 'GroupId' --output text)

# Allow HTTP access (port 3000 for Oxy)
aws ec2 authorize-security-group-ingress \
  --group-id $ECS_SG_ID \
  --protocol tcp \
  --port 3000 \
  --cidr 0.0.0.0/0

# Allow MCP SSE access (port 8000)
aws ec2 authorize-security-group-ingress \
  --group-id $ECS_SG_ID \
  --protocol tcp \
  --port 8000 \
  --cidr 0.0.0.0/0

# Allow NFS access for EFS (port 2049)
aws ec2 authorize-security-group-ingress \
  --group-id $ECS_SG_ID \
  --protocol tcp \
  --port 2049 \
  --source-group $ECS_SG_ID

# Create security group for ALB
ALB_SG_ID=$(aws ec2 create-security-group \
  --group-name oxy-ecs-simple-alb-sg \
  --description "Security group for Oxy ECS ALB" \
  --vpc-id $VPC_ID \
  --tag-specifications 'ResourceType=security-group,Tags=[{Key=Name,Value=oxy-ecs-simple-alb-sg}]' \
  --query 'GroupId' --output text)

# Allow HTTP and HTTPS for ALB
aws ec2 authorize-security-group-ingress \
  --group-id $ALB_SG_ID \
  --protocol tcp \
  --port 80 \
  --cidr 0.0.0.0/0

aws ec2 authorize-security-group-ingress \
  --group-id $ALB_SG_ID \
  --protocol tcp \
  --port 443 \
  --cidr 0.0.0.0/0

# Create security group for EFS
EFS_SG_ID=$(aws ec2 create-security-group \
  --group-name oxy-ecs-simple-efs-sg \
  --description "Security group for Oxy ECS EFS" \
  --vpc-id $VPC_ID \
  --tag-specifications 'ResourceType=security-group,Tags=[{Key=Name,Value=oxy-ecs-simple-efs-sg}]' \
  --query 'GroupId' --output text)

# Allow NFS access from ECS tasks
aws ec2 authorize-security-group-ingress \
  --group-id $EFS_SG_ID \
  --protocol tcp \
  --port 2049 \
  --source-group $ECS_SG_ID

Step 2: Create EFS File System

2.1 Create EFS File System

# Create EFS file system
EFS_ID=$(aws efs create-file-system \
  --creation-token oxy-ecs-simple-$(date +%s) \
  --performance-mode generalPurpose \
  --throughput-mode provisioned \
  --provisioned-throughput-in-mibps 20 \
  --tags Key=Name,Value=oxy-ecs-simple-workspace \
  --query 'FileSystemId' \
  --output text)

echo "Created EFS: $EFS_ID"

# Create mount targets in each subnet
MT_1_ID=$(aws efs create-mount-target \
  --file-system-id $EFS_ID \
  --subnet-id $SUBNET_1_ID \
  --security-groups $EFS_SG_ID \
  --query 'MountTargetId' \
  --output text)

MT_2_ID=$(aws efs create-mount-target \
  --file-system-id $EFS_ID \
  --subnet-id $SUBNET_2_ID \
  --security-groups $EFS_SG_ID \
  --query 'MountTargetId' \
  --output text)

# Create EFS access point
ACCESS_POINT_ID=$(aws efs create-access-point \
  --file-system-id $EFS_ID \
  --posix-user Uid=0,Gid=0 \
  --root-directory Path=/oxy-workspace,CreationInfo='{OwnerUid=0,OwnerGid=0,Permissions=755}' \
  --tags Key=Name,Value=oxy-simple-access-point \
  --query 'AccessPointId' \
  --output text)

echo "Created EFS Access Point: $ACCESS_POINT_ID"

Step 3: Prepare Container Images

3.1 Create ECR Repository

# Create ECR repository for Oxy
aws ecr create-repository \
  --repository-name oxy-simple-app \
  --region us-west-2

# Get account ID for ECR URI
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
ECR_URI="${ACCOUNT_ID}.dkr.ecr.us-west-2.amazonaws.com/oxy-simple-app"

# Get login token
aws ecr get-login-password --region us-west-2 | \
  docker login --username AWS --password-stdin $ECR_URI

3.2 Create Application Files

Create the application configuration and Dockerfile:

# Create config.yml
cat > config.yml << 'EOF'
databases:
  - name: "shared_duckdb"
    type: "duckdb"
    dataset: "/mnt/efs/oxy_data/shared.db"

models:
  - name: "gpt-4"
    vendor: "openai"
    model_ref: "gpt-4"
    key_var: "OPENAI_API_KEY"

authentication:
  basic:
    smtp_user: "noreply@yourdomain.com"
    smtp_password_var: "SMTP_PASSWORD"
    smtp_server: "smtp.gmail.com"
    smtp_port: 587

defaults:
  database: "shared_duckdb"
EOF

# Create Dockerfile
cat > Dockerfile << 'EOF'
FROM ubuntu:22.04

# Install dependencies
RUN apt-get update && apt-get install -y \
    curl \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*

# Install Oxy
RUN curl --proto '=https' --tlsv1.2 -LsSf https://internal.oxy.tech | /bin/bash

# Create app directory
WORKDIR /app

# Copy configuration file
COPY config.yml /app/config.yml

# Create data directory
RUN mkdir -p /mnt/efs

# Expose ports
EXPOSE 3000 8000

# Create startup script
RUN echo '#!/bin/bash\n\
echo "Starting Oxy with built-in authentication..."\n\
/usr/local/bin/oxy serve --auth-mode built-in --config /app/config.yml --port 3000 & \n\
/usr/local/bin/oxy mcp-sse --port 8000 & \n\
wait' > /app/start.sh && chmod +x /app/start.sh

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

CMD ["/app/start.sh"]
EOF

3.3 Build and Push Container

# Build the image
docker build -t oxy-simple-app .

# Tag for ECR
docker tag oxy-simple-app:latest $ECR_URI:latest

# Push to ECR
docker push $ECR_URI:latest

echo "Container image pushed to: $ECR_URI:latest"

Step 4: Create IAM Roles

4.1 Create ECS Task Execution Role

# Create trust policy for ECS tasks
cat > ecs-task-execution-trust-policy.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ecs-tasks.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF

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

# Attach AWS managed policy
aws iam attach-role-policy \
  --role-name OxyECSSimpleExecutionRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

4.2 Create ECS Task Role

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

# Create policy for EFS access
cat > ecs-task-policy.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "elasticfilesystem:ClientMount",
        "elasticfilesystem:ClientWrite",
        "elasticfilesystem:ClientRootAccess"
      ],
      "Resource": "*"
    }
  ]
}
EOF

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

# Create and attach policy
aws iam create-policy \
  --policy-name OxyECSSimpleTaskPolicy \
  --policy-document file://ecs-task-policy.json

aws iam attach-role-policy \
  --role-name OxyECSSimpleTaskRole \
  --policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/OxyECSSimpleTaskPolicy

Step 5: Create ECS Cluster and Service

5.1 Create ECS Cluster

# Create ECS cluster
CLUSTER_ARN=$(aws ecs create-cluster \
  --cluster-name oxy-simple \
  --capacity-providers FARGATE \
  --default-capacity-provider-strategy capacityProvider=FARGATE,weight=1 \
  --tags key=Name,value=oxy-simple-cluster \
  --query 'cluster.clusterArn' \
  --output text)

echo "Created ECS Cluster: $CLUSTER_ARN"

# Create CloudWatch log group
aws logs create-log-group \
  --log-group-name "/aws/ecs/oxy-simple" \
  --retention-in-days 30

5.2 Create Task Definition

# Create task definition JSON
cat > oxy-task-definition.json << EOF
{
  "family": "oxy-simple-app",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "512",
  "memory": "1024",
  "executionRoleArn": "arn:aws:iam::${ACCOUNT_ID}:role/OxyECSSimpleExecutionRole",
  "taskRoleArn": "arn:aws:iam::${ACCOUNT_ID}:role/OxyECSSimpleTaskRole",
  "containerDefinitions": [
    {
      "name": "oxy-app",
      "image": "${ECR_URI}:latest",
      "portMappings": [
        {
          "containerPort": 3000,
          "protocol": "tcp"
        },
        {
          "containerPort": 8000,
          "protocol": "tcp"
        }
      ],
      "environment": [
        {
          "name": "OXY_STATE_DIR",
          "value": "/mnt/efs/oxy_data"
        },
        {
          "name": "AWS_REGION",
          "value": "us-west-2"
        },
        {
          "name": "OPENAI_API_KEY",
          "value": "your_openai_api_key_here"
        },
        {
          "name": "SMTP_PASSWORD",
          "value": "your_smtp_password_here"
        }
      ],
      "mountPoints": [
        {
          "sourceVolume": "efs-storage",
          "containerPath": "/mnt/efs",
          "readOnly": false
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/aws/ecs/oxy-simple",
          "awslogs-region": "us-west-2",
          "awslogs-stream-prefix": "ecs"
        }
      },
      "healthCheck": {
        "command": ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"],
        "interval": 30,
        "timeout": 5,
        "retries": 3,
        "startPeriod": 60
      }
    }
  ],
  "volumes": [
    {
      "name": "efs-storage",
      "efsVolumeConfiguration": {
        "fileSystemId": "${EFS_ID}",
        "accessPointId": "${ACCESS_POINT_ID}",
        "transitEncryption": "ENABLED"
      }
    }
  ]
}
EOF

# Register task definition
TASK_DEF_ARN=$(aws ecs register-task-definition \
  --cli-input-json file://oxy-task-definition.json \
  --query 'taskDefinition.taskDefinitionArn' \
  --output text)

echo "Created Task Definition: $TASK_DEF_ARN"

Step 6: Create Application Load Balancer

6.1 Create Application Load Balancer

# Create ALB
ALB_ARN=$(aws elbv2 create-load-balancer \
  --name oxy-simple-alb \
  --subnets $SUBNET_1_ID $SUBNET_2_ID \
  --security-groups $ALB_SG_ID \
  --scheme internet-facing \
  --type application \
  --ip-address-type ipv4 \
  --tags Key=Name,Value=oxy-simple-alb \
  --query 'LoadBalancers[0].LoadBalancerArn' \
  --output text)

echo "Created ALB: $ALB_ARN"

# Create target group
TG_ARN=$(aws elbv2 create-target-group \
  --name oxy-simple-tg \
  --protocol HTTP \
  --port 3000 \
  --vpc-id $VPC_ID \
  --target-type ip \
  --health-check-protocol HTTP \
  --health-check-path /health \
  --health-check-interval-seconds 30 \
  --health-check-timeout-seconds 5 \
  --healthy-threshold-count 2 \
  --unhealthy-threshold-count 3 \
  --query 'TargetGroups[0].TargetGroupArn' \
  --output text)

echo "Created Target Group: $TG_ARN"

# Create ALB listener
aws elbv2 create-listener \
  --load-balancer-arn $ALB_ARN \
  --protocol HTTP \
  --port 80 \
  --default-actions Type=forward,TargetGroupArn=$TG_ARN

# Get ALB DNS name
ALB_DNS=$(aws elbv2 describe-load-balancers \
  --load-balancer-arns $ALB_ARN \
  --query 'LoadBalancers[0].DNSName' \
  --output text)

echo "ALB DNS: $ALB_DNS"

Step 7: Create ECS Service

7.1 Create ECS Service

# Create ECS service
SERVICE_ARN=$(aws ecs create-service \
  --cluster oxy-simple \
  --service-name oxy-simple-service \
  --task-definition oxy-simple-app \
  --desired-count 2 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[$SUBNET_1_ID,$SUBNET_2_ID],securityGroups=[$ECS_SG_ID],assignPublicIp=ENABLED}" \
  --load-balancers targetGroupArn=$TG_ARN,containerName=oxy-app,containerPort=3000 \
  --health-check-grace-period-seconds 60 \
  --tags key=Name,value=oxy-simple-service \
  --query 'service.serviceArn' \
  --output text)

echo "Created ECS Service: $SERVICE_ARN"

# Wait for service to stabilize
echo "Waiting for service to stabilize..."
aws ecs wait services-stable --cluster oxy-simple --services oxy-simple-service
echo "Service is stable"

Step 8: Set Up Auto Scaling

8.1 Configure Application Auto Scaling

# Register scalable target
aws application-autoscaling register-scalable-target \
  --service-namespace ecs \
  --scalable-dimension ecs:service:DesiredCount \
  --resource-id service/oxy-simple/oxy-simple-service \
  --min-capacity 1 \
  --max-capacity 10

# Create scaling policy for CPU utilization
aws application-autoscaling put-scaling-policy \
  --service-namespace ecs \
  --scalable-dimension ecs:service:DesiredCount \
  --resource-id service/oxy-simple/oxy-simple-service \
  --policy-name oxy-simple-cpu-scaling \
  --policy-type TargetTrackingScaling \
  --target-tracking-scaling-policy-configuration '{
    "TargetValue": 70.0,
    "PredefinedMetricSpecification": {
      "PredefinedMetricType": "ECSServiceAverageCPUUtilization"
    },
    "ScaleOutCooldown": 300,
    "ScaleInCooldown": 300
  }'

Step 9: Access Your Application

9.1 Access the Application

echo "Access your Oxy application at: http://$ALB_DNS"
echo "The application may take a few minutes to fully start up."

9.2 First-Time Setup

  1. Open your browser and navigate to http://$ALB_DNS
  2. You’ll be prompted to create an admin account
  3. Enter your email and password to create the first user
  4. The application will send a verification email (if SMTP is configured)

9.3 Update Environment Variables

To update API keys or other environment variables:

  1. Edit the task definition JSON file
  2. Register a new task definition revision
  3. Update the service to use the new task definition
# Update task definition with new environment variables
# Edit oxy-task-definition.json with new values
aws ecs register-task-definition --cli-input-json file://oxy-task-definition.json

# Update service to use new task definition
aws ecs update-service \
  --cluster oxy-simple \
  --service oxy-simple-service \
  --task-definition oxy-simple-app

Step 10: Monitor Your ECS Deployment

10.1 Check Service Status

# Get cluster info
aws ecs describe-clusters --clusters oxy-simple

# Check service status
aws ecs describe-services \
  --cluster oxy-simple \
  --services oxy-simple-service

# List running tasks
aws ecs list-tasks \
  --cluster oxy-simple \
  --service-name oxy-simple-service

10.2 Monitor Logs

# View service logs
aws logs describe-log-streams \
  --log-group-name "/aws/ecs/oxy-simple"

# Get latest log events
aws logs get-log-events \
  --log-group-name "/aws/ecs/oxy-simple" \
  --log-stream-name "$(aws logs describe-log-streams --log-group-name "/aws/ecs/oxy-simple" --order-by LastEventTime --descending --max-items 1 --query 'logStreams[0].logStreamName' --output text)"

10.3 EFS Data Management

# Check EFS mount targets
aws efs describe-mount-targets --file-system-id $EFS_ID

# Monitor EFS performance
aws efs describe-file-systems --file-system-id $EFS_ID

Troubleshooting

Common Issues

1. Tasks failing to start

# Check task definition
aws ecs describe-task-definition --task-definition oxy-simple-app

# Check stopped tasks
aws ecs list-tasks \
  --cluster oxy-simple \
  --desired-status STOPPED

# Get task failure reasons
STOPPED_TASK=$(aws ecs list-tasks --cluster oxy-simple --desired-status STOPPED --query 'taskArns[0]' --output text)
aws ecs describe-tasks --cluster oxy-simple --tasks $STOPPED_TASK

2. Health check failures

# Check target group health
aws elbv2 describe-target-health --target-group-arn $TG_ARN

# Check container logs for startup issues
aws logs get-log-events \
  --log-group-name "/aws/ecs/oxy-simple" \
  --log-stream-name "ecs/oxy-app/$(date +%Y/%m/%d)"

3. EFS mount issues

# Verify EFS mount targets are available
aws efs describe-mount-targets --file-system-id $EFS_ID

# Check EFS security group allows NFS traffic
aws ec2 describe-security-groups --group-ids $EFS_SG_ID

Production Considerations

Security Best Practices

  1. Use Secrets Manager: Store sensitive values like API keys in AWS Secrets Manager
  2. Enable EFS encryption: Encrypt data at rest and in transit
  3. Use private subnets: Move containers to private subnets with NAT Gateway
  4. Enable VPC Flow Logs: Monitor network traffic
  5. Regular updates: Keep container images updated

Performance Optimization

  1. Right-size containers: Monitor CPU and memory usage to optimize resource allocation
  2. EFS Performance: Consider using Max I/O mode for high-throughput workloads
  3. ALB optimization: Enable connection draining and appropriate timeouts
  4. Container placement: Use placement strategies for better resource distribution

Monitoring and Alerting

# Create CloudWatch alarms
aws cloudwatch put-metric-alarm \
  --alarm-name "OxySimple-HighCPU" \
  --alarm-description "Alert when CPU exceeds 80%" \
  --metric-name CPUUtilization \
  --namespace AWS/ECS \
  --statistic Average \
  --period 300 \
  --threshold 80 \
  --comparison-operator GreaterThanThreshold \
  --dimensions Name=ServiceName,Value=oxy-simple-service Name=ClusterName,Value=oxy-simple \
  --evaluation-periods 2

Cleanup

To destroy the infrastructure:

# Stop ECS service
aws ecs update-service \
  --cluster oxy-simple \
  --service oxy-simple-service \
  --desired-count 0

# Wait for tasks to stop
aws ecs wait services-stable \
  --cluster oxy-simple \
  --services oxy-simple-service

# Delete ECS service
aws ecs delete-service \
  --cluster oxy-simple \
  --service oxy-simple-service

# Delete ECS cluster
aws ecs delete-cluster --cluster oxy-simple

# Delete ALB and target group
aws elbv2 delete-load-balancer --load-balancer-arn $ALB_ARN
aws elbv2 wait load-balancers-deleted --load-balancer-arns $ALB_ARN
aws elbv2 delete-target-group --target-group-arn $TG_ARN

# Delete EFS resources
aws efs delete-access-point --access-point-id $ACCESS_POINT_ID
aws efs delete-mount-target --mount-target-id $MT_1_ID
aws efs delete-mount-target --mount-target-id $MT_2_ID
aws efs wait mount-target-deleted --mount-target-id $MT_1_ID
aws efs wait mount-target-deleted --mount-target-id $MT_2_ID
aws efs delete-file-system --file-system-id $EFS_ID

# Delete IAM roles and policies
aws iam detach-role-policy --role-name OxyECSSimpleExecutionRole --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
aws iam detach-role-policy --role-name OxyECSSimpleTaskRole --policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/OxyECSSimpleTaskPolicy
aws iam delete-role --role-name OxyECSSimpleExecutionRole
aws iam delete-role --role-name OxyECSSimpleTaskRole
aws iam delete-policy --policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/OxyECSSimpleTaskPolicy

# Delete ECR repository
aws ecr delete-repository --repository-name oxy-simple-app --force

# Delete security groups
aws ec2 delete-security-group --group-id $ECS_SG_ID
aws ec2 delete-security-group --group-id $ALB_SG_ID
aws ec2 delete-security-group --group-id $EFS_SG_ID

# Delete VPC resources
aws ec2 delete-route --route-table-id $ROUTE_TABLE_ID --destination-cidr-block 0.0.0.0/0
aws ec2 disassociate-route-table --association-id $(aws ec2 describe-route-tables --route-table-ids $ROUTE_TABLE_ID --query 'RouteTables[0].Associations[?Main==`false`].RouteTableAssociationId' --output text)
aws ec2 delete-route-table --route-table-id $ROUTE_TABLE_ID
aws ec2 delete-subnet --subnet-id $SUBNET_1_ID
aws ec2 delete-subnet --subnet-id $SUBNET_2_ID
aws ec2 detach-internet-gateway --internet-gateway-id $IGW_ID --vpc-id $VPC_ID
aws ec2 delete-internet-gateway --internet-gateway-id $IGW_ID
aws ec2 delete-vpc --vpc-id $VPC_ID

# Delete CloudWatch log group
aws logs delete-log-group --log-group-name "/aws/ecs/oxy-simple"

# Clean up local files
rm config.yml Dockerfile oxy-task-definition.json ecs-task-*.json

Next Steps