feat(infra): retarget WO writers and ESMs to kebab tables (PLAT-11) (#174)

* feat(infra): retarget WO writers and ESMs to kebab Dynamo tables

Point Lambda env, IAM, emitter streams, and DDB alarms at work-orders /
work-order-comments for the PLAT-11 freeze cutover. Legacy PascalCase
tables remain in state until the decommission soak.

* style(infra): terraform fmt wo_ddb alarm map alignment
This commit is contained in:
Adam Moussa 2026-08-07 14:07:57 -04:00 • committed by GitHub
parent 2e7cdc64e6
commit 419293b8ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 34 additions and 51 deletions

View file

@ -123,8 +123,8 @@ resource "aws_lambda_function" "procurement_api" {
environment {
variables = {
VERIFIED_SITES_TABLE = aws_dynamodb_table.verified_sites.name
WORK_ORDERS_TABLE = aws_dynamodb_table.work_orders.name
COMMENTS_TABLE = aws_dynamodb_table.work_order_comments.name
WORK_ORDERS_TABLE = aws_dynamodb_table.work_orders_kebab.name
COMMENTS_TABLE = aws_dynamodb_table.work_order_comments_kebab.name
PO_TABLE = aws_dynamodb_table.purchase_orders.name
WEB_UI_AUTH_TOKEN_SECRET_ARN = var.web_ui_auth_token_secret_arn
}

View file

@ -357,10 +357,10 @@ resource "aws_iam_role_policy" "wo_email_processor_ddb" {
"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/*",
aws_dynamodb_table.work_orders_kebab.arn,
"${aws_dynamodb_table.work_orders_kebab.arn}/index/*",
aws_dynamodb_table.work_order_comments_kebab.arn,
"${aws_dynamodb_table.work_order_comments_kebab.arn}/index/*",
]
}
]
@ -421,10 +421,10 @@ resource "aws_iam_role_policy" "wo_web_ui_ddb" {
"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/*",
aws_dynamodb_table.work_orders_kebab.arn,
"${aws_dynamodb_table.work_orders_kebab.arn}/index/*",
aws_dynamodb_table.work_order_comments_kebab.arn,
"${aws_dynamodb_table.work_order_comments_kebab.arn}/index/*",
]
}
]
@ -468,16 +468,16 @@ resource "aws_iam_role_policy" "wo_shoc_emitter_streams" {
"dynamodb:ListStreams",
]
Resource = [
aws_dynamodb_table.work_orders.stream_arn,
aws_dynamodb_table.work_order_comments.stream_arn,
aws_dynamodb_table.work_orders_kebab.stream_arn,
aws_dynamodb_table.work_order_comments_kebab.stream_arn,
]
},
{
Effect = "Allow"
Action = ["dynamodb:DescribeTable"]
Resource = [
aws_dynamodb_table.work_orders.arn,
aws_dynamodb_table.work_order_comments.arn,
aws_dynamodb_table.work_orders_kebab.arn,
aws_dynamodb_table.work_order_comments_kebab.arn,
]
}
]
@ -635,10 +635,10 @@ resource "aws_iam_role_policy" "procurement_api_ddb" {
"${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/*",
aws_dynamodb_table.work_orders_kebab.arn,
"${aws_dynamodb_table.work_orders_kebab.arn}/index/*",
aws_dynamodb_table.work_order_comments_kebab.arn,
"${aws_dynamodb_table.work_order_comments_kebab.arn}/index/*",
]
}
]

View file

@ -570,25 +570,9 @@ import {
id = "workorder-shoc-emitter-rejected-messages"
}
import {
to = aws_cloudwatch_metric_alarm.wo_ddb_throttles["WorkOrders"]
id = "WorkOrders-throttles"
}
import {
to = aws_cloudwatch_metric_alarm.wo_ddb_system_errors["WorkOrders"]
id = "WorkOrders-system-errors"
}
import {
to = aws_cloudwatch_metric_alarm.wo_ddb_throttles["WorkOrderComments"]
id = "WorkOrderComments-throttles"
}
import {
to = aws_cloudwatch_metric_alarm.wo_ddb_system_errors["WorkOrderComments"]
id = "WorkOrderComments-system-errors"
}
# PLAT-11 cutover: PascalCase WO DDB alarm imports removed when alarm map
# keys moved to work-orders / work-order-comments. Legacy alarm resources are
# destroyed by the cutover apply; kebab alarms are created fresh.
# --- CloudWatch alarms (API) ---
import {

View file

@ -1,6 +1,5 @@
# PLAT-11: PascalCase tables remain authoritative until the freeze cutover.
# Kebab tables are created empty in the prep apply; writers/ESMs stay on legacy
# until docs/plat-11/cutover-runbook.md Phase 2.
# PLAT-11: PascalCase tables retained until decommission soak.
# Writers/ESMs/alarms point at kebab tables after the freeze cutover.
resource "aws_dynamodb_table" "work_orders" {
name = "WorkOrders"
@ -87,10 +86,10 @@ resource "aws_dynamodb_table" "work_order_comments_kebab" {
}
locals {
# Alarms stay on the live writer tables (legacy until cutover).
# Alarms follow live writer tables after PLAT-11 cutover.
wo_ddb_alarm_tables = {
"WorkOrders" = aws_dynamodb_table.work_orders.name
"WorkOrderComments" = aws_dynamodb_table.work_order_comments.name
"work-orders" = aws_dynamodb_table.work_orders_kebab.name
"work-order-comments" = aws_dynamodb_table.work_order_comments_kebab.name
}
}

View file

@ -35,8 +35,8 @@ resource "aws_lambda_function" "wo_email_processor" {
environment {
variables = {
WORK_ORDERS_TABLE = aws_dynamodb_table.work_orders.name
COMMENTS_TABLE = aws_dynamodb_table.work_order_comments.name
WORK_ORDERS_TABLE = aws_dynamodb_table.work_orders_kebab.name
COMMENTS_TABLE = aws_dynamodb_table.work_order_comments_kebab.name
ALLOWED_DKIM_DOMAINS = "seahaven.com"
BEDROCK_MODEL_ID = local.bedrock_model_id
}
@ -68,8 +68,8 @@ resource "aws_lambda_function" "wo_web_ui" {
environment {
variables = {
WORK_ORDERS_TABLE = aws_dynamodb_table.work_orders.name
COMMENTS_TABLE = aws_dynamodb_table.work_order_comments.name
WORK_ORDERS_TABLE = aws_dynamodb_table.work_orders_kebab.name
COMMENTS_TABLE = aws_dynamodb_table.work_order_comments_kebab.name
WEB_UI_AUTH_TOKEN_SECRET_ARN = var.web_ui_auth_token_secret_arn
}
}

View file

@ -293,8 +293,8 @@ resource "aws_lambda_function" "wo_shoc_emitter" {
REJECTED_QUEUE_URL = aws_sqs_queue.shoc_emitter_rejected.url
SHOC_WEBHOOK_URL = var.shoc_webhook_url
# Stream ARN classification (PLAT-11); must match ESM source table names.
WORK_ORDERS_TABLE = aws_dynamodb_table.work_orders.name
COMMENTS_TABLE = aws_dynamodb_table.work_order_comments.name
WORK_ORDERS_TABLE = aws_dynamodb_table.work_orders_kebab.name
COMMENTS_TABLE = aws_dynamodb_table.work_order_comments_kebab.name
}
}
@ -310,7 +310,7 @@ resource "aws_lambda_function" "wo_shoc_emitter" {
}
resource "aws_lambda_event_source_mapping" "wo_shoc_emitter_work_orders" {
event_source_arn = aws_dynamodb_table.work_orders.stream_arn
event_source_arn = aws_dynamodb_table.work_orders_kebab.stream_arn
function_name = aws_lambda_function.wo_shoc_emitter.arn
starting_position = "LATEST"
batch_size = 10
@ -329,7 +329,7 @@ resource "aws_lambda_event_source_mapping" "wo_shoc_emitter_work_orders" {
}
resource "aws_lambda_event_source_mapping" "wo_shoc_emitter_comments" {
event_source_arn = aws_dynamodb_table.work_order_comments.stream_arn
event_source_arn = aws_dynamodb_table.work_order_comments_kebab.stream_arn
function_name = aws_lambda_function.wo_shoc_emitter.arn
starting_position = "LATEST"
batch_size = 10