meal-order-manager/terraform/lambda.tf
Adam Moussa 4682d952a0
fix(auth): accept federated portal Cognito tokens for meals admin
Google Workspace federation stores email_verified=false, which 403'd the
portal Admin probe while the public menu still loaded.
2026-09-18 11:15:14 -04:00

213 lines
7.4 KiB
HCL

# The shared layer and the six functions.
#
# Packages come from the artifacts bucket (see artifacts.tf). Function packages
# contain the handler only: boto3 comes from the runtime, and fpdf2 plus the
# `shared` package come from the layer.
resource "aws_lambda_layer_version" "shared" {
layer_name = "${local.project}-shared"
description = "fpdf2 and the shared package for meal-order-manager"
s3_bucket = aws_s3_bucket.artifacts.id
s3_key = aws_s3_object.shared_layer.key
source_code_hash = data.archive_file.shared_layer.output_base64sha256
compatible_runtimes = ["python3.12"]
compatible_architectures = ["arm64"]
}
# ---------------------------------------------------------------------------
# submit-order — HTTP API handler for the order form and the admin surface
# ---------------------------------------------------------------------------
resource "aws_lambda_function" "submit_order" {
function_name = "${local.project}-submit-order"
role = aws_iam_role.submit_order.arn
handler = "handler.lambda_handler"
runtime = "python3.12"
architectures = ["arm64"]
memory_size = 128
timeout = 10
s3_bucket = aws_s3_bucket.artifacts.id
s3_key = aws_s3_object.function["submit_order"].key
source_code_hash = data.archive_file.function["submit_order"].output_base64sha256
layers = [aws_lambda_layer_version.shared.arn]
environment {
variables = merge(local.common_env, {
SLACK_NOTIFIER_ARN = aws_lambda_function.slack_notifier.arn
GOOGLE_CLIENT_ID_PARAM = local.google_client_id_param
PORTAL_COGNITO_ISSUER_PARAM = aws_ssm_parameter.portal_cognito_issuer.name
PORTAL_COGNITO_AUDIENCE_PARAM = aws_ssm_parameter.portal_cognito_audience.name
PORTAL_COGNITO_TRUST_PARAM = aws_ssm_parameter.portal_cognito_trust.name
})
}
depends_on = [
aws_cloudwatch_log_group.function,
aws_iam_role_policy.submit_order,
aws_iam_role_policy_attachment.submit_order_basic,
]
}
# ---------------------------------------------------------------------------
# admin-authorizer — validates the Google ID token for every /api/admin route
# ---------------------------------------------------------------------------
resource "aws_lambda_function" "admin_authorizer" {
function_name = "${local.project}-admin-authorizer"
role = aws_iam_role.admin_authorizer.arn
handler = "handler.lambda_handler"
runtime = "python3.12"
architectures = ["arm64"]
memory_size = 128
timeout = 10
s3_bucket = aws_s3_bucket.artifacts.id
s3_key = aws_s3_object.function["admin_authorizer"].key
source_code_hash = data.archive_file.function["admin_authorizer"].output_base64sha256
layers = [aws_lambda_layer_version.shared.arn]
environment {
variables = merge(local.common_env, {
GOOGLE_CLIENT_ID_PARAM = local.google_client_id_param
PORTAL_COGNITO_ISSUER_PARAM = aws_ssm_parameter.portal_cognito_issuer.name
PORTAL_COGNITO_AUDIENCE_PARAM = aws_ssm_parameter.portal_cognito_audience.name
PORTAL_COGNITO_TRUST_PARAM = aws_ssm_parameter.portal_cognito_trust.name
})
}
depends_on = [
aws_cloudwatch_log_group.function,
aws_iam_role_policy.admin_authorizer,
aws_iam_role_policy_attachment.admin_authorizer_basic,
]
}
# ---------------------------------------------------------------------------
# close-form — closes the weekly order window, then fans out to aggregation
# ---------------------------------------------------------------------------
resource "aws_lambda_function" "close_form" {
function_name = "${local.project}-close-form"
role = aws_iam_role.close_form.arn
handler = "handler.lambda_handler"
runtime = "python3.12"
architectures = ["arm64"]
memory_size = 128
timeout = 30
s3_bucket = aws_s3_bucket.artifacts.id
s3_key = aws_s3_object.function["close_form"].key
source_code_hash = data.archive_file.function["close_form"].output_base64sha256
layers = [aws_lambda_layer_version.shared.arn]
environment {
variables = merge(local.common_env, {
AGGREGATE_FUNCTION_ARN = aws_lambda_function.aggregate_orders.arn
})
}
depends_on = [
aws_cloudwatch_log_group.function,
aws_iam_role_policy.close_form,
aws_iam_role_policy_attachment.close_form_basic,
]
}
# ---------------------------------------------------------------------------
# aggregate-orders — rolls the week's orders into CSV and PDF artifacts
# ---------------------------------------------------------------------------
resource "aws_lambda_function" "aggregate_orders" {
function_name = "${local.project}-aggregate-orders"
role = aws_iam_role.aggregate_orders.arn
handler = "handler.lambda_handler"
runtime = "python3.12"
architectures = ["arm64"]
memory_size = 256
timeout = 60
s3_bucket = aws_s3_bucket.artifacts.id
s3_key = aws_s3_object.function["aggregate_orders"].key
source_code_hash = data.archive_file.function["aggregate_orders"].output_base64sha256
layers = [aws_lambda_layer_version.shared.arn]
environment {
variables = merge(local.common_env, {
SLACK_NOTIFIER_ARN = aws_lambda_function.slack_notifier.arn
CHECKCOMPONENTS_QUEUE_URL = var.checkcomponents_queue_url
})
}
depends_on = [
aws_cloudwatch_log_group.function,
aws_iam_role_policy.aggregate_orders,
aws_iam_role_policy_attachment.aggregate_orders_basic,
]
}
# ---------------------------------------------------------------------------
# slack-notifier — reminders and summaries into Slack
# ---------------------------------------------------------------------------
resource "aws_lambda_function" "slack_notifier" {
function_name = "${local.project}-slack-notifier"
role = aws_iam_role.slack_notifier.arn
handler = "handler.lambda_handler"
runtime = "python3.12"
architectures = ["arm64"]
memory_size = 128
timeout = 30
s3_bucket = aws_s3_bucket.artifacts.id
s3_key = aws_s3_object.function["slack_notifier"].key
source_code_hash = data.archive_file.function["slack_notifier"].output_base64sha256
layers = [aws_lambda_layer_version.shared.arn]
environment {
variables = local.common_env
}
depends_on = [
aws_cloudwatch_log_group.function,
aws_iam_role_policy.slack_notifier,
aws_iam_role_policy_attachment.slack_notifier_basic,
]
}
# ---------------------------------------------------------------------------
# sync-roster — pulls the employee roster from Slack ahead of the menu publish
# ---------------------------------------------------------------------------
resource "aws_lambda_function" "sync_roster" {
function_name = "${local.project}-sync-roster"
role = aws_iam_role.sync_roster.arn
handler = "handler.lambda_handler"
runtime = "python3.12"
architectures = ["arm64"]
memory_size = 128
timeout = 60
s3_bucket = aws_s3_bucket.artifacts.id
s3_key = aws_s3_object.function["sync_roster"].key
source_code_hash = data.archive_file.function["sync_roster"].output_base64sha256
layers = [aws_lambda_layer_version.shared.arn]
environment {
variables = local.common_env
}
depends_on = [
aws_cloudwatch_log_group.function,
aws_iam_role_policy.sync_roster,
aws_iam_role_policy_attachment.sync_roster_basic,
]
}