procurement-ingest/terraform/iam.tf

658 lines
18 KiB
HCL

data "aws_iam_policy_document" "lambda_assume" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
}
}
locals {
bedrock_invoke_policy_json = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream",
]
Resource = [
"arn:aws:bedrock:${var.aws_region}:${local.account_id}:inference-profile/${local.bedrock_model_id}",
"arn:aws:bedrock:${var.aws_region}::foundation-model/anthropic.claude-haiku-4-5-20251001-v1:0",
"arn:aws:bedrock:us-east-2::foundation-model/anthropic.claude-haiku-4-5-20251001-v1:0",
"arn:aws:bedrock:us-west-2::foundation-model/anthropic.claude-haiku-4-5-20251001-v1:0",
]
}
]
})
dynamodb_cmk_rw_policy_json = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"kms:Decrypt",
"kms:DescribeKey",
"kms:Encrypt",
"kms:GenerateDataKey*",
"kms:ReEncrypt*",
]
Resource = [data.aws_ssm_parameter.dynamodb_cmk_arn.value]
Condition = {
StringEquals = {
"kms:ViaService" = "dynamodb.${var.aws_region}.amazonaws.com"
}
}
}
]
})
dynamodb_cmk_read_policy_json = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"kms:Decrypt",
"kms:DescribeKey",
]
Resource = [data.aws_ssm_parameter.dynamodb_cmk_arn.value]
Condition = {
StringEquals = {
"kms:ViaService" = "dynamodb.${var.aws_region}.amazonaws.com"
}
}
}
]
})
web_ui_auth_secret_policy_json = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = ["secretsmanager:GetSecretValue"]
Resource = [var.web_ui_auth_token_secret_arn]
}
]
})
}
# ---------------------------------------------------------------------------
# po-email-processor
# ---------------------------------------------------------------------------
resource "aws_iam_role" "po_email_processor" {
name = "po-email-processor"
path = "/tf-managed/"
assume_role_policy = data.aws_iam_policy_document.lambda_assume.json
permissions_boundary = local.boundary_arn
}
resource "aws_iam_role_policy_attachment" "po_email_processor_basic" {
role = aws_iam_role.po_email_processor.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_iam_role_policy" "po_email_processor_s3" {
name = "s3-read-emails"
role = aws_iam_role.po_email_processor.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:ListBucket",
]
Resource = [
aws_s3_bucket.po_emails.arn,
"${aws_s3_bucket.po_emails.arn}/*",
]
}
]
})
}
resource "aws_iam_role_policy" "po_email_processor_ddb" {
name = "dynamodb-rw"
role = aws_iam_role.po_email_processor.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"dynamodb:BatchGetItem",
"dynamodb:BatchWriteItem",
"dynamodb:ConditionCheckItem",
"dynamodb:DeleteItem",
"dynamodb:DescribeTable",
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:UpdateItem",
]
Resource = [
aws_dynamodb_table.purchase_orders.arn,
"${aws_dynamodb_table.purchase_orders.arn}/index/*",
]
}
]
})
}
resource "aws_iam_role_policy" "po_email_processor_kms" {
name = "dynamodb-cmk"
role = aws_iam_role.po_email_processor.id
policy = local.dynamodb_cmk_rw_policy_json
}
resource "aws_iam_role_policy" "po_email_processor_bedrock" {
name = "bedrock-invoke"
role = aws_iam_role.po_email_processor.id
policy = local.bedrock_invoke_policy_json
}
resource "aws_iam_role_policy" "po_email_processor_dlq" {
name = "sqs-dlq-send"
role = aws_iam_role.po_email_processor.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = ["sqs:SendMessage"]
Resource = [aws_sqs_queue.po_email_processor_dlq.arn]
}
]
})
}
# ---------------------------------------------------------------------------
# po-web-ui
# ---------------------------------------------------------------------------
resource "aws_iam_role" "po_web_ui" {
name = "po-web-ui"
path = "/tf-managed/"
assume_role_policy = data.aws_iam_policy_document.lambda_assume.json
permissions_boundary = local.boundary_arn
}
resource "aws_iam_role_policy_attachment" "po_web_ui_basic" {
role = aws_iam_role.po_web_ui.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_iam_role_policy" "po_web_ui_ddb" {
name = "dynamodb-read"
role = aws_iam_role.po_web_ui.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"dynamodb:BatchGetItem",
"dynamodb:ConditionCheckItem",
"dynamodb:DescribeTable",
"dynamodb:GetItem",
"dynamodb:Query",
"dynamodb:Scan",
]
Resource = [
aws_dynamodb_table.purchase_orders.arn,
"${aws_dynamodb_table.purchase_orders.arn}/index/*",
]
}
]
})
}
resource "aws_iam_role_policy" "po_web_ui_kms" {
name = "dynamodb-cmk"
role = aws_iam_role.po_web_ui.id
policy = local.dynamodb_cmk_read_policy_json
}
resource "aws_iam_role_policy" "po_web_ui_secrets" {
name = "secretsmanager-get"
role = aws_iam_role.po_web_ui.id
policy = local.web_ui_auth_secret_policy_json
}
# ---------------------------------------------------------------------------
# po-site-extractor
# ---------------------------------------------------------------------------
resource "aws_iam_role" "po_site_extractor" {
name = "po-site-extractor"
path = "/tf-managed/"
assume_role_policy = data.aws_iam_policy_document.lambda_assume.json
permissions_boundary = local.boundary_arn
}
resource "aws_iam_role_policy_attachment" "po_site_extractor_basic" {
role = aws_iam_role.po_site_extractor.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_iam_role_policy" "po_site_extractor_ddb" {
name = "dynamodb-rw-sites"
role = aws_iam_role.po_site_extractor.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"dynamodb:BatchGetItem",
"dynamodb:BatchWriteItem",
"dynamodb:ConditionCheckItem",
"dynamodb:DeleteItem",
"dynamodb:DescribeTable",
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:UpdateItem",
]
Resource = [
aws_dynamodb_table.verified_sites.arn,
"${aws_dynamodb_table.verified_sites.arn}/index/*",
aws_dynamodb_table.pending_site_review.arn,
"${aws_dynamodb_table.pending_site_review.arn}/index/*",
]
},
{
Effect = "Allow"
Action = [
"dynamodb:DescribeStream",
"dynamodb:GetRecords",
"dynamodb:GetShardIterator",
"dynamodb:ListStreams",
]
Resource = [aws_dynamodb_table.purchase_orders.stream_arn]
},
{
Effect = "Allow"
Action = ["dynamodb:DescribeTable"]
Resource = [aws_dynamodb_table.purchase_orders.arn]
}
]
})
}
resource "aws_iam_role_policy" "po_site_extractor_kms" {
name = "dynamodb-cmk"
role = aws_iam_role.po_site_extractor.id
policy = local.dynamodb_cmk_read_policy_json
}
# ---------------------------------------------------------------------------
# workorder-email-processor
# ---------------------------------------------------------------------------
resource "aws_iam_role" "wo_email_processor" {
name = "workorder-email-processor"
path = "/tf-managed/"
assume_role_policy = data.aws_iam_policy_document.lambda_assume.json
permissions_boundary = local.boundary_arn
}
resource "aws_iam_role_policy_attachment" "wo_email_processor_basic" {
role = aws_iam_role.wo_email_processor.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_iam_role_policy" "wo_email_processor_s3" {
name = "s3-read-emails"
role = aws_iam_role.wo_email_processor.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:ListBucket",
]
Resource = [
aws_s3_bucket.wo_emails.arn,
"${aws_s3_bucket.wo_emails.arn}/*",
]
}
]
})
}
resource "aws_iam_role_policy" "wo_email_processor_ddb" {
name = "dynamodb-rw"
role = aws_iam_role.wo_email_processor.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"dynamodb:BatchGetItem",
"dynamodb:BatchWriteItem",
"dynamodb:ConditionCheckItem",
"dynamodb:DeleteItem",
"dynamodb:DescribeTable",
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:UpdateItem",
]
Resource = [
aws_dynamodb_table.work_orders.arn,
"${aws_dynamodb_table.work_orders.arn}/index/*",
aws_dynamodb_table.work_order_comments.arn,
"${aws_dynamodb_table.work_order_comments.arn}/index/*",
]
}
]
})
}
resource "aws_iam_role_policy" "wo_email_processor_bedrock" {
name = "bedrock-invoke"
role = aws_iam_role.wo_email_processor.id
policy = local.bedrock_invoke_policy_json
}
resource "aws_iam_role_policy" "wo_email_processor_dlq" {
name = "sqs-dlq-send"
role = aws_iam_role.wo_email_processor.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = ["sqs:SendMessage"]
Resource = [aws_sqs_queue.wo_email_processor_dlq.arn]
}
]
})
}
# ---------------------------------------------------------------------------
# workorder-web-ui
# ---------------------------------------------------------------------------
resource "aws_iam_role" "wo_web_ui" {
name = "workorder-web-ui"
path = "/tf-managed/"
assume_role_policy = data.aws_iam_policy_document.lambda_assume.json
permissions_boundary = local.boundary_arn
}
resource "aws_iam_role_policy_attachment" "wo_web_ui_basic" {
role = aws_iam_role.wo_web_ui.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_iam_role_policy" "wo_web_ui_ddb" {
name = "dynamodb-read"
role = aws_iam_role.wo_web_ui.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"dynamodb:BatchGetItem",
"dynamodb:ConditionCheckItem",
"dynamodb:DescribeTable",
"dynamodb:GetItem",
"dynamodb:Query",
"dynamodb:Scan",
]
Resource = [
aws_dynamodb_table.work_orders.arn,
"${aws_dynamodb_table.work_orders.arn}/index/*",
aws_dynamodb_table.work_order_comments.arn,
"${aws_dynamodb_table.work_order_comments.arn}/index/*",
]
}
]
})
}
resource "aws_iam_role_policy" "wo_web_ui_secrets" {
name = "secretsmanager-get"
role = aws_iam_role.wo_web_ui.id
policy = local.web_ui_auth_secret_policy_json
}
# ---------------------------------------------------------------------------
# workorder-shoc-emitter
# ---------------------------------------------------------------------------
resource "aws_iam_role" "wo_shoc_emitter" {
name = "workorder-shoc-emitter"
path = "/tf-managed/"
assume_role_policy = data.aws_iam_policy_document.lambda_assume.json
permissions_boundary = local.boundary_arn
}
resource "aws_iam_role_policy_attachment" "wo_shoc_emitter_basic" {
role = aws_iam_role.wo_shoc_emitter.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_iam_role_policy" "wo_shoc_emitter_streams" {
name = "dynamodb-stream-read"
role = aws_iam_role.wo_shoc_emitter.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"dynamodb:DescribeStream",
"dynamodb:GetRecords",
"dynamodb:GetShardIterator",
"dynamodb:ListStreams",
]
Resource = [
aws_dynamodb_table.work_orders.stream_arn,
aws_dynamodb_table.work_order_comments.stream_arn,
]
},
{
Effect = "Allow"
Action = ["dynamodb:DescribeTable"]
Resource = [
aws_dynamodb_table.work_orders.arn,
aws_dynamodb_table.work_order_comments.arn,
]
}
]
})
}
resource "aws_iam_role_policy" "wo_shoc_emitter_secrets" {
name = "secretsmanager-get"
role = aws_iam_role.wo_shoc_emitter.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = ["secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret"]
Resource = [var.shoc_hmac_secret_arn]
}
]
})
}
resource "aws_iam_role_policy" "wo_shoc_emitter_kms" {
name = "shoc-hmac-kms"
role = aws_iam_role.wo_shoc_emitter.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = ["kms:Decrypt"]
Resource = [aws_kms_key.shoc_webhook.arn]
Condition = {
StringEquals = {
"kms:ViaService" = "secretsmanager.${var.aws_region}.amazonaws.com"
}
}
}
]
})
}
resource "aws_iam_role_policy" "wo_shoc_emitter_sqs" {
name = "sqs-send"
role = aws_iam_role.wo_shoc_emitter.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = ["sqs:SendMessage"]
Resource = [
aws_sqs_queue.shoc_emitter_rejected.arn,
aws_sqs_queue.shoc_emitter_failures.arn,
]
}
]
})
}
# ---------------------------------------------------------------------------
# workorder-shoc-hmac-rotator
# ---------------------------------------------------------------------------
resource "aws_iam_role" "wo_shoc_hmac_rotator" {
name = "workorder-shoc-hmac-rotator"
path = "/tf-managed/"
assume_role_policy = data.aws_iam_policy_document.lambda_assume.json
permissions_boundary = local.boundary_arn
}
resource "aws_iam_role_policy_attachment" "wo_shoc_hmac_rotator_basic" {
role = aws_iam_role.wo_shoc_hmac_rotator.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_iam_role_policy" "wo_shoc_hmac_rotator_secrets" {
name = "secretsmanager-rotate"
role = aws_iam_role.wo_shoc_hmac_rotator.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"secretsmanager:DescribeSecret",
"secretsmanager:GetSecretValue",
"secretsmanager:PutSecretValue",
"secretsmanager:UpdateSecretVersionStage",
]
Resource = [var.shoc_hmac_secret_arn]
}
]
})
}
resource "aws_iam_role_policy" "wo_shoc_hmac_rotator_kms" {
name = "shoc-hmac-kms"
role = aws_iam_role.wo_shoc_hmac_rotator.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"kms:Decrypt",
"kms:Encrypt",
"kms:GenerateDataKey*",
"kms:ReEncrypt*",
]
Resource = [aws_kms_key.shoc_webhook.arn]
Condition = {
StringEquals = {
"kms:ViaService" = "secretsmanager.${var.aws_region}.amazonaws.com"
}
}
}
]
})
}
# ---------------------------------------------------------------------------
# procurement-api
# ---------------------------------------------------------------------------
resource "aws_iam_role" "procurement_api" {
name = "procurement-api"
path = "/tf-managed/"
assume_role_policy = data.aws_iam_policy_document.lambda_assume.json
permissions_boundary = local.boundary_arn
}
resource "aws_iam_role_policy_attachment" "procurement_api_basic" {
role = aws_iam_role.procurement_api.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_iam_role_policy" "procurement_api_ddb" {
name = "dynamodb-read"
role = aws_iam_role.procurement_api.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"dynamodb:BatchGetItem",
"dynamodb:ConditionCheckItem",
"dynamodb:DescribeTable",
"dynamodb:GetItem",
"dynamodb:Query",
"dynamodb:Scan",
]
Resource = [
aws_dynamodb_table.purchase_orders.arn,
"${aws_dynamodb_table.purchase_orders.arn}/index/*",
aws_dynamodb_table.verified_sites.arn,
"${aws_dynamodb_table.verified_sites.arn}/index/*",
aws_dynamodb_table.work_orders.arn,
"${aws_dynamodb_table.work_orders.arn}/index/*",
aws_dynamodb_table.work_order_comments.arn,
"${aws_dynamodb_table.work_order_comments.arn}/index/*",
]
}
]
})
}
resource "aws_iam_role_policy" "procurement_api_kms" {
name = "dynamodb-cmk"
role = aws_iam_role.procurement_api.id
policy = local.dynamodb_cmk_read_policy_json
}
resource "aws_iam_role_policy" "procurement_api_secrets" {
name = "secretsmanager-get"
role = aws_iam_role.procurement_api.id
policy = local.web_ui_auth_secret_policy_json
}