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>
101 lines
2.7 KiB
HCL
101 lines
2.7 KiB
HCL
/**
|
|
* 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}/*"
|
|
}
|
|
]
|
|
})
|
|
}
|
|
|
|
# Task Role Policy: Secrets Manager access for Discord token
|
|
resource "aws_iam_role_policy" "ecs_task_secrets" {
|
|
name = "${var.project_name}-secrets-access"
|
|
role = aws_iam_role.ecs_task_role.id
|
|
|
|
policy = jsonencode({
|
|
Version = "2012-10-17"
|
|
Statement = [
|
|
{
|
|
Effect = "Allow"
|
|
Action = ["secretsmanager:GetSecretValue"]
|
|
Resource = "arn:aws:secretsmanager:${var.aws_region}:${var.aws_account_id}:secret:${var.discord_token_secret_name}-??????*"
|
|
}
|
|
]
|
|
})
|
|
}
|