meal-order-manager/terraform/iam_github_weekly_menu.tf
Adam Moussa c5c29b2bef
fix(iam): attach per-workload lambda permissions boundary (PLAT-52) (#148)
* fix(iam): attach per-workload lambda permissions boundary (PLAT-52)

* fix(iam): skip boundary delete on weekly-menu role (PLAT-52)
2026-08-20 16:02:46 -04:00

128 lines
4.3 KiB
HCL

# GitHub Actions OIDC role for .github/workflows/weekly-menu.yml.
#
# Trust is pinned three ways (aud, sub to main, job_workflow_ref to the
# weekly-menu workflow at main) so no other workflow in the repo can assume it.
# Permissions mirror the mgmt github-oidc-deploy-roles weekly-menu role, retargeted
# to prod resources and without form-api-key (SigV4 publish path).
#
# OIDC provider ARN is literal (not a data source): hcptf-meal-order-manager-plan
# lacks iam:GetOpenIDConnectProvider, and the provider is account-stable.
locals {
github_oidc_provider_arn = "arn:aws:iam::${local.account_id}:oidc-provider/token.actions.githubusercontent.com"
}
data "aws_iam_policy_document" "weekly_menu_assume" {
statement {
effect = "Allow"
actions = ["sts:AssumeRoleWithWebIdentity"]
principals {
type = "Federated"
identifiers = [local.github_oidc_provider_arn]
}
condition {
test = "StringEquals"
variable = "token.actions.githubusercontent.com:aud"
values = ["sts.amazonaws.com"]
}
condition {
test = "StringEquals"
variable = "token.actions.githubusercontent.com:sub"
values = ["repo:Sea-Haven-Industries/meal-order-manager:ref:refs/heads/main"]
}
condition {
test = "StringEquals"
variable = "token.actions.githubusercontent.com:job_workflow_ref"
values = ["Sea-Haven-Industries/meal-order-manager/.github/workflows/weekly-menu.yml@refs/heads/main"]
}
}
}
resource "aws_iam_role" "weekly_menu" {
name = "githubdeploy-meal-order-manager-weekly-menu"
path = "/tf-managed/"
description = "GitHub Actions weekly-menu scrape/publish for meal-order-manager"
assume_role_policy = data.aws_iam_policy_document.weekly_menu_assume.json
max_session_duration = 3600
# Not a Lambda execution role. Config omits permissions_boundary so a later
# apply will not PutRolePermissionsBoundary the Lambda ceiling back. Live still
# has seahaven-lambda-execution-boundary; omitting without ignore_changes would
# plan DeleteRolePermissionsBoundary, which hcptf-meal-order-manager is denied
# (DenyBoundaryTampering). Ignore the attribute so this apply does not touch
# the ceiling. An administrator deletes the live attachment, then a follow-up
# drops this lifecycle after refresh-only updates state to null.
lifecycle {
ignore_changes = [permissions_boundary]
}
}
data "aws_iam_policy_document" "weekly_menu" {
statement {
sid = "SlackBotSecret"
effect = "Allow"
actions = [
"secretsmanager:GetSecretValue",
]
resources = [var.slack_bot_secret_arn]
}
statement {
sid = "DeployAndAppParams"
effect = "Allow"
actions = [
"ssm:GetParameter",
]
resources = [
"arn:aws:ssm:${var.aws_region}:${local.account_id}:parameter${local.ssm_prefix}/deploy/api-url",
"arn:aws:ssm:${var.aws_region}:${local.account_id}:parameter${local.ssm_prefix}/deploy/form-bucket",
"arn:aws:ssm:${var.aws_region}:${local.account_id}:parameter${local.ssm_prefix}/deploy/distribution-id",
"arn:aws:ssm:${var.aws_region}:${local.account_id}:parameter${local.ssm_prefix}/deploy/form-url",
"arn:aws:ssm:${var.aws_region}:${local.account_id}:parameter${local.google_client_id_param}",
"arn:aws:ssm:${var.aws_region}:${local.account_id}:parameter${local.slack_channel_param}",
]
}
statement {
sid = "PublishApi"
effect = "Allow"
actions = [
"execute-api:Invoke",
]
resources = [
"arn:aws:execute-api:${var.aws_region}:${local.account_id}:${aws_apigatewayv2_api.order_api.id}/*/GET/api/publish/settings",
"arn:aws:execute-api:${var.aws_region}:${local.account_id}:${aws_apigatewayv2_api.order_api.id}/*/POST/api/publish/menu",
]
}
statement {
sid = "FormObjects"
effect = "Allow"
actions = [
"s3:PutObject",
]
resources = [
"${aws_s3_bucket.form.arn}/index.html",
"${aws_s3_bucket.form.arn}/archive/*.html",
]
}
statement {
sid = "InvalidateForm"
effect = "Allow"
actions = [
"cloudfront:CreateInvalidation",
]
resources = [aws_cloudfront_distribution.form.arn]
}
}
resource "aws_iam_role_policy" "weekly_menu" {
name = "weekly-menu-publish"
role = aws_iam_role.weekly_menu.id
policy = data.aws_iam_policy_document.weekly_menu.json
}