feat(aws): add ECS Fargate deployment for Clawdbot Discord bot
- Create VPC with public subnets for outbound internet access - Create ECR repository for container images - Create IAM roles (Task Role with DynamoDB/S3 access, Execution Role) - Create ECS Task Definition (Fargate, 2048 MB memory, 256 CPU) - Create ECS Service (1 desired count) - Create CloudWatch Log Group - Add Security Group (outbound only for Discord bot) - Update Dockerfile with .buildstamp to prevent runtime rebuild - Add .env.example with Discord webhook and channel configurations - Update .gitignore to exclude Terraform state files Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
3c63ee1d6d
commit
f56c9d712a
13
.env.example
13
.env.example
@ -3,3 +3,16 @@ TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
TWILIO_AUTH_TOKEN=your_auth_token_here
|
||||
# Must be a WhatsApp-enabled Twilio number, prefixed with whatsapp:
|
||||
TWILIO_WHATSAPP_FROM=whatsapp:+17343367101
|
||||
|
||||
# Discord Webhook (for simple notifications)
|
||||
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/xxx/yyy
|
||||
|
||||
# Discord Notification Channel (for clawdbot connection)
|
||||
# Default: #status channel (PPAL Server)
|
||||
# Use Discord channel ID (e.g., "1465087451113722019")
|
||||
DISCORD_NOTIFY_CHANNEL=1465087451113722019
|
||||
|
||||
# Discord Second Brain Channel (for memory/knowledge storage)
|
||||
# Default: #general channel (PPAL Server)
|
||||
# Use Discord channel ID (e.g., "1465087447225598207")
|
||||
DISCORD_BRAIN_CHANNEL=1465087447225598207
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@ -76,3 +76,11 @@ src/**/*.js.map
|
||||
src/**/*.d.ts
|
||||
src/**/*.d.ts.map
|
||||
|
||||
|
||||
# Terraform
|
||||
workers/clawdbot-aws/terraform/.terraform/
|
||||
workers/clawdbot-aws/terraform/*.tfstate
|
||||
workers/clawdbot-aws/terraform/*.tfstate.backup
|
||||
workers/clawdbot-aws/terraform/*.tfplan
|
||||
workers/clawdbot-aws/terraform/plan.tfplan
|
||||
workers/clawdbot-aws/terraform/tfplan
|
||||
|
||||
@ -25,6 +25,8 @@ RUN pnpm install --frozen-lockfile
|
||||
|
||||
COPY . .
|
||||
RUN pnpm build
|
||||
# Create .buildstamp to prevent runtime rebuild
|
||||
RUN mkdir -p dist && echo "$(date +%s%N)" > dist/.buildstamp
|
||||
# Force pnpm for UI build (Bun may fail on ARM/Synology architectures)
|
||||
ENV CLAWDBOT_PREFER_PNPM=1
|
||||
RUN pnpm ui:install
|
||||
|
||||
25
workers/clawdbot-aws/terraform/.terraform.lock.hcl
generated
Normal file
25
workers/clawdbot-aws/terraform/.terraform.lock.hcl
generated
Normal file
@ -0,0 +1,25 @@
|
||||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/hashicorp/aws" {
|
||||
version = "5.100.0"
|
||||
constraints = "~> 5.0"
|
||||
hashes = [
|
||||
"h1:Ijt7pOlB7Tr7maGQIqtsLFbl7pSMIj06TVdkoSBcYOw=",
|
||||
"zh:054b8dd49f0549c9a7cc27d159e45327b7b65cf404da5e5a20da154b90b8a644",
|
||||
"zh:0b97bf8d5e03d15d83cc40b0530a1f84b459354939ba6f135a0086c20ebbe6b2",
|
||||
"zh:1589a2266af699cbd5d80737a0fe02e54ec9cf2ca54e7e00ac51c7359056f274",
|
||||
"zh:6330766f1d85f01ae6ea90d1b214b8b74cc8c1badc4696b165b36ddd4cc15f7b",
|
||||
"zh:7c8c2e30d8e55291b86fcb64bdf6c25489d538688545eb48fd74ad622e5d3862",
|
||||
"zh:99b1003bd9bd32ee323544da897148f46a527f622dc3971af63ea3e251596342",
|
||||
"zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
|
||||
"zh:9f8b909d3ec50ade83c8062290378b1ec553edef6a447c56dadc01a99f4eaa93",
|
||||
"zh:aaef921ff9aabaf8b1869a86d692ebd24fbd4e12c21205034bb679b9caf883a2",
|
||||
"zh:ac882313207aba00dd5a76dbd572a0ddc818bb9cbf5c9d61b28fe30efaec951e",
|
||||
"zh:bb64e8aff37becab373a1a0cc1080990785304141af42ed6aa3dd4913b000421",
|
||||
"zh:dfe495f6621df5540d9c92ad40b8067376350b005c637ea6efac5dc15028add4",
|
||||
"zh:f0ddf0eaf052766cfe09dea8200a946519f653c384ab4336e2a4a64fdd6310e9",
|
||||
"zh:f1b7e684f4c7ae1eed272b6de7d2049bb87a0275cb04dbb7cda6636f600699c9",
|
||||
"zh:ff461571e3f233699bf690db319dfe46aec75e58726636a0d97dd9ac6e32fb70",
|
||||
]
|
||||
}
|
||||
124
workers/clawdbot-aws/terraform/dynamodb.tf
Normal file
124
workers/clawdbot-aws/terraform/dynamodb.tf
Normal file
@ -0,0 +1,124 @@
|
||||
/**
|
||||
* DynamoDB Tables for θ-cycle Session Persistence & Artifact Storage
|
||||
*/
|
||||
|
||||
resource "aws_dynamodb_table" "sessions" {
|
||||
name = var.sessions_table_name
|
||||
billing_mode = "PAY_PER_REQUEST"
|
||||
hash_key = "sessionId"
|
||||
|
||||
attribute {
|
||||
name = "sessionId"
|
||||
type = "S"
|
||||
}
|
||||
|
||||
attribute {
|
||||
name = "userId"
|
||||
type = "S"
|
||||
}
|
||||
|
||||
attribute {
|
||||
name = "guildId"
|
||||
type = "S"
|
||||
}
|
||||
|
||||
attribute {
|
||||
name = "status"
|
||||
type = "S"
|
||||
}
|
||||
|
||||
global_secondary_index {
|
||||
name = "UserIndex"
|
||||
hash_key = "userId"
|
||||
range_key = "status"
|
||||
projection_type = "ALL"
|
||||
}
|
||||
|
||||
global_secondary_index {
|
||||
name = "GuildIndex"
|
||||
hash_key = "guildId"
|
||||
range_key = "status"
|
||||
projection_type = "ALL"
|
||||
}
|
||||
|
||||
point_in_time_recovery {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
ttl {
|
||||
attribute_name = "expiresAt"
|
||||
}
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-sessions"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_dynamodb_table" "artifacts" {
|
||||
name = var.artifacts_table_name
|
||||
billing_mode = "PAY_PER_REQUEST"
|
||||
hash_key = "id"
|
||||
|
||||
attribute {
|
||||
name = "id"
|
||||
type = "S"
|
||||
}
|
||||
|
||||
attribute {
|
||||
name = "sessionId"
|
||||
type = "S"
|
||||
}
|
||||
|
||||
attribute {
|
||||
name = "userId"
|
||||
type = "S"
|
||||
}
|
||||
|
||||
attribute {
|
||||
name = "type"
|
||||
type = "S"
|
||||
}
|
||||
|
||||
attribute {
|
||||
name = "createdAt"
|
||||
type = "N"
|
||||
}
|
||||
|
||||
attribute {
|
||||
name = "expiresAt"
|
||||
type = "N"
|
||||
}
|
||||
|
||||
global_secondary_index {
|
||||
name = "SessionIndex"
|
||||
hash_key = "sessionId"
|
||||
range_key = "createdAt"
|
||||
projection_type = "ALL"
|
||||
}
|
||||
|
||||
global_secondary_index {
|
||||
name = "UserIndex"
|
||||
hash_key = "userId"
|
||||
range_key = "createdAt"
|
||||
projection_type = "ALL"
|
||||
}
|
||||
|
||||
global_secondary_index {
|
||||
name = "TypeIndex"
|
||||
hash_key = "type"
|
||||
range_key = "expiresAt"
|
||||
projection_type = "ALL"
|
||||
}
|
||||
|
||||
point_in_time_recovery {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
ttl {
|
||||
attribute_name = "expiresAt"
|
||||
}
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-artifacts"
|
||||
}
|
||||
}
|
||||
36
workers/clawdbot-aws/terraform/ecr.tf
Normal file
36
workers/clawdbot-aws/terraform/ecr.tf
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Amazon ECR Repository for Clawdbot Container Images
|
||||
*/
|
||||
|
||||
resource "aws_ecr_repository" "clawdbot" {
|
||||
name = "${var.project_name}/bot"
|
||||
image_tag_mutability = "MUTABLE"
|
||||
|
||||
image_scanning_configuration {
|
||||
scan_on_push = true
|
||||
}
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-ecr"
|
||||
}
|
||||
}
|
||||
|
||||
# ECR Lifecycle Policy (keep last 10 images)
|
||||
resource "aws_ecr_lifecycle_policy" "clawdbot" {
|
||||
repository = aws_ecr_repository.clawdbot.name
|
||||
|
||||
policy = jsonencode({
|
||||
rules = [{
|
||||
rulePriority = 1
|
||||
description = "Keep last 10 images"
|
||||
selection = {
|
||||
tagStatus = "any"
|
||||
countType = "imageCountMoreThan"
|
||||
countNumber = 10
|
||||
}
|
||||
action = {
|
||||
type = "expire"
|
||||
}
|
||||
}]
|
||||
})
|
||||
}
|
||||
126
workers/clawdbot-aws/terraform/ecs.tf
Normal file
126
workers/clawdbot-aws/terraform/ecs.tf
Normal file
@ -0,0 +1,126 @@
|
||||
/**
|
||||
* 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 = var.discord_token
|
||||
},
|
||||
{
|
||||
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"
|
||||
}
|
||||
}
|
||||
83
workers/clawdbot-aws/terraform/iam.tf
Normal file
83
workers/clawdbot-aws/terraform/iam.tf
Normal file
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* IAM Roles for ECS Fargate Task Execution
|
||||
*/
|
||||
|
||||
# Assume role policy for ECS tasks
|
||||
data "aws_iam_policy_document" "ecs_assume_role" {
|
||||
statement {
|
||||
actions = ["sts:AssumeRole"]
|
||||
|
||||
principals {
|
||||
type = "Service"
|
||||
identifiers = ["ecs-tasks.amazonaws.com"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ECS Task Execution Role (for ECR pull and CloudWatch logs)
|
||||
resource "aws_iam_role" "ecs_execution_role" {
|
||||
name = "${var.project_name}-ecs-execution-role"
|
||||
assume_role_policy = data.aws_iam_policy_document.ecs_assume_role.json
|
||||
}
|
||||
|
||||
# Attach AmazonECSTaskExecutionRolePolicy (required for Fargate)
|
||||
resource "aws_iam_role_policy_attachment" "ecs_execution_role_policy" {
|
||||
role = aws_iam_role.ecs_execution_role.name
|
||||
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
|
||||
}
|
||||
|
||||
# ECS Task Role (for application access to DynamoDB and S3)
|
||||
resource "aws_iam_role" "ecs_task_role" {
|
||||
name = "${var.project_name}-ecs-task-role"
|
||||
assume_role_policy = data.aws_iam_policy_document.ecs_assume_role.json
|
||||
}
|
||||
|
||||
# Task Role Policy: DynamoDB access
|
||||
resource "aws_iam_role_policy" "ecs_task_dynamodb" {
|
||||
name = "${var.project_name}-dynamodb-access"
|
||||
role = aws_iam_role.ecs_task_role.id
|
||||
|
||||
policy = jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [
|
||||
{
|
||||
Effect = "Allow"
|
||||
Action = [
|
||||
"dynamodb:GetItem",
|
||||
"dynamodb:PutItem",
|
||||
"dynamodb:UpdateItem",
|
||||
"dynamodb:DeleteItem",
|
||||
"dynamodb:Query",
|
||||
"dynamodb:Scan"
|
||||
]
|
||||
Resource = [
|
||||
aws_dynamodb_table.sessions.arn,
|
||||
"${aws_dynamodb_table.sessions.arn}/index/*",
|
||||
aws_dynamodb_table.artifacts.arn,
|
||||
"${aws_dynamodb_table.artifacts.arn}/index/*"
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
# Task Role Policy: S3 access
|
||||
resource "aws_iam_role_policy" "ecs_task_s3" {
|
||||
name = "${var.project_name}-s3-access"
|
||||
role = aws_iam_role.ecs_task_role.id
|
||||
|
||||
policy = jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [
|
||||
{
|
||||
Effect = "Allow"
|
||||
Action = [
|
||||
"s3:PutObject",
|
||||
"s3:GetObject",
|
||||
"s3:DeleteObject"
|
||||
]
|
||||
Resource = "${aws_s3_bucket.artifacts.arn}/*"
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
99
workers/clawdbot-aws/terraform/outputs.tf
Normal file
99
workers/clawdbot-aws/terraform/outputs.tf
Normal file
@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Deployment Outputs
|
||||
*/
|
||||
|
||||
output "sessions_table_name" {
|
||||
description = "DynamoDB table name for session storage"
|
||||
value = aws_dynamodb_table.sessions.name
|
||||
}
|
||||
|
||||
output "artifacts_table_name" {
|
||||
description = "DynamoDB table name for artifacts metadata"
|
||||
value = aws_dynamodb_table.artifacts.name
|
||||
}
|
||||
|
||||
output "artifacts_bucket_name" {
|
||||
description = "S3 bucket name for artifact storage"
|
||||
value = aws_s3_bucket.artifacts.id
|
||||
}
|
||||
|
||||
output "artifacts_bucket_arn" {
|
||||
description = "S3 bucket ARN"
|
||||
value = aws_s3_bucket.artifacts.arn
|
||||
}
|
||||
|
||||
output "sessions_table_arn" {
|
||||
description = "DynamoDB sessions table ARN"
|
||||
value = aws_dynamodb_table.sessions.arn
|
||||
}
|
||||
|
||||
output "artifacts_table_arn" {
|
||||
description = "DynamoDB artifacts table ARN"
|
||||
value = aws_dynamodb_table.artifacts.arn
|
||||
}
|
||||
|
||||
output "environment_variables" {
|
||||
description = "Required environment variables for clawdbot"
|
||||
value = {
|
||||
SESSIONS_TABLE_NAME = aws_dynamodb_table.sessions.name
|
||||
ARTIFACTS_TABLE_NAME = aws_dynamodb_table.artifacts.name
|
||||
ARTIFACTS_S3_BUCKET = aws_s3_bucket.artifacts.id
|
||||
AWS_REGION = var.aws_region
|
||||
}
|
||||
}
|
||||
|
||||
# ECS Outputs
|
||||
output "ecs_cluster_name" {
|
||||
description = "ECS cluster name"
|
||||
value = aws_ecs_cluster.clawdbot.name
|
||||
}
|
||||
|
||||
output "ecs_service_name" {
|
||||
description = "ECS service name"
|
||||
value = aws_ecs_service.clawdbot.name
|
||||
}
|
||||
|
||||
output "ecs_task_definition_family" {
|
||||
description = "ECS task definition family"
|
||||
value = aws_ecs_task_definition.clawdbot.family
|
||||
}
|
||||
|
||||
output "ecr_repository_url" {
|
||||
description = "ECR repository URL for container images"
|
||||
value = aws_ecr_repository.clawdbot.repository_url
|
||||
}
|
||||
|
||||
output "ecr_repository_name" {
|
||||
description = "ECR repository name"
|
||||
value = aws_ecr_repository.clawdbot.name
|
||||
}
|
||||
|
||||
output "cloudwatch_log_group_name" {
|
||||
description = "CloudWatch Log Group name"
|
||||
value = aws_cloudwatch_log_group.clawdbot.name
|
||||
}
|
||||
|
||||
output "vpc_id" {
|
||||
description = "VPC ID"
|
||||
value = aws_vpc.clawdbot.id
|
||||
}
|
||||
|
||||
output "public_subnet_ids" {
|
||||
description = "Public subnet IDs"
|
||||
value = aws_subnet.public[*].id
|
||||
}
|
||||
|
||||
output "security_group_id" {
|
||||
description = "Security Group ID"
|
||||
value = aws_security_group.clawdbot.id
|
||||
}
|
||||
|
||||
output "ecs_task_role_arn" {
|
||||
description = "ECS Task Role ARN"
|
||||
value = aws_iam_role.ecs_task_role.arn
|
||||
}
|
||||
|
||||
output "ecs_execution_role_arn" {
|
||||
description = "ECS Execution Role ARN"
|
||||
value = aws_iam_role.ecs_execution_role.arn
|
||||
}
|
||||
60
workers/clawdbot-aws/terraform/s3.tf
Normal file
60
workers/clawdbot-aws/terraform/s3.tf
Normal file
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* S3 Bucket for Artifact Storage
|
||||
*/
|
||||
|
||||
resource "aws_s3_bucket" "artifacts" {
|
||||
bucket = var.artifacts_bucket_name
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-artifacts"
|
||||
}
|
||||
}
|
||||
|
||||
# S3 bucket versioning (disabled for cost optimization)
|
||||
resource "aws_s3_bucket_versioning" "artifacts" {
|
||||
bucket = aws_s3_bucket.artifacts.bucket
|
||||
versioning_configuration {
|
||||
status = "Suspended"
|
||||
}
|
||||
}
|
||||
|
||||
# S3 bucket server-side encryption (SSE-S3)
|
||||
resource "aws_s3_bucket_server_side_encryption_configuration" "artifacts" {
|
||||
bucket = aws_s3_bucket.artifacts.id
|
||||
|
||||
rule {
|
||||
apply_server_side_encryption_by_default {
|
||||
sse_algorithm = "AES256"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# S3 bucket public access block (private bucket)
|
||||
resource "aws_s3_bucket_public_access_block" "artifacts" {
|
||||
bucket = aws_s3_bucket.artifacts.id
|
||||
|
||||
block_public_acls = true
|
||||
block_public_policy = true
|
||||
ignore_public_acls = true
|
||||
restrict_public_buckets = true
|
||||
}
|
||||
|
||||
# S3 bucket lifecycle configuration (auto-expire old artifacts)
|
||||
resource "aws_s3_bucket_lifecycle_configuration" "artifacts" {
|
||||
bucket = aws_s3_bucket.artifacts.id
|
||||
|
||||
rule {
|
||||
id = "expire-old-artifacts"
|
||||
status = "Enabled"
|
||||
|
||||
filter {}
|
||||
|
||||
expiration {
|
||||
days = var.artifacts_bucket_ttl_days
|
||||
}
|
||||
|
||||
noncurrent_version_expiration {
|
||||
noncurrent_days = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
66
workers/clawdbot-aws/terraform/variables.tf
Normal file
66
workers/clawdbot-aws/terraform/variables.tf
Normal file
@ -0,0 +1,66 @@
|
||||
variable "aws_region" {
|
||||
description = "AWS region for resources"
|
||||
type = string
|
||||
default = "ap-northeast-1"
|
||||
}
|
||||
|
||||
variable "environment" {
|
||||
description = "Environment name (e.g., production, staging)"
|
||||
type = string
|
||||
default = "production"
|
||||
}
|
||||
|
||||
variable "project_name" {
|
||||
description = "Project name used for resource naming"
|
||||
type = string
|
||||
default = "clawdbot"
|
||||
}
|
||||
|
||||
variable "sessions_table_name" {
|
||||
description = "DynamoDB table name for session storage"
|
||||
type = string
|
||||
default = "clawdbot-sessions"
|
||||
}
|
||||
|
||||
variable "artifacts_table_name" {
|
||||
description = "DynamoDB table name for artifacts metadata"
|
||||
type = string
|
||||
default = "clawdbot-artifacts"
|
||||
}
|
||||
|
||||
variable "artifacts_bucket_name" {
|
||||
description = "S3 bucket name for artifact storage"
|
||||
type = string
|
||||
default = "clawdbot-artifacts"
|
||||
}
|
||||
|
||||
variable "artifacts_bucket_ttl_days" {
|
||||
description = "S3 bucket lifecycle TTL in days"
|
||||
type = number
|
||||
default = 7
|
||||
}
|
||||
|
||||
# ECS Fargate Configuration
|
||||
variable "ecs_task_cpu" {
|
||||
description = "ECS task CPU units (256 = 0.25 vCPU)"
|
||||
type = number
|
||||
default = 256
|
||||
}
|
||||
|
||||
variable "ecs_task_memory" {
|
||||
description = "ECS task memory in MB"
|
||||
type = number
|
||||
default = 2048
|
||||
}
|
||||
|
||||
variable "ecs_service_desired_count" {
|
||||
description = "Desired number of ECS tasks"
|
||||
type = number
|
||||
default = 1
|
||||
}
|
||||
|
||||
variable "discord_token" {
|
||||
description = "Discord bot token (DISCORD_BOT_TOKEN) - sensitive, do not commit to version control"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
23
workers/clawdbot-aws/terraform/versions.tf
Normal file
23
workers/clawdbot-aws/terraform/versions.tf
Normal file
@ -0,0 +1,23 @@
|
||||
terraform {
|
||||
required_version = ">= 1.0"
|
||||
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
region = var.aws_region
|
||||
|
||||
default_tags {
|
||||
tags = {
|
||||
Project = var.project_name
|
||||
Environment = var.environment
|
||||
ManagedBy = "Terraform"
|
||||
Component = "clawdbot-backend"
|
||||
}
|
||||
}
|
||||
}
|
||||
63
workers/clawdbot-aws/terraform/vpc.tf
Normal file
63
workers/clawdbot-aws/terraform/vpc.tf
Normal file
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* VPC for Clawdbot ECS Fargate Deployment
|
||||
* Discord bot requires outbound internet access for WebSocket connections
|
||||
*/
|
||||
|
||||
# VPC
|
||||
resource "aws_vpc" "clawdbot" {
|
||||
cidr_block = "10.0.0.0/16"
|
||||
enable_dns_hostnames = true
|
||||
enable_dns_support = true
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-vpc"
|
||||
}
|
||||
}
|
||||
|
||||
# Internet Gateway for outbound internet access
|
||||
resource "aws_internet_gateway" "clawdbot" {
|
||||
vpc_id = aws_vpc.clawdbot.id
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-igw"
|
||||
}
|
||||
}
|
||||
|
||||
# Public Subnets (for Fargate tasks with public IP)
|
||||
resource "aws_subnet" "public" {
|
||||
count = 2
|
||||
vpc_id = aws_vpc.clawdbot.id
|
||||
cidr_block = cidrsubnet(aws_vpc.clawdbot.cidr_block, 8, count.index)
|
||||
availability_zone = data.aws_availability_zones.available.names[count.index]
|
||||
map_public_ip_on_launch = true
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-public-${count.index + 1}"
|
||||
}
|
||||
}
|
||||
|
||||
# Route Table for public subnets
|
||||
resource "aws_route_table" "public" {
|
||||
vpc_id = aws_vpc.clawdbot.id
|
||||
|
||||
route {
|
||||
cidr_block = "0.0.0.0/0"
|
||||
gateway_id = aws_internet_gateway.clawdbot.id
|
||||
}
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-public-rt"
|
||||
}
|
||||
}
|
||||
|
||||
# Route Table Association for public subnets
|
||||
resource "aws_route_table_association" "public" {
|
||||
count = 2
|
||||
subnet_id = aws_subnet.public[count.index].id
|
||||
route_table_id = aws_route_table.public.id
|
||||
}
|
||||
|
||||
# Data source for available AZs
|
||||
data "aws_availability_zones" "available" {
|
||||
state = "available"
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user