openclaw/workers/clawdbot-aws/terraform/ecs.tf
Shunsuke Hayashi 4d74fdf593 feat(aws): add ECS Fargate deployment for Clawdbot Discord bot
Add complete ECS Fargate infrastructure for Clawdbot Discord bot
deployment on AWS.

## Infrastructure (Terraform)
- **VPC**: 100.64.0.0/16 with DNS support
- **Public Subnets**: 2 subnets in different AZs
- **ECR Repository**: clawdbot/bot with lifecycle policy
- **IAM Roles**: Task Role (DynamoDB+S3) + Execution Role
- **ECS Resources**: Fargate (256 CPU, 2048 MB memory)
- **Security Group**: Outbound only
- **CloudWatch**: Log group with 7-day retention

## Security Improvements
- Discord Bot Token → AWS Secrets Manager
- IAM policy with least privilege principle
- No plaintext secrets in code

## Code Quality
- Fix all 22 lint errors
- Add *.d.ts to oxlint ignore patterns
- Fix Dockerfile .buildstamp for Linux compatibility

Closes #2049

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-26 13:38:43 +09:00

127 lines
3.3 KiB
HCL

/**
* ECS Fargate Configuration for Clawdbot Discord Bot
*/
# CloudWatch Log Group
resource "aws_cloudwatch_log_group" "clawdbot" {
name = "/ecs/${var.project_name}"
retention_in_days = 7
tags = {
Name = "${var.project_name}-logs"
}
}
# ECS Cluster
resource "aws_ecs_cluster" "clawdbot" {
name = "${var.project_name}-cluster"
tags = {
Name = "${var.project_name}-ecs"
}
}
# Security Group (outbound only, no inbound needed for Discord bot)
resource "aws_security_group" "clawdbot" {
name = "${var.project_name}-sg"
description = "Security group for Clawdbot ECS tasks (outbound only)"
vpc_id = aws_vpc.clawdbot.id
# No ingress rules needed - Discord bot initiates outbound WebSocket connections
# Egress (outbound) for internet access
egress {
description = "Allow all outbound traffic"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-sg"
}
}
# ECS Task Definition
resource "aws_ecs_task_definition" "clawdbot" {
family = "${var.project_name}-task"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = var.ecs_task_cpu
memory = var.ecs_task_memory
execution_role_arn = aws_iam_role.ecs_execution_role.arn
task_role_arn = aws_iam_role.ecs_task_role.arn
container_definitions = jsonencode([
{
name = "${var.project_name}-container"
image = "${aws_ecr_repository.clawdbot.repository_url}:latest"
cpu = var.ecs_task_cpu
memory = var.ecs_task_memory
essential = true
command = ["node", "dist/entry.js", "gateway", "--allow-unconfigured"]
environment = [
{
name = "AWS_REGION"
value = var.aws_region
},
{
name = "SESSIONS_TABLE_NAME"
value = aws_dynamodb_table.sessions.name
},
{
name = "ARTIFACTS_TABLE_NAME"
value = aws_dynamodb_table.artifacts.name
},
{
name = "ARTIFACTS_S3_BUCKET"
value = aws_s3_bucket.artifacts.id
},
{
name = "DISCORD_BOT_TOKEN"
value = data.aws_secretsmanager_secret_version.discord_token.secret_string
},
{
name = "CLAWDBOT_FORCE_BUILD"
value = "0"
}
]
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = aws_cloudwatch_log_group.clawdbot.name
"awslogs-region" = var.aws_region
"awslogs-stream-prefix" = "ecs"
"awslogs-create-group" = "true"
}
}
}
])
tags = {
Name = "${var.project_name}-taskdef"
}
}
# ECS Service
resource "aws_ecs_service" "clawdbot" {
name = "${var.project_name}-service"
cluster = aws_ecs_cluster.clawdbot.id
task_definition = aws_ecs_task_definition.clawdbot.arn
desired_count = var.ecs_service_desired_count
launch_type = "FARGATE"
network_configuration {
subnets = aws_subnet.public[*].id
security_groups = [aws_security_group.clawdbot.id]
assign_public_ip = true
}
tags = {
Name = "${var.project_name}-service"
}
}