meal-order-manager/terraform/iam.tf
Adam Moussa f48a82c476
Some checks are pending
Deploy API / Resolve target (push) Waiting to run
Deploy API / Deploy API to (push) Blocked by required conditions
feat(api): serve meals on ECS Fargate instead of Lambda (PLAT-215) (#199)
* feat(api): serve meals on ECS Fargate instead of Lambda

Keep the Flask app always-on with in-process jobs so CloudFront no longer fronts a cold-start API Gateway.

* fix(jobs): run delayed close and reminder deliveries

Wall-clock skip windows dropped the only weekly SQS attempt when Scheduler already fired in Eastern time. Dev schedules stay disabled.

* fix(api): return JSON objects and stop logging job payloads

Flask now jsonify-s handler dicts so API responses are not HTML, and the worker logs only event and status.

* fix(ci): restore the reusable workflow so the required check is named ci / ci

Inlining the job reported `ci` instead of the org ruleset's `ci / ci`.

* fix(secrets): drop unused os import so ruff check passes

* style: apply ruff format so ci-python-app lint passes

* fix(infra): give meals its own VPC because prod has none

* chore(security): re-key ALB SG checkov suppression after vpc.tf
2026-09-21 19:34:24 +00:00

183 lines
4.7 KiB
HCL

# Execution, task, and EventBridge Scheduler roles for the Fargate API.
data "aws_iam_policy_document" "ecs_assume" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ecs-tasks.amazonaws.com"]
}
}
}
data "aws_iam_policy_document" "scheduler_assume" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["scheduler.amazonaws.com"]
}
}
}
data "aws_iam_policy_document" "ecs_task_boundary" {
source_policy_documents = [
data.aws_iam_policy_document.dynamodb_crud.json,
data.aws_iam_policy_document.ssm_read.json,
data.aws_iam_policy_document.slack_bot_secret_read.json,
]
statement {
sid = "ReportsWrite"
effect = "Allow"
actions = ["s3:PutObject", "s3:GetObject"]
resources = ["${aws_s3_bucket.reports.arn}/*"]
}
statement {
sid = "JobsQueue"
effect = "Allow"
actions = ["sqs:SendMessage", "sqs:ReceiveMessage", "sqs:DeleteMessage", "sqs:GetQueueAttributes"]
resources = [aws_sqs_queue.jobs.arn]
}
statement {
sid = "CheckcomponentsSend"
effect = "Allow"
actions = ["sqs:SendMessage"]
resources = compact([var.checkcomponents_queue_arn])
}
statement {
sid = "EcrAuth"
effect = "Allow"
actions = ["ecr:GetAuthorizationToken"]
resources = ["*"]
}
statement {
sid = "EcrPull"
effect = "Allow"
actions = [
"ecr:BatchCheckLayerAvailability",
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer",
]
resources = [aws_ecr_repository.api.arn]
}
statement {
sid = "TaskLogs"
effect = "Allow"
actions = [
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:CreateLogGroup",
]
resources = [
aws_cloudwatch_log_group.api.arn,
"${aws_cloudwatch_log_group.api.arn}:*",
]
}
}
resource "aws_iam_policy" "ecs_task_boundary" {
name = "${local.project}-ecs-task-boundary"
path = "/tf-managed/"
description = "Permissions boundary for the meal-order-manager ECS task role"
policy = data.aws_iam_policy_document.ecs_task_boundary.json
}
resource "aws_iam_role" "ecs_execution" {
name = "${local.project}-ecs-exec"
path = "/tf-managed/"
assume_role_policy = data.aws_iam_policy_document.ecs_assume.json
permissions_boundary = aws_iam_policy.ecs_task_boundary.arn
}
resource "aws_iam_role_policy_attachment" "ecs_execution" {
role = aws_iam_role.ecs_execution.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}
data "aws_iam_policy_document" "dynamodb_crud" {
statement {
sid = "OrdersTableCrud"
effect = "Allow"
actions = [
"dynamodb:BatchGetItem",
"dynamodb:BatchWriteItem",
"dynamodb:ConditionCheckItem",
"dynamodb:DeleteItem",
"dynamodb:DescribeTable",
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:UpdateItem",
]
resources = [
aws_dynamodb_table.orders.arn,
"${aws_dynamodb_table.orders.arn}/index/*",
]
}
}
data "aws_iam_policy_document" "ssm_read" {
statement {
sid = "ReadProjectParameters"
effect = "Allow"
actions = ["ssm:GetParameter"]
resources = [local.ssm_parameter_arn_wildcard]
}
}
data "aws_iam_policy_document" "slack_bot_secret_read" {
statement {
sid = "ReadSlackBotToken"
effect = "Allow"
actions = ["secretsmanager:GetSecretValue"]
resources = [var.slack_bot_secret_arn]
}
}
resource "aws_iam_role" "ecs_task" {
name = "${local.project}-api"
path = "/tf-managed/"
assume_role_policy = data.aws_iam_policy_document.ecs_assume.json
permissions_boundary = aws_iam_policy.ecs_task_boundary.arn
}
resource "aws_iam_role_policy" "ecs_task" {
name = "api-runtime"
role = aws_iam_role.ecs_task.id
policy = data.aws_iam_policy_document.ecs_task_boundary.json
}
resource "aws_iam_role" "scheduler" {
name = "${local.project}-scheduler"
path = "/tf-managed/"
assume_role_policy = data.aws_iam_policy_document.scheduler_assume.json
permissions_boundary = aws_iam_policy.ecs_task_boundary.arn
}
data "aws_iam_policy_document" "scheduler" {
statement {
sid = "SendJobs"
effect = "Allow"
actions = ["sqs:SendMessage"]
resources = [aws_sqs_queue.jobs.arn]
}
}
resource "aws_iam_role_policy" "scheduler" {
name = "enqueue-jobs"
role = aws_iam_role.scheduler.id
policy = data.aws_iam_policy_document.scheduler.json
}